1 //===-- AppleGetPendingItemsHandler.cpp -------------------------------*- C++
2 //-*-===//
3 //
4 //                     The LLVM Compiler Infrastructure
5 //
6 // This file is distributed under the University of Illinois Open Source
7 // License. See LICENSE.TXT for details.
8 //
9 //===----------------------------------------------------------------------===//
10 
11 #include "AppleGetPendingItemsHandler.h"
12 
13 // C Includes
14 // C++ Includes
15 // Other libraries and framework includes
16 // Project includes
17 
18 #include "lldb/Core/Module.h"
19 #include "lldb/Core/Value.h"
20 #include "lldb/Expression/DiagnosticManager.h"
21 #include "lldb/Expression/FunctionCaller.h"
22 #include "lldb/Expression/UtilityFunction.h"
23 #include "lldb/Symbol/ClangASTContext.h"
24 #include "lldb/Symbol/Symbol.h"
25 #include "lldb/Target/ExecutionContext.h"
26 #include "lldb/Target/Process.h"
27 #include "lldb/Target/Target.h"
28 #include "lldb/Target/Thread.h"
29 #include "lldb/Utility/ConstString.h"
30 #include "lldb/Utility/Log.h"
31 #include "lldb/Utility/StreamString.h"
32 
33 using namespace lldb;
34 using namespace lldb_private;
35 
36 const char *AppleGetPendingItemsHandler::g_get_pending_items_function_name =
37     "__lldb_backtrace_recording_get_pending_items";
38 const char *AppleGetPendingItemsHandler::g_get_pending_items_function_code =
39     "                                  \n\
40 extern \"C\"                                                                                                    \n\
41 {                                                                                                               \n\
42     /*                                                                                                          \n\
43      * mach defines                                                                                             \n\
44      */                                                                                                         \n\
45                                                                                                                 \n\
46     typedef unsigned int uint32_t;                                                                              \n\
47     typedef unsigned long long uint64_t;                                                                        \n\
48     typedef uint32_t mach_port_t;                                                                               \n\
49     typedef mach_port_t vm_map_t;                                                                               \n\
50     typedef int kern_return_t;                                                                                  \n\
51     typedef uint64_t mach_vm_address_t;                                                                         \n\
52     typedef uint64_t mach_vm_size_t;                                                                            \n\
53                                                                                                                 \n\
54     mach_port_t mach_task_self ();                                                                              \n\
55     kern_return_t mach_vm_deallocate (vm_map_t target, mach_vm_address_t address, mach_vm_size_t size);         \n\
56                                                                                                                 \n\
57     /*                                                                                                          \n\
58      * libBacktraceRecording defines                                                                            \n\
59      */                                                                                                         \n\
60                                                                                                                 \n\
61     typedef uint32_t queue_list_scope_t;                                                                        \n\
62     typedef void *dispatch_queue_t;                                                                             \n\
63     typedef void *introspection_dispatch_queue_info_t;                                                          \n\
64     typedef void *introspection_dispatch_item_info_ref;                                                         \n\
65                                                                                                                 \n\
66     extern uint64_t __introspection_dispatch_queue_get_pending_items (dispatch_queue_t queue,                   \n\
67                                                  introspection_dispatch_item_info_ref *returned_queues_buffer,  \n\
68                                                  uint64_t *returned_queues_buffer_size);                        \n\
69     extern int printf(const char *format, ...);                                                                 \n\
70                                                                                                                 \n\
71     /*                                                                                                          \n\
72      * return type define                                                                                       \n\
73      */                                                                                                         \n\
74                                                                                                                 \n\
75     struct get_pending_items_return_values                                                                      \n\
76     {                                                                                                           \n\
77         uint64_t pending_items_buffer_ptr;    /* the address of the items buffer from libBacktraceRecording */  \n\
78         uint64_t pending_items_buffer_size;   /* the size of the items buffer from libBacktraceRecording */     \n\
79         uint64_t count;                /* the number of items included in the queues buffer */                  \n\
80     };                                                                                                          \n\
81                                                                                                                 \n\
82     void  __lldb_backtrace_recording_get_pending_items                                                          \n\
83                                                (struct get_pending_items_return_values *return_buffer,          \n\
84                                                 int debug,                                                      \n\
85                                                 uint64_t /* dispatch_queue_t */ queue,                          \n\
86                                                 void *page_to_free,                                             \n\
87                                                 uint64_t page_to_free_size)                                     \n\
88 {                                                                                                               \n\
89     if (debug)                                                                                                  \n\
90       printf (\"entering get_pending_items with args return_buffer == %p, debug == %d, queue == 0x%llx, page_to_free == %p, page_to_free_size == 0x%llx\\n\", return_buffer, debug, queue, page_to_free, page_to_free_size); \n\
91     if (page_to_free != 0)                                                                                      \n\
92     {                                                                                                           \n\
93         mach_vm_deallocate (mach_task_self(), (mach_vm_address_t) page_to_free, (mach_vm_size_t) page_to_free_size); \n\
94     }                                                                                                           \n\
95                                                                                                                 \n\
96     return_buffer->count = __introspection_dispatch_queue_get_pending_items (                                   \n\
97                                                       (void*) queue,                                            \n\
98                                                       (void**)&return_buffer->pending_items_buffer_ptr,         \n\
99                                                       &return_buffer->pending_items_buffer_size);               \n\
100     if (debug)                                                                                                  \n\
101         printf(\"result was count %lld\\n\", return_buffer->count);                                             \n\
102 }                                                                                                               \n\
103 }                                                                                                               \n\
104 ";
105 
106 AppleGetPendingItemsHandler::AppleGetPendingItemsHandler(Process *process)
107     : m_process(process), m_get_pending_items_impl_code(),
108       m_get_pending_items_function_mutex(),
109       m_get_pending_items_return_buffer_addr(LLDB_INVALID_ADDRESS),
110       m_get_pending_items_retbuffer_mutex() {}
111 
112 AppleGetPendingItemsHandler::~AppleGetPendingItemsHandler() {}
113 
114 void AppleGetPendingItemsHandler::Detach() {
115   if (m_process && m_process->IsAlive() &&
116       m_get_pending_items_return_buffer_addr != LLDB_INVALID_ADDRESS) {
117     std::unique_lock<std::mutex> lock(m_get_pending_items_retbuffer_mutex,
118                                       std::defer_lock);
119     lock.try_lock(); // Even if we don't get the lock, deallocate the buffer
120     m_process->DeallocateMemory(m_get_pending_items_return_buffer_addr);
121   }
122 }
123 
124 // Compile our __lldb_backtrace_recording_get_pending_items() function (from
125 // the source above in g_get_pending_items_function_code) if we don't find that
126 // function in the inferior already with USE_BUILTIN_FUNCTION defined.  (e.g.
127 // this would be the case for testing.)
128 //
129 // Insert the __lldb_backtrace_recording_get_pending_items into the inferior
130 // process if needed.
131 //
132 // Write the get_pending_items_arglist into the inferior's memory space to
133 // prepare for the call.
134 //
135 // Returns the address of the arguments written down in the inferior process,
136 // which can be used to make the function call.
137 
138 lldb::addr_t AppleGetPendingItemsHandler::SetupGetPendingItemsFunction(
139     Thread &thread, ValueList &get_pending_items_arglist) {
140   ThreadSP thread_sp(thread.shared_from_this());
141   ExecutionContext exe_ctx(thread_sp);
142   DiagnosticManager diagnostics;
143   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYSTEM_RUNTIME));
144 
145   lldb::addr_t args_addr = LLDB_INVALID_ADDRESS;
146   FunctionCaller *get_pending_items_caller = nullptr;
147 
148   // Scope for mutex locker:
149   {
150     std::lock_guard<std::mutex> guard(m_get_pending_items_function_mutex);
151 
152     // First stage is to make the ClangUtility to hold our injected function:
153 
154     if (!m_get_pending_items_impl_code.get()) {
155       if (g_get_pending_items_function_code != NULL) {
156         Status error;
157         m_get_pending_items_impl_code.reset(
158             exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage(
159                 g_get_pending_items_function_code, eLanguageTypeObjC,
160                 g_get_pending_items_function_name, error));
161         if (error.Fail()) {
162           if (log)
163             log->Printf("Failed to get UtilityFunction for pending-items "
164                         "introspection: %s.",
165                         error.AsCString());
166           return args_addr;
167         }
168 
169         if (!m_get_pending_items_impl_code->Install(diagnostics, exe_ctx)) {
170           if (log) {
171             log->Printf("Failed to install pending-items introspection.");
172             diagnostics.Dump(log);
173           }
174           m_get_pending_items_impl_code.reset();
175           return args_addr;
176         }
177       } else {
178         if (log)
179           log->Printf("No pending-items introspection code found.");
180         return LLDB_INVALID_ADDRESS;
181       }
182 
183       // Next make the runner function for our implementation utility function.
184       Status error;
185       ClangASTContext *clang_ast_context =
186           thread.GetProcess()->GetTarget().GetScratchClangASTContext();
187       CompilerType get_pending_items_return_type =
188           clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
189       get_pending_items_caller =
190           m_get_pending_items_impl_code->MakeFunctionCaller(
191               get_pending_items_return_type, get_pending_items_arglist,
192               thread_sp, error);
193       if (error.Fail() || get_pending_items_caller == nullptr) {
194         if (log)
195           log->Printf("Failed to install pending-items introspection function "
196                       "caller: %s.",
197                       error.AsCString());
198         m_get_pending_items_impl_code.reset();
199         return args_addr;
200       }
201     }
202   }
203 
204   diagnostics.Clear();
205 
206   if (get_pending_items_caller == nullptr) {
207     if (log)
208       log->Printf("Failed to get get_pending_items_caller.");
209     return LLDB_INVALID_ADDRESS;
210   }
211 
212   // Now write down the argument values for this particular call.  This looks
213   // like it might be a race condition if other threads were calling into here,
214   // but actually it isn't because we allocate a new args structure for this
215   // call by passing args_addr = LLDB_INVALID_ADDRESS...
216 
217   if (!get_pending_items_caller->WriteFunctionArguments(
218           exe_ctx, args_addr, get_pending_items_arglist, diagnostics)) {
219     if (log) {
220       log->Printf("Error writing pending-items function arguments.");
221       diagnostics.Dump(log);
222     }
223 
224     return args_addr;
225   }
226 
227   return args_addr;
228 }
229 
230 AppleGetPendingItemsHandler::GetPendingItemsReturnInfo
231 AppleGetPendingItemsHandler::GetPendingItems(Thread &thread, addr_t queue,
232                                              addr_t page_to_free,
233                                              uint64_t page_to_free_size,
234                                              Status &error) {
235   lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0);
236   ProcessSP process_sp(thread.CalculateProcess());
237   TargetSP target_sp(thread.CalculateTarget());
238   ClangASTContext *clang_ast_context = target_sp->GetScratchClangASTContext();
239   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYSTEM_RUNTIME));
240 
241   GetPendingItemsReturnInfo return_value;
242   return_value.items_buffer_ptr = LLDB_INVALID_ADDRESS;
243   return_value.items_buffer_size = 0;
244   return_value.count = 0;
245 
246   error.Clear();
247 
248   if (thread.SafeToCallFunctions() == false) {
249     if (log)
250       log->Printf("Not safe to call functions on thread 0x%" PRIx64,
251                   thread.GetID());
252     error.SetErrorString("Not safe to call functions on this thread.");
253     return return_value;
254   }
255 
256   // Set up the arguments for a call to
257 
258   // struct get_pending_items_return_values
259   // {
260   //     uint64_t pending_items_buffer_ptr;    /* the address of the items
261   //     buffer from libBacktraceRecording */
262   //     uint64_t pending_items_buffer_size;   /* the size of the items buffer
263   //     from libBacktraceRecording */
264   //     uint64_t count;                /* the number of items included in the
265   //     queues buffer */
266   // };
267   //
268   // void  __lldb_backtrace_recording_get_pending_items
269   //                                            (struct
270   //                                            get_pending_items_return_values
271   //                                            *return_buffer,
272   //                                             int debug,
273   //                                             uint64_t /* dispatch_queue_t */
274   //                                             queue
275   //                                             void *page_to_free,
276   //                                             uint64_t page_to_free_size)
277 
278   // Where the return_buffer argument points to a 24 byte region of memory
279   // already allocated by lldb in the inferior process.
280 
281   CompilerType clang_void_ptr_type =
282       clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
283   Value return_buffer_ptr_value;
284   return_buffer_ptr_value.SetValueType(Value::eValueTypeScalar);
285   return_buffer_ptr_value.SetCompilerType(clang_void_ptr_type);
286 
287   CompilerType clang_int_type = clang_ast_context->GetBasicType(eBasicTypeInt);
288   Value debug_value;
289   debug_value.SetValueType(Value::eValueTypeScalar);
290   debug_value.SetCompilerType(clang_int_type);
291 
292   CompilerType clang_uint64_type =
293       clang_ast_context->GetBasicType(eBasicTypeUnsignedLongLong);
294   Value queue_value;
295   queue_value.SetValueType(Value::eValueTypeScalar);
296   queue_value.SetCompilerType(clang_uint64_type);
297 
298   Value page_to_free_value;
299   page_to_free_value.SetValueType(Value::eValueTypeScalar);
300   page_to_free_value.SetCompilerType(clang_void_ptr_type);
301 
302   Value page_to_free_size_value;
303   page_to_free_size_value.SetValueType(Value::eValueTypeScalar);
304   page_to_free_size_value.SetCompilerType(clang_uint64_type);
305 
306   std::lock_guard<std::mutex> guard(m_get_pending_items_retbuffer_mutex);
307   if (m_get_pending_items_return_buffer_addr == LLDB_INVALID_ADDRESS) {
308     addr_t bufaddr = process_sp->AllocateMemory(
309         32, ePermissionsReadable | ePermissionsWritable, error);
310     if (!error.Success() || bufaddr == LLDB_INVALID_ADDRESS) {
311       if (log)
312         log->Printf("Failed to allocate memory for return buffer for get "
313                     "current queues func call");
314       return return_value;
315     }
316     m_get_pending_items_return_buffer_addr = bufaddr;
317   }
318 
319   ValueList argument_values;
320 
321   return_buffer_ptr_value.GetScalar() = m_get_pending_items_return_buffer_addr;
322   argument_values.PushValue(return_buffer_ptr_value);
323 
324   debug_value.GetScalar() = 0;
325   argument_values.PushValue(debug_value);
326 
327   queue_value.GetScalar() = queue;
328   argument_values.PushValue(queue_value);
329 
330   if (page_to_free != LLDB_INVALID_ADDRESS)
331     page_to_free_value.GetScalar() = page_to_free;
332   else
333     page_to_free_value.GetScalar() = 0;
334   argument_values.PushValue(page_to_free_value);
335 
336   page_to_free_size_value.GetScalar() = page_to_free_size;
337   argument_values.PushValue(page_to_free_size_value);
338 
339   addr_t args_addr = SetupGetPendingItemsFunction(thread, argument_values);
340 
341   DiagnosticManager diagnostics;
342   ExecutionContext exe_ctx;
343   FunctionCaller *get_pending_items_caller =
344       m_get_pending_items_impl_code->GetFunctionCaller();
345 
346   EvaluateExpressionOptions options;
347   options.SetUnwindOnError(true);
348   options.SetIgnoreBreakpoints(true);
349   options.SetStopOthers(true);
350   options.SetTimeout(std::chrono::milliseconds(500));
351   options.SetTryAllThreads(false);
352   options.SetIsForUtilityExpr(true);
353   thread.CalculateExecutionContext(exe_ctx);
354 
355   if (get_pending_items_caller == NULL) {
356     error.SetErrorString("Unable to compile function to call "
357                          "__introspection_dispatch_queue_get_pending_items");
358     return return_value;
359   }
360 
361   ExpressionResults func_call_ret;
362   Value results;
363   func_call_ret = get_pending_items_caller->ExecuteFunction(
364       exe_ctx, &args_addr, options, diagnostics, results);
365   if (func_call_ret != eExpressionCompleted || !error.Success()) {
366     if (log)
367       log->Printf("Unable to call "
368                   "__introspection_dispatch_queue_get_pending_items(), got "
369                   "ExpressionResults %d, error contains %s",
370                   func_call_ret, error.AsCString(""));
371     error.SetErrorString("Unable to call "
372                          "__introspection_dispatch_queue_get_pending_items() "
373                          "for list of queues");
374     return return_value;
375   }
376 
377   return_value.items_buffer_ptr = m_process->ReadUnsignedIntegerFromMemory(
378       m_get_pending_items_return_buffer_addr, 8, LLDB_INVALID_ADDRESS, error);
379   if (!error.Success() ||
380       return_value.items_buffer_ptr == LLDB_INVALID_ADDRESS) {
381     return_value.items_buffer_ptr = LLDB_INVALID_ADDRESS;
382     return return_value;
383   }
384 
385   return_value.items_buffer_size = m_process->ReadUnsignedIntegerFromMemory(
386       m_get_pending_items_return_buffer_addr + 8, 8, 0, error);
387 
388   if (!error.Success()) {
389     return_value.items_buffer_ptr = LLDB_INVALID_ADDRESS;
390     return return_value;
391   }
392 
393   return_value.count = m_process->ReadUnsignedIntegerFromMemory(
394       m_get_pending_items_return_buffer_addr + 16, 8, 0, error);
395   if (!error.Success()) {
396     return_value.items_buffer_ptr = LLDB_INVALID_ADDRESS;
397     return return_value;
398   }
399 
400   if (log)
401     log->Printf("AppleGetPendingItemsHandler called "
402                 "__introspection_dispatch_queue_get_pending_items "
403                 "(page_to_free == 0x%" PRIx64 ", size = %" PRId64
404                 "), returned page is at 0x%" PRIx64 ", size %" PRId64
405                 ", count = %" PRId64,
406                 page_to_free, page_to_free_size, return_value.items_buffer_ptr,
407                 return_value.items_buffer_size, return_value.count);
408 
409   return return_value;
410 }
411