LLDB  mainline
AppleGetQueuesHandler.cpp
Go to the documentation of this file.
1 //===-- AppleGetQueuesHandler.cpp -------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
10 
11 #include "lldb/Core/Module.h"
12 #include "lldb/Core/Value.h"
17 #include "lldb/Symbol/Symbol.h"
19 #include "lldb/Target/Process.h"
20 #include "lldb/Target/Target.h"
21 #include "lldb/Target/Thread.h"
23 #include "lldb/Utility/Log.h"
25 
26 using namespace lldb;
27 using namespace lldb_private;
28 
29 const char *AppleGetQueuesHandler::g_get_current_queues_function_name =
30  "__lldb_backtrace_recording_get_current_queues";
31 const char *AppleGetQueuesHandler::g_get_current_queues_function_code =
32  " \n\
33 extern \"C\" \n\
34 { \n\
35  /* \n\
36  * mach defines \n\
37  */ \n\
38  \n\
39  typedef unsigned int uint32_t; \n\
40  typedef unsigned long long uint64_t; \n\
41  typedef uint32_t mach_port_t; \n\
42  typedef mach_port_t vm_map_t; \n\
43  typedef int kern_return_t; \n\
44  typedef uint64_t mach_vm_address_t; \n\
45  typedef uint64_t mach_vm_size_t; \n\
46  \n\
47  mach_port_t mach_task_self (); \n\
48  kern_return_t mach_vm_deallocate (vm_map_t target, mach_vm_address_t address, mach_vm_size_t size); \n\
49  \n\
50  /* \n\
51  * libBacktraceRecording defines \n\
52  */ \n\
53  \n\
54  typedef uint32_t queue_list_scope_t; \n\
55  typedef void *introspection_dispatch_queue_info_t; \n\
56  \n\
57  extern uint64_t __introspection_dispatch_get_queues (queue_list_scope_t scope, \n\
58  introspection_dispatch_queue_info_t *returned_queues_buffer, \n\
59  uint64_t *returned_queues_buffer_size); \n\
60  extern int printf(const char *format, ...); \n\
61  \n\
62  /* \n\
63  * return type define \n\
64  */ \n\
65  \n\
66  struct get_current_queues_return_values \n\
67  { \n\
68  uint64_t queues_buffer_ptr; /* the address of the queues buffer from libBacktraceRecording */ \n\
69  uint64_t queues_buffer_size; /* the size of the queues buffer from libBacktraceRecording */ \n\
70  uint64_t count; /* the number of queues included in the queues buffer */ \n\
71  }; \n\
72  \n\
73  void __lldb_backtrace_recording_get_current_queues \n\
74  (struct get_current_queues_return_values *return_buffer, \n\
75  int debug, \n\
76  void *page_to_free, \n\
77  uint64_t page_to_free_size) \n\
78 { \n\
79  if (debug) \n\
80  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\
81  if (page_to_free != 0) \n\
82  { \n\
83  mach_vm_deallocate (mach_task_self(), (mach_vm_address_t) page_to_free, (mach_vm_size_t) page_to_free_size); \n\
84  } \n\
85  \n\
86  return_buffer->count = __introspection_dispatch_get_queues ( \n\
87  /* QUEUES_WITH_ANY_ITEMS */ 2, \n\
88  (void**)&return_buffer->queues_buffer_ptr, \n\
89  &return_buffer->queues_buffer_size); \n\
90  if (debug) \n\
91  printf(\"result was count %lld\\n\", return_buffer->count); \n\
92 } \n\
93 } \n\
94 ";
95 
96 AppleGetQueuesHandler::AppleGetQueuesHandler(Process *process)
97  : m_process(process), m_get_queues_impl_code_up(),
98  m_get_queues_function_mutex(),
99  m_get_queues_return_buffer_addr(LLDB_INVALID_ADDRESS),
100  m_get_queues_retbuffer_mutex() {}
101 
103 
105 
106  if (m_process && m_process->IsAlive() &&
107  m_get_queues_return_buffer_addr != LLDB_INVALID_ADDRESS) {
108  std::unique_lock<std::mutex> lock(m_get_queues_retbuffer_mutex,
109  std::defer_lock);
110  lock.try_lock(); // Even if we don't get the lock, deallocate the buffer
111  m_process->DeallocateMemory(m_get_queues_return_buffer_addr);
112  }
113 }
114 
115 // Construct a CompilerType for the structure that
116 // g_get_current_queues_function_code will return by value so we can extract
117 // the fields after performing the function call. i.e. we are getting this
118 // struct returned to us:
119 //
120 // struct get_current_queues_return_values
121 // {
122 // introspection_dispatch_queue_info_t *queues_buffer;
123 // uint64_t queues_buffer_size;
124 // uint64_t count;
125 // };
126 
127 // Compile our __lldb_backtrace_recording_get_current_queues() function (from
128 // the source above in g_get_current_queues_function_code) if we don't find
129 // that function in the inferior already with USE_BUILTIN_FUNCTION defined.
130 // (e.g. this would be the case for testing.)
131 //
132 // Insert the __lldb_backtrace_recording_get_current_queues into the inferior
133 // process if needed.
134 //
135 // Write the get_queues_arglist into the inferior's memory space to prepare for
136 // the call.
137 //
138 // Returns the address of the arguments written down in the inferior process,
139 // which can be used to make the function call.
140 
142 AppleGetQueuesHandler::SetupGetQueuesFunction(Thread &thread,
143  ValueList &get_queues_arglist) {
144  ThreadSP thread_sp(thread.shared_from_this());
145  ExecutionContext exe_ctx(thread_sp);
146 
147  Address impl_code_address;
148  DiagnosticManager diagnostics;
151 
152  FunctionCaller *get_queues_caller = nullptr;
153 
154  // Scope for mutex locker:
155  {
156  std::lock_guard<std::mutex> guard(m_get_queues_function_mutex);
157 
158  // First stage is to make the ClangUtility to hold our injected function:
159 
160  if (!m_get_queues_impl_code_up) {
161  if (g_get_current_queues_function_code != NULL) {
162  Status error;
163  m_get_queues_impl_code_up.reset(
164  exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage(
165  g_get_current_queues_function_code, eLanguageTypeC,
166  g_get_current_queues_function_name, error));
167  if (error.Fail()) {
168  if (log)
169  log->Printf(
170  "Failed to get UtilityFunction for queues introspection: %s.",
171  error.AsCString());
172  return args_addr;
173  }
174 
175  if (!m_get_queues_impl_code_up->Install(diagnostics, exe_ctx)) {
176  if (log) {
177  log->Printf("Failed to install queues introspection");
178  diagnostics.Dump(log);
179  }
180  m_get_queues_impl_code_up.reset();
181  return args_addr;
182  }
183  } else {
184  if (log) {
185  log->Printf("No queues introspection code found.");
186  diagnostics.Dump(log);
187  }
188  return LLDB_INVALID_ADDRESS;
189  }
190  }
191 
192  // Next make the runner function for our implementation utility function.
193  ClangASTContext *clang_ast_context =
194  thread.GetProcess()->GetTarget().GetScratchClangASTContext();
195  CompilerType get_queues_return_type =
196  clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
197  Status error;
198  get_queues_caller = m_get_queues_impl_code_up->MakeFunctionCaller(
199  get_queues_return_type, get_queues_arglist, thread_sp, error);
200  if (error.Fail() || get_queues_caller == nullptr) {
201  if (log)
202  log->Printf(
203  "Could not get function caller for get-queues function: %s.",
204  error.AsCString());
205  return args_addr;
206  }
207  }
208 
209  diagnostics.Clear();
210 
211  // Now write down the argument values for this particular call. This looks
212  // like it might be a race condition if other threads were calling into here,
213  // but actually it isn't because we allocate a new args structure for this
214  // call by passing args_addr = LLDB_INVALID_ADDRESS...
215 
216  if (!get_queues_caller->WriteFunctionArguments(
217  exe_ctx, args_addr, get_queues_arglist, diagnostics)) {
218  if (log) {
219  log->Printf("Error writing get-queues function arguments.");
220  diagnostics.Dump(log);
221  }
222  return args_addr;
223  }
224 
225  return args_addr;
226 }
227 
230  uint64_t page_to_free_size,
231  Status &error) {
232  lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0);
233  ProcessSP process_sp(thread.CalculateProcess());
234  TargetSP target_sp(thread.CalculateTarget());
235  ClangASTContext *clang_ast_context = target_sp->GetScratchClangASTContext();
237 
238  GetQueuesReturnInfo return_value;
240  return_value.queues_buffer_size = 0;
241  return_value.count = 0;
242 
243  error.Clear();
244 
245  if (!thread.SafeToCallFunctions()) {
246  if (log)
247  log->Printf("Not safe to call functions on thread 0x%" PRIx64,
248  thread.GetID());
249  error.SetErrorString("Not safe to call functions on this thread.");
250  return return_value;
251  }
252 
253  // Set up the arguments for a call to
254 
255  // struct get_current_queues_return_values
256  // {
257  // uint64_t queues_buffer_ptr; /* the address of the queues buffer from
258  // libBacktraceRecording */
259  // uint64_t queues_buffer_size; /* the size of the queues buffer from
260  // libBacktraceRecording */
261  // uint64_t count; /* the number of queues included in the
262  // queues buffer */
263  // };
264  //
265  // void
266  // __lldb_backtrace_recording_get_current_queues
267  // (struct
268  // get_current_queues_return_values
269  // *return_buffer,
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  Value page_to_free_value;
288  page_to_free_value.SetValueType(Value::eValueTypeScalar);
289  page_to_free_value.SetCompilerType(clang_void_ptr_type);
290 
291  CompilerType clang_uint64_type =
292  clang_ast_context->GetBasicType(eBasicTypeUnsignedLongLong);
293  Value page_to_free_size_value;
294  page_to_free_size_value.SetValueType(Value::eValueTypeScalar);
295  page_to_free_size_value.SetCompilerType(clang_uint64_type);
296 
297  std::lock_guard<std::mutex> guard(m_get_queues_retbuffer_mutex);
298  if (m_get_queues_return_buffer_addr == LLDB_INVALID_ADDRESS) {
299  addr_t bufaddr = process_sp->AllocateMemory(
300  32, ePermissionsReadable | ePermissionsWritable, error);
301  if (!error.Success() || bufaddr == LLDB_INVALID_ADDRESS) {
302  if (log)
303  log->Printf("Failed to allocate memory for return buffer for get "
304  "current queues func call");
305  return return_value;
306  }
307  m_get_queues_return_buffer_addr = bufaddr;
308  }
309 
310  ValueList argument_values;
311 
312  return_buffer_ptr_value.GetScalar() = m_get_queues_return_buffer_addr;
313  argument_values.PushValue(return_buffer_ptr_value);
314 
315  debug_value.GetScalar() = 0;
316  argument_values.PushValue(debug_value);
317 
318  if (page_to_free != LLDB_INVALID_ADDRESS)
319  page_to_free_value.GetScalar() = page_to_free;
320  else
321  page_to_free_value.GetScalar() = 0;
322  argument_values.PushValue(page_to_free_value);
323 
324  page_to_free_size_value.GetScalar() = page_to_free_size;
325  argument_values.PushValue(page_to_free_size_value);
326 
327  addr_t args_addr = SetupGetQueuesFunction(thread, argument_values);
328 
329  if (!m_get_queues_impl_code_up) {
330  error.SetErrorString(
331  "Unable to compile __introspection_dispatch_get_queues.");
332  return return_value;
333  }
334 
335  FunctionCaller *get_queues_caller =
336  m_get_queues_impl_code_up->GetFunctionCaller();
337 
338  if (get_queues_caller == NULL) {
339  error.SetErrorString(
340  "Unable to get caller for call __introspection_dispatch_get_queues");
341  return return_value;
342  }
343 
344  DiagnosticManager diagnostics;
345  ExecutionContext exe_ctx;
347  options.SetUnwindOnError(true);
348  options.SetIgnoreBreakpoints(true);
349  options.SetStopOthers(true);
350  options.SetTimeout(process_sp->GetUtilityExpressionTimeout());
351  options.SetTryAllThreads(false);
352  options.SetIsForUtilityExpr(true);
353  thread.CalculateExecutionContext(exe_ctx);
354 
355  ExpressionResults func_call_ret;
356  Value results;
357  func_call_ret = get_queues_caller->ExecuteFunction(
358  exe_ctx, &args_addr, options, diagnostics, results);
359  if (func_call_ret != eExpressionCompleted || !error.Success()) {
360  if (log)
361  log->Printf("Unable to call introspection_get_dispatch_queues(), got "
362  "ExpressionResults %d, error contains %s",
363  func_call_ret, error.AsCString(""));
364  error.SetErrorString("Unable to call introspection_get_dispatch_queues() "
365  "for list of queues");
366  return return_value;
367  }
368 
369  return_value.queues_buffer_ptr = m_process->ReadUnsignedIntegerFromMemory(
370  m_get_queues_return_buffer_addr, 8, LLDB_INVALID_ADDRESS, error);
371  if (!error.Success() ||
372  return_value.queues_buffer_ptr == LLDB_INVALID_ADDRESS) {
374  return return_value;
375  }
376 
377  return_value.queues_buffer_size = m_process->ReadUnsignedIntegerFromMemory(
378  m_get_queues_return_buffer_addr + 8, 8, 0, error);
379 
380  if (!error.Success()) {
382  return return_value;
383  }
384 
385  return_value.count = m_process->ReadUnsignedIntegerFromMemory(
386  m_get_queues_return_buffer_addr + 16, 8, 0, error);
387  if (!error.Success()) {
389  return return_value;
390  }
391 
392  if (log)
393  log->Printf("AppleGetQueuesHandler called "
394  "__introspection_dispatch_get_queues (page_to_free == "
395  "0x%" PRIx64 ", size = %" PRId64
396  "), returned page is at 0x%" PRIx64 ", size %" PRId64
397  ", count = %" PRId64,
398  page_to_free, page_to_free_size, return_value.queues_buffer_ptr,
399  return_value.queues_buffer_size, return_value.count);
400 
401  return return_value;
402 }
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.
GetQueuesReturnInfo GetCurrentQueues(Thread &thread, lldb::addr_t page_to_free, uint64_t page_to_free_size, lldb_private::Status &error)
Get the list of queues that exist (with any active or pending items) via a call to introspection_get_...
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
A section + offset based address class.
Definition: Address.h:80
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
Non-standardized C, such as K&R.
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
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