blob: 3cf110f0b6ca0a77aa0db862d9020f93f1136fdc [file] [log] [blame]
Jason Molenda2fd83352014-02-05 05:44:54 +00001//===-- AppleGetQueuesHandler.cpp -------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "AppleGetQueuesHandler.h"
11
12// C Includes
13// C++ Includes
14// Other libraries and framework includes
15// Project includes
16#include "AppleThreadPlanStepThroughObjCTrampoline.h"
17
18#include "clang/AST/ASTContext.h"
19#include "clang/AST/DeclCXX.h"
20
21#include "lldb/Core/ConstString.h"
22#include "lldb/Core/Log.h"
23#include "lldb/Core/Module.h"
24#include "lldb/Core/StreamString.h"
25#include "lldb/Core/Value.h"
26#include "lldb/Expression/ClangExpression.h"
27#include "lldb/Expression/ClangFunction.h"
28#include "lldb/Expression/ClangUtilityFunction.h"
29#include "lldb/Symbol/ClangASTContext.h"
30#include "lldb/Symbol/Symbol.h"
31#include "lldb/Target/ExecutionContext.h"
32#include "lldb/Target/Process.h"
33
34using namespace lldb;
35using namespace lldb_private;
36
37const char *AppleGetQueuesHandler::g_get_current_queues_function_name = "__lldb_backtrace_recording_get_current_queues";
38const char *AppleGetQueuesHandler::g_get_current_queues_function_code = " \n\
39extern \"C\" \n\
40{ \n\
41 /* \n\
42 * mach defines \n\
43 */ \n\
44 \n\
45 typedef unsigned int uint32_t; \n\
46 typedef unsigned long long uint64_t; \n\
47 typedef uint32_t mach_port_t; \n\
48 typedef mach_port_t vm_map_t; \n\
49 typedef int kern_return_t; \n\
50 typedef uint64_t mach_vm_address_t; \n\
51 typedef uint64_t mach_vm_size_t; \n\
52 \n\
53 mach_port_t mach_task_self (); \n\
54 kern_return_t mach_vm_deallocate (vm_map_t target, mach_vm_address_t address, mach_vm_size_t size); \n\
55 \n\
56 /* \n\
57 * libBacktraceRecording defines \n\
58 */ \n\
59 \n\
60 typedef uint32_t queue_list_scope_t; \n\
61 typedef void *introspection_dispatch_queue_info_t; \n\
62 \n\
63 extern uint64_t __introspection_dispatch_get_queues (queue_list_scope_t scope, \n\
64 introspection_dispatch_queue_info_t *returned_queues_buffer, \n\
65 uint64_t *returned_queues_buffer_size); \n\
66 extern int printf(const char *format, ...); \n\
67 \n\
68 /* \n\
69 * return type define \n\
70 */ \n\
71 \n\
72 struct get_current_queues_return_values \n\
73 { \n\
74 uint64_t queues_buffer_ptr; /* the address of the queues buffer from libBacktraceRecording */ \n\
75 uint64_t queues_buffer_size; /* the size of the queues buffer from libBacktraceRecording */ \n\
76 uint64_t count; /* the number of queues included in the queues buffer */ \n\
77 }; \n\
78 \n\
79 void __lldb_backtrace_recording_get_current_queues \n\
80 (struct get_current_queues_return_values *return_buffer, \n\
81 int debug, \n\
82 void *page_to_free, \n\
83 uint64_t page_to_free_size) \n\
84{ \n\
85 if (debug) \n\
86 printf (\"entering get_current_queues with args %p, %d, 0x%p, 0x%llx\\n\", return_buffer, debug, page_to_free, page_to_free_size); \n\
87 if (page_to_free != 0) \n\
88 { \n\
89 mach_vm_deallocate (mach_task_self(), (mach_vm_address_t) page_to_free, (mach_vm_size_t) page_to_free_size); \n\
90 } \n\
91 \n\
92 return_buffer->count = __introspection_dispatch_get_queues ( \n\
93 /* QUEUES_WITH_ANY_ITEMS */ 2, \n\
94 (void**)&return_buffer->queues_buffer_ptr, \n\
95 &return_buffer->queues_buffer_size); \n\
96 if (debug) \n\
97 printf(\"result was count %lld\\n\", return_buffer->count); \n\
98} \n\
99} \n\
100";
101
102AppleGetQueuesHandler::AppleGetQueuesHandler (Process *process) :
103 m_process (process),
104 m_get_queues_function (),
105 m_get_queues_impl_code (),
106 m_get_queues_function_mutex(),
107 m_get_queues_return_buffer_addr (LLDB_INVALID_ADDRESS),
108 m_get_queues_retbuffer_mutex()
109{
110}
111
112AppleGetQueuesHandler::~AppleGetQueuesHandler ()
113{
114}
115
116void
117AppleGetQueuesHandler::Detach ()
118{
119
120 if (m_process && m_process->IsAlive() && m_get_queues_return_buffer_addr != LLDB_INVALID_ADDRESS)
121 {
122 Mutex::Locker locker;
123 locker.TryLock (m_get_queues_retbuffer_mutex); // Even if we don't get the lock, deallocate the buffer
124 m_process->DeallocateMemory (m_get_queues_return_buffer_addr);
125 }
126}
127
128// Construct a ClangASTType for the structure that g_get_current_queues_function_code will return by value
129// so we can extract the fields after performing the function call.
130// i.e. we are getting this struct returned to us:
131//
132// struct get_current_queues_return_values
133// {
134// introspection_dispatch_queue_info_t *queues_buffer;
135// uint64_t queues_buffer_size;
136// uint64_t count;
137// };
138
139
140// Compile our __lldb_backtrace_recording_get_current_queues() function (from the
141// source above in g_get_current_queues_function_code) if we don't find that function in the inferior
142// already with USE_BUILTIN_FUNCTION defined. (e.g. this would be the case for testing.)
143//
144// Insert the __lldb_backtrace_recording_get_current_queues into the inferior process if needed.
145//
146// Write the get_queues_arglist into the inferior's memory space to prepare for the call.
147//
148// Returns the address of the arguments written down in the inferior process, which can be used to
149// make the function call.
150
151lldb::addr_t
152AppleGetQueuesHandler::SetupGetQueuesFunction (Thread &thread, ValueList &get_queues_arglist)
153{
154 ExecutionContext exe_ctx (thread.shared_from_this());
155 Address impl_code_address;
156 StreamString errors;
157 Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME));
158 lldb::addr_t args_addr = LLDB_INVALID_ADDRESS;
159
160 // Scope for mutex locker:
161 {
162 Mutex::Locker locker(m_get_queues_function_mutex);
163
164 // First stage is to make the ClangUtility to hold our injected function:
165
166#define USE_BUILTIN_FUNCTION 0 // Define this to 1 and we will use the get_implementation function found in the target.
167 // This is useful for debugging additions to the get_impl function 'cause you don't have
168 // to bother with string-ifying the code into g_get_current_queues_function_code.
169
170 if (USE_BUILTIN_FUNCTION)
171 {
172 ConstString our_utility_function_name("__lldb_backtrace_recording_get_current_queues");
173 SymbolContextList sc_list;
174
175 exe_ctx.GetTargetRef().GetImages().FindSymbolsWithNameAndType (our_utility_function_name, eSymbolTypeCode, sc_list);
176 if (sc_list.GetSize() == 1)
177 {
178 SymbolContext sc;
179 sc_list.GetContextAtIndex(0, sc);
180 if (sc.symbol != NULL)
181 impl_code_address = sc.symbol->GetAddress();
182
183 //lldb::addr_t addr = impl_code_address.GetOpcodeLoadAddress (exe_ctx.GetTargetPtr());
184 //printf ("Getting address for our_utility_function: 0x%" PRIx64 ".\n", addr);
185 }
186 else
187 {
188 //printf ("Could not find queues introspection function address.\n");
189 return args_addr;
190 }
191 }
192 else if (!m_get_queues_impl_code.get())
193 {
194 if (g_get_current_queues_function_code != NULL)
195 {
196 m_get_queues_impl_code.reset (new ClangUtilityFunction (g_get_current_queues_function_code,
197 g_get_current_queues_function_name));
198 if (!m_get_queues_impl_code->Install(errors, exe_ctx))
199 {
200 if (log)
201 log->Printf ("Failed to install queues introspection: %s.", errors.GetData());
202 m_get_queues_impl_code.reset();
203 return args_addr;
204 }
205 }
206 else
207 {
208 if (log)
209 log->Printf("No queues introspection code found.");
210 errors.Printf ("No queues introspection code found.");
211 return LLDB_INVALID_ADDRESS;
212 }
213
214 impl_code_address.Clear();
215 impl_code_address.SetOffset(m_get_queues_impl_code->StartAddress());
216 }
217 else
218 {
219 impl_code_address.Clear();
220 impl_code_address.SetOffset(m_get_queues_impl_code->StartAddress());
221 }
222
223 // Next make the runner function for our implementation utility function.
224 if (!m_get_queues_function.get())
225 {
226 ClangASTContext *clang_ast_context = thread.GetProcess()->GetTarget().GetScratchClangASTContext();
227 ClangASTType get_queues_return_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
228 m_get_queues_function.reset(new ClangFunction (thread,
229 get_queues_return_type,
230 impl_code_address,
231 get_queues_arglist));
232
233 errors.Clear();
234 unsigned num_errors = m_get_queues_function->CompileFunction(errors);
235 if (num_errors)
236 {
237 if (log)
238 log->Printf ("Error compiling get-queues function: \"%s\".", errors.GetData());
239 return args_addr;
240 }
241
242 errors.Clear();
243 if (!m_get_queues_function->WriteFunctionWrapper(exe_ctx, errors))
244 {
245 if (log)
246 log->Printf ("Error Inserting get-queues function: \"%s\".", errors.GetData());
247 return args_addr;
248 }
249 }
250 }
251
252 errors.Clear();
253
254 // Now write down the argument values for this particular call. This looks like it might be a race condition
255 // if other threads were calling into here, but actually it isn't because we allocate a new args structure for
256 // this call by passing args_addr = LLDB_INVALID_ADDRESS...
257
258 if (!m_get_queues_function->WriteFunctionArguments (exe_ctx, args_addr, impl_code_address, get_queues_arglist, errors))
259 {
260 if (log)
261 log->Printf ("Error writing get-queues function arguments: \"%s\".", errors.GetData());
262 return args_addr;
263 }
264
265 return args_addr;
266}
267
268AppleGetQueuesHandler::GetQueuesReturnInfo
269AppleGetQueuesHandler::GetCurrentQueues (Thread &thread, addr_t page_to_free, uint64_t page_to_free_size, Error &error)
270{
271 lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0);
272 ProcessSP process_sp (thread.CalculateProcess());
273 TargetSP target_sp (thread.CalculateTarget());
274 ClangASTContext *clang_ast_context = target_sp->GetScratchClangASTContext();
275 Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME));
276
277 GetQueuesReturnInfo return_value;
278 return_value.queues_buffer_ptr = LLDB_INVALID_ADDRESS;
279 return_value.queues_buffer_size = 0;
280 return_value.count = 0;
281
282 error.Clear();
283
284 // Set up the arguments for a call to
285
286 // struct get_current_queues_return_values
287 // {
288 // uint64_t queues_buffer_ptr; /* the address of the queues buffer from libBacktraceRecording */
289 // uint64_t queues_buffer_size; /* the size of the queues buffer from libBacktraceRecording */
290 // uint64_t count; /* the number of queues included in the queues buffer */
291 // };
292 //
293 // void
294 // __lldb_backtrace_recording_get_current_queues
295 // (struct get_current_queues_return_values *return_buffer,
296 // void *page_to_free,
297 // uint64_t page_to_free_size);
298
299 // Where the return_buffer argument points to a 24 byte region of memory already allocated by lldb in
300 // the inferior process.
301
302 ClangASTType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
303 Value return_buffer_ptr_value;
304 return_buffer_ptr_value.SetValueType (Value::eValueTypeScalar);
305 return_buffer_ptr_value.SetClangType (clang_void_ptr_type);
306
307 ClangASTType clang_int_type = clang_ast_context->GetBasicType(eBasicTypeInt);
308 Value debug_value;
309 debug_value.SetValueType (Value::eValueTypeScalar);
310 debug_value.SetClangType (clang_int_type);
311
312 Value page_to_free_value;
313 page_to_free_value.SetValueType (Value::eValueTypeScalar);
314 page_to_free_value.SetClangType (clang_void_ptr_type);
315
316 ClangASTType clang_uint64_type = clang_ast_context->GetBasicType(eBasicTypeUnsignedLongLong);
317 Value page_to_free_size_value;
318 page_to_free_size_value.SetValueType (Value::eValueTypeScalar);
319 page_to_free_size_value.SetClangType (clang_uint64_type);
320
321
322 Mutex::Locker locker(m_get_queues_retbuffer_mutex);
323 if (m_get_queues_return_buffer_addr == LLDB_INVALID_ADDRESS)
324 {
325 addr_t bufaddr = process_sp->AllocateMemory (32, ePermissionsReadable | ePermissionsWritable, error);
326 if (!error.Success() || bufaddr == LLDB_INVALID_ADDRESS)
327 {
328 if (log)
329 log->Printf ("Failed to allocate memory for return buffer for get current queues func call");
330 return return_value;
331 }
332 m_get_queues_return_buffer_addr = bufaddr;
333 }
334
335 ValueList argument_values;
336
337 return_buffer_ptr_value.GetScalar() = m_get_queues_return_buffer_addr;
338 argument_values.PushValue (return_buffer_ptr_value);
339
340 debug_value.GetScalar() = 0;
341 argument_values.PushValue (debug_value);
342
343 if (page_to_free != LLDB_INVALID_ADDRESS)
344 page_to_free_value.GetScalar() = page_to_free;
345 else
346 page_to_free_value.GetScalar() = 0;
347 argument_values.PushValue (page_to_free_value);
348
349 page_to_free_size_value.GetScalar() = page_to_free_size;
350 argument_values.PushValue (page_to_free_size_value);
351
352 addr_t args_addr = SetupGetQueuesFunction (thread, argument_values);
353
354 if (m_get_queues_function == NULL)
355 {
356 error.SetErrorString ("Unable to compile function to call __introspection_dispatch_get_queues");
357 }
358
359 StreamString errors;
360 ExecutionContext exe_ctx;
361 EvaluateExpressionOptions options;
362 options.SetUnwindOnError (true);
363 options.SetIgnoreBreakpoints (true);
364 options.SetStopOthers (true);
365 thread.CalculateExecutionContext (exe_ctx);
366
367 ExecutionResults func_call_ret;
368 Value results;
369 func_call_ret = m_get_queues_function->ExecuteFunction (exe_ctx, &args_addr, options, errors, results);
370 if (func_call_ret != eExecutionCompleted || !error.Success())
371 {
372 if (log)
373 log->Printf ("Unable to call introspection_get_dispatch_queues(), got ExecutionResults %d, error contains %s", func_call_ret, error.AsCString(""));
374 error.SetErrorString ("Unable to call introspection_get_dispatch_queues() for list of queues");
375 return return_value;
376 }
377
378 return_value.queues_buffer_ptr = m_process->ReadUnsignedIntegerFromMemory (m_get_queues_return_buffer_addr, 8, LLDB_INVALID_ADDRESS, error);
379 if (!error.Success() || return_value.queues_buffer_ptr == LLDB_INVALID_ADDRESS)
380 {
381 return_value.queues_buffer_ptr = LLDB_INVALID_ADDRESS;
382 return return_value;
383 }
384
385 return_value.queues_buffer_size = m_process->ReadUnsignedIntegerFromMemory (m_get_queues_return_buffer_addr + 8, 8, 0, error);
386
387 if (!error.Success())
388 {
389 return_value.queues_buffer_ptr = LLDB_INVALID_ADDRESS;
390 return return_value;
391 }
392
393 return_value.count = m_process->ReadUnsignedIntegerFromMemory (m_get_queues_return_buffer_addr + 16, 8, 0, error);
394 if (!error.Success())
395 {
396 return_value.queues_buffer_ptr = LLDB_INVALID_ADDRESS;
397 return return_value;
398 }
399
400 return return_value;
401}