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