LLDB  mainline
AppleGetThreadItemInfoHandler.cpp
Go to the documentation of this file.
1 //===-- AppleGetThreadItemInfoHandler.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"
20 #include "lldb/Symbol/Symbol.h"
22 #include "lldb/Target/Process.h"
23 #include "lldb/Target/StackFrame.h"
24 #include "lldb/Target/Target.h"
25 #include "lldb/Target/Thread.h"
27 #include "lldb/Utility/Log.h"
29 #include "lldb/lldb-private.h"
30 
31 using namespace lldb;
32 using namespace lldb_private;
33 
34 const char
35  *AppleGetThreadItemInfoHandler::g_get_thread_item_info_function_name =
36  "__lldb_backtrace_recording_get_thread_item_info";
37 const char
38  *AppleGetThreadItemInfoHandler::g_get_thread_item_info_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  typedef void *pthread_t; \n\
58  extern int printf(const char *format, ...); \n\
59  extern pthread_t pthread_self(void); \n\
60  \n\
61  /* \n\
62  * libBacktraceRecording defines \n\
63  */ \n\
64  \n\
65  typedef uint32_t queue_list_scope_t; \n\
66  typedef void *dispatch_queue_t; \n\
67  typedef void *introspection_dispatch_queue_info_t; \n\
68  typedef void *introspection_dispatch_item_info_ref; \n\
69  \n\
70  extern void __introspection_dispatch_thread_get_item_info (uint64_t thread_id, \n\
71  introspection_dispatch_item_info_ref *returned_queues_buffer, \n\
72  uint64_t *returned_queues_buffer_size); \n\
73  \n\
74  /* \n\
75  * return type define \n\
76  */ \n\
77  \n\
78  struct get_thread_item_info_return_values \n\
79  { \n\
80  uint64_t item_info_buffer_ptr; /* the address of the items buffer from libBacktraceRecording */ \n\
81  uint64_t item_info_buffer_size; /* the size of the items buffer from libBacktraceRecording */ \n\
82  }; \n\
83  \n\
84  void __lldb_backtrace_recording_get_thread_item_info \n\
85  (struct get_thread_item_info_return_values *return_buffer, \n\
86  int debug, \n\
87  uint64_t thread_id, \n\
88  void *page_to_free, \n\
89  uint64_t page_to_free_size) \n\
90 { \n\
91  void *pthread_id = pthread_self (); \n\
92  if (debug) \n\
93  printf (\"entering get_thread_item_info with args return_buffer == %p, debug == %d, thread id == 0x%llx, page_to_free == %p, page_to_free_size == 0x%llx\\n\", return_buffer, debug, (uint64_t) thread_id, page_to_free, page_to_free_size); \n\
94  if (page_to_free != 0) \n\
95  { \n\
96  mach_vm_deallocate (mach_task_self(), (mach_vm_address_t) page_to_free, (mach_vm_size_t) page_to_free_size); \n\
97  } \n\
98  \n\
99  __introspection_dispatch_thread_get_item_info (thread_id, \n\
100  (void**)&return_buffer->item_info_buffer_ptr, \n\
101  &return_buffer->item_info_buffer_size); \n\
102 } \n\
103 } \n\
104 ";
105 
106 AppleGetThreadItemInfoHandler::AppleGetThreadItemInfoHandler(Process *process)
107  : m_process(process), m_get_thread_item_info_impl_code(),
108  m_get_thread_item_info_function_mutex(),
109  m_get_thread_item_info_return_buffer_addr(LLDB_INVALID_ADDRESS),
110  m_get_thread_item_info_retbuffer_mutex() {}
111 
113 
115 
116  if (m_process && m_process->IsAlive() &&
117  m_get_thread_item_info_return_buffer_addr != LLDB_INVALID_ADDRESS) {
118  std::unique_lock<std::mutex> lock(m_get_thread_item_info_retbuffer_mutex,
119  std::defer_lock);
120  lock.try_lock(); // Even if we don't get the lock, deallocate the buffer
121  m_process->DeallocateMemory(m_get_thread_item_info_return_buffer_addr);
122  }
123 }
124 
125 // Compile our __lldb_backtrace_recording_get_thread_item_info() function (from
126 // the source above in g_get_thread_item_info_function_code) if we don't find
127 // that function in the inferior already with USE_BUILTIN_FUNCTION defined.
128 // (e.g. this would be the case for testing.)
129 //
130 // Insert the __lldb_backtrace_recording_get_thread_item_info into the inferior
131 // process if needed.
132 //
133 // Write the get_thread_item_info_arglist into the inferior's memory space to
134 // prepare for the call.
135 //
136 // Returns the address of the arguments written down in the inferior process,
137 // which can be used to make the function call.
138 
139 lldb::addr_t AppleGetThreadItemInfoHandler::SetupGetThreadItemInfoFunction(
140  Thread &thread, ValueList &get_thread_item_info_arglist) {
141  ThreadSP thread_sp(thread.shared_from_this());
142  ExecutionContext exe_ctx(thread_sp);
143  Address impl_code_address;
144  DiagnosticManager diagnostics;
147  FunctionCaller *get_thread_item_info_caller = nullptr;
148 
149  // Scope for mutex locker:
150  {
151  std::lock_guard<std::mutex> guard(m_get_thread_item_info_function_mutex);
152 
153  // First stage is to make the ClangUtility to hold our injected function:
154 
155  if (!m_get_thread_item_info_impl_code) {
156  Status error;
157  if (g_get_thread_item_info_function_code != NULL) {
158  m_get_thread_item_info_impl_code.reset(
159  exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage(
160  g_get_thread_item_info_function_code, eLanguageTypeC,
161  g_get_thread_item_info_function_name, error));
162  if (error.Fail()) {
163  if (log)
164  log->Printf("Failed to get UtilityFunction for "
165  "get-thread-item-info introspection: %s.",
166  error.AsCString());
167  m_get_thread_item_info_impl_code.reset();
168  return args_addr;
169  }
170 
171  if (!m_get_thread_item_info_impl_code->Install(diagnostics, exe_ctx)) {
172  if (log) {
173  log->Printf(
174  "Failed to install get-thread-item-info introspection.");
175  diagnostics.Dump(log);
176  }
177 
178  m_get_thread_item_info_impl_code.reset();
179  return args_addr;
180  }
181  } else {
182  if (log)
183  log->Printf("No get-thread-item-info introspection code found.");
184  return LLDB_INVALID_ADDRESS;
185  }
186 
187  // Also make the FunctionCaller for this UtilityFunction:
188 
189  ClangASTContext *clang_ast_context =
190  thread.GetProcess()->GetTarget().GetScratchClangASTContext();
191  CompilerType get_thread_item_info_return_type =
192  clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
193 
194  get_thread_item_info_caller =
195  m_get_thread_item_info_impl_code->MakeFunctionCaller(
196  get_thread_item_info_return_type, get_thread_item_info_arglist,
197  thread_sp, error);
198  if (error.Fail() || get_thread_item_info_caller == nullptr) {
199  if (log)
200  log->Printf("Failed to install get-thread-item-info introspection "
201  "caller: %s.",
202  error.AsCString());
203  m_get_thread_item_info_impl_code.reset();
204  return args_addr;
205  }
206 
207  } else {
208  get_thread_item_info_caller =
209  m_get_thread_item_info_impl_code->GetFunctionCaller();
210  }
211  }
212 
213  diagnostics.Clear();
214 
215  // Now write down the argument values for this particular call. This looks
216  // like it might be a race condition if other threads were calling into here,
217  // but actually it isn't because we allocate a new args structure for this
218  // call by passing args_addr = LLDB_INVALID_ADDRESS...
219 
220  if (!get_thread_item_info_caller->WriteFunctionArguments(
221  exe_ctx, args_addr, get_thread_item_info_arglist, diagnostics)) {
222  if (log) {
223  log->Printf("Error writing get-thread-item-info function arguments");
224  diagnostics.Dump(log);
225  }
226  return args_addr;
227  }
228 
229  return args_addr;
230 }
231 
234  tid_t thread_id,
235  addr_t page_to_free,
236  uint64_t page_to_free_size,
237  Status &error) {
238  lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0);
239  ProcessSP process_sp(thread.CalculateProcess());
240  TargetSP target_sp(thread.CalculateTarget());
241  ClangASTContext *clang_ast_context = target_sp->GetScratchClangASTContext();
243 
244  GetThreadItemInfoReturnInfo return_value;
245  return_value.item_buffer_ptr = LLDB_INVALID_ADDRESS;
246  return_value.item_buffer_size = 0;
247 
248  error.Clear();
249 
250  if (!thread.SafeToCallFunctions()) {
251  if (log)
252  log->Printf("Not safe to call functions on thread 0x%" PRIx64,
253  thread.GetID());
254  error.SetErrorString("Not safe to call functions on this thread.");
255  return return_value;
256  }
257 
258  // Set up the arguments for a call to
259 
260  // struct get_thread_item_info_return_values {
261  // uint64_t item_info_buffer_ptr; /* the address of the items buffer
262  // from libBacktraceRecording */
263  // uint64_t item_info_buffer_size; /* the size of the items buffer from
264  // libBacktraceRecording */
265  // };
266  //
267  // void __lldb_backtrace_recording_get_thread_item_info
268  // (struct
269  // get_thread_item_info_return_values
270  // *return_buffer,
271  // int debug,
272  // void *page_to_free,
273  // uint64_t page_to_free_size)
274 
275  // Where the return_buffer argument points to a 24 byte region of memory
276  // already allocated by lldb in the inferior process.
277 
278  CompilerType clang_void_ptr_type =
279  clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
280  Value return_buffer_ptr_value;
281  return_buffer_ptr_value.SetValueType(Value::eValueTypeScalar);
282  return_buffer_ptr_value.SetCompilerType(clang_void_ptr_type);
283 
284  CompilerType clang_int_type = clang_ast_context->GetBasicType(eBasicTypeInt);
285  Value debug_value;
287  debug_value.SetCompilerType(clang_int_type);
288 
289  CompilerType clang_uint64_type =
290  clang_ast_context->GetBasicType(eBasicTypeUnsignedLongLong);
291  Value thread_id_value;
292  thread_id_value.SetValueType(Value::eValueTypeScalar);
293  thread_id_value.SetCompilerType(clang_uint64_type);
294 
295  Value page_to_free_value;
296  page_to_free_value.SetValueType(Value::eValueTypeScalar);
297  page_to_free_value.SetCompilerType(clang_void_ptr_type);
298 
299  Value page_to_free_size_value;
300  page_to_free_size_value.SetValueType(Value::eValueTypeScalar);
301  page_to_free_size_value.SetCompilerType(clang_uint64_type);
302 
303  std::lock_guard<std::mutex> guard(m_get_thread_item_info_retbuffer_mutex);
304  if (m_get_thread_item_info_return_buffer_addr == LLDB_INVALID_ADDRESS) {
305  addr_t bufaddr = process_sp->AllocateMemory(
306  32, ePermissionsReadable | ePermissionsWritable, error);
307  if (!error.Success() || bufaddr == LLDB_INVALID_ADDRESS) {
308  if (log)
309  log->Printf("Failed to allocate memory for return buffer for get "
310  "current queues func call");
311  return return_value;
312  }
313  m_get_thread_item_info_return_buffer_addr = bufaddr;
314  }
315 
316  ValueList argument_values;
317 
318  return_buffer_ptr_value.GetScalar() =
319  m_get_thread_item_info_return_buffer_addr;
320  argument_values.PushValue(return_buffer_ptr_value);
321 
322  debug_value.GetScalar() = 0;
323  argument_values.PushValue(debug_value);
324 
325  thread_id_value.GetScalar() = thread_id;
326  argument_values.PushValue(thread_id_value);
327 
328  if (page_to_free != LLDB_INVALID_ADDRESS)
329  page_to_free_value.GetScalar() = page_to_free;
330  else
331  page_to_free_value.GetScalar() = 0;
332  argument_values.PushValue(page_to_free_value);
333 
334  page_to_free_size_value.GetScalar() = page_to_free_size;
335  argument_values.PushValue(page_to_free_size_value);
336 
337  addr_t args_addr = SetupGetThreadItemInfoFunction(thread, argument_values);
338 
339  DiagnosticManager diagnostics;
340  ExecutionContext exe_ctx;
342  FunctionCaller *get_thread_item_info_caller = nullptr;
343 
344  options.SetUnwindOnError(true);
345  options.SetIgnoreBreakpoints(true);
346  options.SetStopOthers(true);
347  options.SetTimeout(process_sp->GetUtilityExpressionTimeout());
348  options.SetTryAllThreads(false);
349  options.SetIsForUtilityExpr(true);
350  thread.CalculateExecutionContext(exe_ctx);
351 
352  if (!m_get_thread_item_info_impl_code) {
353  error.SetErrorString("Unable to compile function to call "
354  "__introspection_dispatch_thread_get_item_info");
355  return return_value;
356  }
357 
358  get_thread_item_info_caller =
359  m_get_thread_item_info_impl_code->GetFunctionCaller();
360 
361  if (!get_thread_item_info_caller) {
362  error.SetErrorString("Unable to compile function caller for "
363  "__introspection_dispatch_thread_get_item_info");
364  return return_value;
365  }
366 
367  ExpressionResults func_call_ret;
368  Value results;
369  func_call_ret = get_thread_item_info_caller->ExecuteFunction(
370  exe_ctx, &args_addr, options, diagnostics, results);
371  if (func_call_ret != eExpressionCompleted || !error.Success()) {
372  if (log)
373  log->Printf("Unable to call "
374  "__introspection_dispatch_thread_get_item_info(), got "
375  "ExpressionResults %d, error contains %s",
376  func_call_ret, error.AsCString(""));
377  error.SetErrorString("Unable to call "
378  "__introspection_dispatch_thread_get_item_info() for "
379  "list of queues");
380  return return_value;
381  }
382 
383  return_value.item_buffer_ptr = m_process->ReadUnsignedIntegerFromMemory(
384  m_get_thread_item_info_return_buffer_addr, 8, LLDB_INVALID_ADDRESS,
385  error);
386  if (!error.Success() ||
387  return_value.item_buffer_ptr == LLDB_INVALID_ADDRESS) {
388  return_value.item_buffer_ptr = LLDB_INVALID_ADDRESS;
389  return return_value;
390  }
391 
392  return_value.item_buffer_size = m_process->ReadUnsignedIntegerFromMemory(
393  m_get_thread_item_info_return_buffer_addr + 8, 8, 0, error);
394 
395  if (!error.Success()) {
396  return_value.item_buffer_ptr = LLDB_INVALID_ADDRESS;
397  return return_value;
398  }
399 
400  if (log)
401  log->Printf("AppleGetThreadItemInfoHandler called "
402  "__introspection_dispatch_thread_get_item_info (page_to_free "
403  "== 0x%" PRIx64 ", size = %" PRId64
404  "), returned page is at 0x%" PRIx64 ", size %" PRId64,
405  page_to_free, page_to_free_size, return_value.item_buffer_ptr,
406  return_value.item_buffer_size);
407 
408  return return_value;
409 }
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
uint64_t tid_t
Definition: lldb-types.h:86
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
GetThreadItemInfoReturnInfo GetThreadItemInfo(Thread &thread, lldb::tid_t thread_id, lldb::addr_t page_to_free, uint64_t page_to_free_size, lldb_private::Status &error)
Get the information about a work item by calling __introspection_dispatch_thread_get_item_info.
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