blob: 80f61a53ea558e60ee218729dfaa209704ec5c8a [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
Jason Molendab4892cd2014-05-13 22:02:48 +0000285 if (thread.SafeToCallFunctions() == false)
286 {
287 if (log)
288 log->Printf ("Not safe to call functions on thread 0x%" PRIx64, thread.GetID());
289 error.SetErrorString ("Not safe to call functions on this thread.");
290 return return_value;
291 }
292
Jason Molenda2fd83352014-02-05 05:44:54 +0000293 // Set up the arguments for a call to
294
295 // struct get_current_queues_return_values
296 // {
297 // uint64_t queues_buffer_ptr; /* the address of the queues buffer from libBacktraceRecording */
298 // uint64_t queues_buffer_size; /* the size of the queues buffer from libBacktraceRecording */
299 // uint64_t count; /* the number of queues included in the queues buffer */
300 // };
301 //
302 // void
303 // __lldb_backtrace_recording_get_current_queues
304 // (struct get_current_queues_return_values *return_buffer,
305 // void *page_to_free,
306 // uint64_t page_to_free_size);
307
308 // Where the return_buffer argument points to a 24 byte region of memory already allocated by lldb in
309 // the inferior process.
310
311 ClangASTType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
312 Value return_buffer_ptr_value;
313 return_buffer_ptr_value.SetValueType (Value::eValueTypeScalar);
314 return_buffer_ptr_value.SetClangType (clang_void_ptr_type);
315
316 ClangASTType clang_int_type = clang_ast_context->GetBasicType(eBasicTypeInt);
317 Value debug_value;
318 debug_value.SetValueType (Value::eValueTypeScalar);
319 debug_value.SetClangType (clang_int_type);
320
321 Value page_to_free_value;
322 page_to_free_value.SetValueType (Value::eValueTypeScalar);
323 page_to_free_value.SetClangType (clang_void_ptr_type);
324
325 ClangASTType clang_uint64_type = clang_ast_context->GetBasicType(eBasicTypeUnsignedLongLong);
326 Value page_to_free_size_value;
327 page_to_free_size_value.SetValueType (Value::eValueTypeScalar);
328 page_to_free_size_value.SetClangType (clang_uint64_type);
329
330
331 Mutex::Locker locker(m_get_queues_retbuffer_mutex);
332 if (m_get_queues_return_buffer_addr == LLDB_INVALID_ADDRESS)
333 {
334 addr_t bufaddr = process_sp->AllocateMemory (32, ePermissionsReadable | ePermissionsWritable, error);
335 if (!error.Success() || bufaddr == LLDB_INVALID_ADDRESS)
336 {
337 if (log)
338 log->Printf ("Failed to allocate memory for return buffer for get current queues func call");
339 return return_value;
340 }
341 m_get_queues_return_buffer_addr = bufaddr;
342 }
343
344 ValueList argument_values;
345
346 return_buffer_ptr_value.GetScalar() = m_get_queues_return_buffer_addr;
347 argument_values.PushValue (return_buffer_ptr_value);
348
349 debug_value.GetScalar() = 0;
350 argument_values.PushValue (debug_value);
351
352 if (page_to_free != LLDB_INVALID_ADDRESS)
353 page_to_free_value.GetScalar() = page_to_free;
354 else
355 page_to_free_value.GetScalar() = 0;
356 argument_values.PushValue (page_to_free_value);
357
358 page_to_free_size_value.GetScalar() = page_to_free_size;
359 argument_values.PushValue (page_to_free_size_value);
360
361 addr_t args_addr = SetupGetQueuesFunction (thread, argument_values);
362
363 if (m_get_queues_function == NULL)
364 {
365 error.SetErrorString ("Unable to compile function to call __introspection_dispatch_get_queues");
Jason Molendafa4286e2014-06-23 22:45:54 +0000366 return return_value;
Jason Molenda2fd83352014-02-05 05:44:54 +0000367 }
368
369 StreamString errors;
370 ExecutionContext exe_ctx;
371 EvaluateExpressionOptions options;
372 options.SetUnwindOnError (true);
373 options.SetIgnoreBreakpoints (true);
374 options.SetStopOthers (true);
Jason Molendaa0560152014-04-25 00:06:26 +0000375 options.SetTimeoutUsec(500000);
376 options.SetTryAllThreads (false);
Jason Molenda2fd83352014-02-05 05:44:54 +0000377 thread.CalculateExecutionContext (exe_ctx);
378
Jim Ingham1624a2d2014-05-05 02:26:40 +0000379 ExpressionResults func_call_ret;
Jason Molenda2fd83352014-02-05 05:44:54 +0000380 Value results;
381 func_call_ret = m_get_queues_function->ExecuteFunction (exe_ctx, &args_addr, options, errors, results);
Jim Ingham8646d3c2014-05-05 02:47:44 +0000382 if (func_call_ret != eExpressionCompleted || !error.Success())
Jason Molenda2fd83352014-02-05 05:44:54 +0000383 {
384 if (log)
Jim Ingham1624a2d2014-05-05 02:26:40 +0000385 log->Printf ("Unable to call introspection_get_dispatch_queues(), got ExpressionResults %d, error contains %s", func_call_ret, error.AsCString(""));
Jason Molenda2fd83352014-02-05 05:44:54 +0000386 error.SetErrorString ("Unable to call introspection_get_dispatch_queues() for list of queues");
387 return return_value;
388 }
389
390 return_value.queues_buffer_ptr = m_process->ReadUnsignedIntegerFromMemory (m_get_queues_return_buffer_addr, 8, LLDB_INVALID_ADDRESS, error);
391 if (!error.Success() || return_value.queues_buffer_ptr == LLDB_INVALID_ADDRESS)
392 {
393 return_value.queues_buffer_ptr = LLDB_INVALID_ADDRESS;
394 return return_value;
395 }
396
397 return_value.queues_buffer_size = m_process->ReadUnsignedIntegerFromMemory (m_get_queues_return_buffer_addr + 8, 8, 0, error);
398
399 if (!error.Success())
400 {
401 return_value.queues_buffer_ptr = LLDB_INVALID_ADDRESS;
402 return return_value;
403 }
404
405 return_value.count = m_process->ReadUnsignedIntegerFromMemory (m_get_queues_return_buffer_addr + 16, 8, 0, error);
406 if (!error.Success())
407 {
408 return_value.queues_buffer_ptr = LLDB_INVALID_ADDRESS;
409 return return_value;
410 }
411
Jason Molenda2a6c2522014-02-15 00:20:40 +0000412 if (log)
413 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);
414
Jason Molenda2fd83352014-02-05 05:44:54 +0000415 return return_value;
416}