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