blob: c9f6e127ec5077dd5ad38413674e467255502965 [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,
Jim Ingham23ef27c2014-04-22 01:42:22 +0000231 get_queues_arglist,
232 "queue-fetch-queues"));
Jason Molenda2fd83352014-02-05 05:44:54 +0000233
234 errors.Clear();
235 unsigned num_errors = m_get_queues_function->CompileFunction(errors);
236 if (num_errors)
237 {
238 if (log)
239 log->Printf ("Error compiling get-queues function: \"%s\".", errors.GetData());
240 return args_addr;
241 }
242
243 errors.Clear();
244 if (!m_get_queues_function->WriteFunctionWrapper(exe_ctx, errors))
245 {
246 if (log)
247 log->Printf ("Error Inserting get-queues function: \"%s\".", errors.GetData());
248 return args_addr;
249 }
250 }
251 }
252
253 errors.Clear();
254
255 // Now write down the argument values for this particular call. This looks like it might be a race condition
256 // if other threads were calling into here, but actually it isn't because we allocate a new args structure for
257 // this call by passing args_addr = LLDB_INVALID_ADDRESS...
258
259 if (!m_get_queues_function->WriteFunctionArguments (exe_ctx, args_addr, impl_code_address, get_queues_arglist, errors))
260 {
261 if (log)
262 log->Printf ("Error writing get-queues function arguments: \"%s\".", errors.GetData());
263 return args_addr;
264 }
265
266 return args_addr;
267}
268
269AppleGetQueuesHandler::GetQueuesReturnInfo
270AppleGetQueuesHandler::GetCurrentQueues (Thread &thread, addr_t page_to_free, uint64_t page_to_free_size, Error &error)
271{
272 lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0);
273 ProcessSP process_sp (thread.CalculateProcess());
274 TargetSP target_sp (thread.CalculateTarget());
275 ClangASTContext *clang_ast_context = target_sp->GetScratchClangASTContext();
276 Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME));
277
278 GetQueuesReturnInfo return_value;
279 return_value.queues_buffer_ptr = LLDB_INVALID_ADDRESS;
280 return_value.queues_buffer_size = 0;
281 return_value.count = 0;
282
283 error.Clear();
284
285 // Set up the arguments for a call to
286
287 // struct get_current_queues_return_values
288 // {
289 // uint64_t queues_buffer_ptr; /* the address of the queues buffer from libBacktraceRecording */
290 // uint64_t queues_buffer_size; /* the size of the queues buffer from libBacktraceRecording */
291 // uint64_t count; /* the number of queues included in the queues buffer */
292 // };
293 //
294 // void
295 // __lldb_backtrace_recording_get_current_queues
296 // (struct get_current_queues_return_values *return_buffer,
297 // void *page_to_free,
298 // uint64_t page_to_free_size);
299
300 // Where the return_buffer argument points to a 24 byte region of memory already allocated by lldb in
301 // the inferior process.
302
303 ClangASTType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
304 Value return_buffer_ptr_value;
305 return_buffer_ptr_value.SetValueType (Value::eValueTypeScalar);
306 return_buffer_ptr_value.SetClangType (clang_void_ptr_type);
307
308 ClangASTType clang_int_type = clang_ast_context->GetBasicType(eBasicTypeInt);
309 Value debug_value;
310 debug_value.SetValueType (Value::eValueTypeScalar);
311 debug_value.SetClangType (clang_int_type);
312
313 Value page_to_free_value;
314 page_to_free_value.SetValueType (Value::eValueTypeScalar);
315 page_to_free_value.SetClangType (clang_void_ptr_type);
316
317 ClangASTType clang_uint64_type = clang_ast_context->GetBasicType(eBasicTypeUnsignedLongLong);
318 Value page_to_free_size_value;
319 page_to_free_size_value.SetValueType (Value::eValueTypeScalar);
320 page_to_free_size_value.SetClangType (clang_uint64_type);
321
322
323 Mutex::Locker locker(m_get_queues_retbuffer_mutex);
324 if (m_get_queues_return_buffer_addr == LLDB_INVALID_ADDRESS)
325 {
326 addr_t bufaddr = process_sp->AllocateMemory (32, ePermissionsReadable | ePermissionsWritable, error);
327 if (!error.Success() || bufaddr == LLDB_INVALID_ADDRESS)
328 {
329 if (log)
330 log->Printf ("Failed to allocate memory for return buffer for get current queues func call");
331 return return_value;
332 }
333 m_get_queues_return_buffer_addr = bufaddr;
334 }
335
336 ValueList argument_values;
337
338 return_buffer_ptr_value.GetScalar() = m_get_queues_return_buffer_addr;
339 argument_values.PushValue (return_buffer_ptr_value);
340
341 debug_value.GetScalar() = 0;
342 argument_values.PushValue (debug_value);
343
344 if (page_to_free != LLDB_INVALID_ADDRESS)
345 page_to_free_value.GetScalar() = page_to_free;
346 else
347 page_to_free_value.GetScalar() = 0;
348 argument_values.PushValue (page_to_free_value);
349
350 page_to_free_size_value.GetScalar() = page_to_free_size;
351 argument_values.PushValue (page_to_free_size_value);
352
353 addr_t args_addr = SetupGetQueuesFunction (thread, argument_values);
354
355 if (m_get_queues_function == NULL)
356 {
357 error.SetErrorString ("Unable to compile function to call __introspection_dispatch_get_queues");
358 }
359
360 StreamString errors;
361 ExecutionContext exe_ctx;
362 EvaluateExpressionOptions options;
363 options.SetUnwindOnError (true);
364 options.SetIgnoreBreakpoints (true);
365 options.SetStopOthers (true);
Jason Molendaa0560152014-04-25 00:06:26 +0000366 options.SetTimeoutUsec(500000);
367 options.SetTryAllThreads (false);
Jason Molenda2fd83352014-02-05 05:44:54 +0000368 thread.CalculateExecutionContext (exe_ctx);
369
370 ExecutionResults func_call_ret;
371 Value results;
372 func_call_ret = m_get_queues_function->ExecuteFunction (exe_ctx, &args_addr, options, errors, results);
373 if (func_call_ret != eExecutionCompleted || !error.Success())
374 {
375 if (log)
376 log->Printf ("Unable to call introspection_get_dispatch_queues(), got ExecutionResults %d, error contains %s", func_call_ret, error.AsCString(""));
377 error.SetErrorString ("Unable to call introspection_get_dispatch_queues() for list of queues");
378 return return_value;
379 }
380
381 return_value.queues_buffer_ptr = m_process->ReadUnsignedIntegerFromMemory (m_get_queues_return_buffer_addr, 8, LLDB_INVALID_ADDRESS, error);
382 if (!error.Success() || return_value.queues_buffer_ptr == LLDB_INVALID_ADDRESS)
383 {
384 return_value.queues_buffer_ptr = LLDB_INVALID_ADDRESS;
385 return return_value;
386 }
387
388 return_value.queues_buffer_size = m_process->ReadUnsignedIntegerFromMemory (m_get_queues_return_buffer_addr + 8, 8, 0, error);
389
390 if (!error.Success())
391 {
392 return_value.queues_buffer_ptr = LLDB_INVALID_ADDRESS;
393 return return_value;
394 }
395
396 return_value.count = m_process->ReadUnsignedIntegerFromMemory (m_get_queues_return_buffer_addr + 16, 8, 0, error);
397 if (!error.Success())
398 {
399 return_value.queues_buffer_ptr = LLDB_INVALID_ADDRESS;
400 return return_value;
401 }
402
Jason Molenda2a6c2522014-02-15 00:20:40 +0000403 if (log)
404 log->Printf ("AppleGetQueuesHandler called __introspection_dispatch_get_queues (page_to_free == 0x%" PRIx64 ", size = %" PRId64 "), returned page is at 0x%" PRIx64 ", size %" PRId64 ", count = %" PRId64, page_to_free, page_to_free_size, return_value.queues_buffer_ptr, return_value.queues_buffer_size, return_value.count);
405
Jason Molenda2fd83352014-02-05 05:44:54 +0000406 return return_value;
407}