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 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 110 AppleGetThreadItemInfoHandler::~AppleGetThreadItemInfoHandler() {} 111 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 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 m_get_thread_item_info_impl_code.reset( 157 exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage( 158 g_get_thread_item_info_function_code, eLanguageTypeC, 159 g_get_thread_item_info_function_name, error)); 160 if (error.Fail()) { 161 LLDB_LOGF(log, 162 "Failed to get UtilityFunction for " 163 "get-thread-item-info introspection: %s.", 164 error.AsCString()); 165 m_get_thread_item_info_impl_code.reset(); 166 return args_addr; 167 } 168 169 if (!m_get_thread_item_info_impl_code->Install(diagnostics, exe_ctx)) { 170 if (log) { 171 LLDB_LOGF(log, 172 "Failed to install get-thread-item-info introspection."); 173 diagnostics.Dump(log); 174 } 175 176 m_get_thread_item_info_impl_code.reset(); 177 return args_addr; 178 } 179 } else { 180 LLDB_LOGF(log, "No get-thread-item-info introspection code found."); 181 return LLDB_INVALID_ADDRESS; 182 } 183 184 // Also make the FunctionCaller for this UtilityFunction: 185 186 TypeSystemClang *clang_ast_context = 187 TypeSystemClang::GetScratch(thread.GetProcess()->GetTarget()); 188 CompilerType get_thread_item_info_return_type = 189 clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); 190 191 get_thread_item_info_caller = 192 m_get_thread_item_info_impl_code->MakeFunctionCaller( 193 get_thread_item_info_return_type, get_thread_item_info_arglist, 194 thread_sp, error); 195 if (error.Fail() || get_thread_item_info_caller == nullptr) { 196 LLDB_LOGF(log, 197 "Failed to install get-thread-item-info introspection " 198 "caller: %s.", 199 error.AsCString()); 200 m_get_thread_item_info_impl_code.reset(); 201 return args_addr; 202 } 203 204 } else { 205 get_thread_item_info_caller = 206 m_get_thread_item_info_impl_code->GetFunctionCaller(); 207 } 208 } 209 210 diagnostics.Clear(); 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_thread_item_info_caller->WriteFunctionArguments( 218 exe_ctx, args_addr, get_thread_item_info_arglist, diagnostics)) { 219 if (log) { 220 LLDB_LOGF(log, "Error writing get-thread-item-info function arguments"); 221 diagnostics.Dump(log); 222 } 223 return args_addr; 224 } 225 226 return args_addr; 227 } 228 229 AppleGetThreadItemInfoHandler::GetThreadItemInfoReturnInfo 230 AppleGetThreadItemInfoHandler::GetThreadItemInfo(Thread &thread, 231 tid_t thread_id, 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 TypeSystemClang *clang_ast_context = TypeSystemClang::GetScratch(*target_sp); 239 Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYSTEM_RUNTIME)); 240 241 GetThreadItemInfoReturnInfo return_value; 242 return_value.item_buffer_ptr = LLDB_INVALID_ADDRESS; 243 return_value.item_buffer_size = 0; 244 245 error.Clear(); 246 247 if (!thread.SafeToCallFunctions()) { 248 LLDB_LOGF(log, "Not safe to call functions on thread 0x%" PRIx64, 249 thread.GetID()); 250 error.SetErrorString("Not safe to call functions on this thread."); 251 return return_value; 252 } 253 254 // Set up the arguments for a call to 255 256 // struct get_thread_item_info_return_values { 257 // uint64_t item_info_buffer_ptr; /* the address of the items buffer 258 // from libBacktraceRecording */ 259 // uint64_t item_info_buffer_size; /* the size of the items buffer from 260 // libBacktraceRecording */ 261 // }; 262 // 263 // void __lldb_backtrace_recording_get_thread_item_info 264 // (struct 265 // get_thread_item_info_return_values 266 // *return_buffer, 267 // int debug, 268 // void *page_to_free, 269 // uint64_t page_to_free_size) 270 271 // Where the return_buffer argument points to a 24 byte region of memory 272 // already allocated by lldb in the inferior process. 273 274 CompilerType clang_void_ptr_type = 275 clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); 276 Value return_buffer_ptr_value; 277 return_buffer_ptr_value.SetValueType(Value::eValueTypeScalar); 278 return_buffer_ptr_value.SetCompilerType(clang_void_ptr_type); 279 280 CompilerType clang_int_type = clang_ast_context->GetBasicType(eBasicTypeInt); 281 Value debug_value; 282 debug_value.SetValueType(Value::eValueTypeScalar); 283 debug_value.SetCompilerType(clang_int_type); 284 285 CompilerType clang_uint64_type = 286 clang_ast_context->GetBasicType(eBasicTypeUnsignedLongLong); 287 Value thread_id_value; 288 thread_id_value.SetValueType(Value::eValueTypeScalar); 289 thread_id_value.SetCompilerType(clang_uint64_type); 290 291 Value page_to_free_value; 292 page_to_free_value.SetValueType(Value::eValueTypeScalar); 293 page_to_free_value.SetCompilerType(clang_void_ptr_type); 294 295 Value page_to_free_size_value; 296 page_to_free_size_value.SetValueType(Value::eValueTypeScalar); 297 page_to_free_size_value.SetCompilerType(clang_uint64_type); 298 299 std::lock_guard<std::mutex> guard(m_get_thread_item_info_retbuffer_mutex); 300 if (m_get_thread_item_info_return_buffer_addr == LLDB_INVALID_ADDRESS) { 301 addr_t bufaddr = process_sp->AllocateMemory( 302 32, ePermissionsReadable | ePermissionsWritable, error); 303 if (!error.Success() || bufaddr == LLDB_INVALID_ADDRESS) { 304 LLDB_LOGF(log, "Failed to allocate memory for return buffer for get " 305 "current queues func call"); 306 return return_value; 307 } 308 m_get_thread_item_info_return_buffer_addr = bufaddr; 309 } 310 311 ValueList argument_values; 312 313 return_buffer_ptr_value.GetScalar() = 314 m_get_thread_item_info_return_buffer_addr; 315 argument_values.PushValue(return_buffer_ptr_value); 316 317 debug_value.GetScalar() = 0; 318 argument_values.PushValue(debug_value); 319 320 thread_id_value.GetScalar() = thread_id; 321 argument_values.PushValue(thread_id_value); 322 323 if (page_to_free != LLDB_INVALID_ADDRESS) 324 page_to_free_value.GetScalar() = page_to_free; 325 else 326 page_to_free_value.GetScalar() = 0; 327 argument_values.PushValue(page_to_free_value); 328 329 page_to_free_size_value.GetScalar() = page_to_free_size; 330 argument_values.PushValue(page_to_free_size_value); 331 332 addr_t args_addr = SetupGetThreadItemInfoFunction(thread, argument_values); 333 334 DiagnosticManager diagnostics; 335 ExecutionContext exe_ctx; 336 EvaluateExpressionOptions options; 337 FunctionCaller *get_thread_item_info_caller = nullptr; 338 339 options.SetUnwindOnError(true); 340 options.SetIgnoreBreakpoints(true); 341 options.SetStopOthers(true); 342 #if __has_feature(address_sanitizer) 343 options.SetTimeout(process_sp->GetUtilityExpressionTimeout()); 344 #else 345 options.SetTimeout(std::chrono::milliseconds(500)); 346 #endif 347 options.SetTryAllThreads(false); 348 options.SetIsForUtilityExpr(true); 349 thread.CalculateExecutionContext(exe_ctx); 350 351 if (!m_get_thread_item_info_impl_code) { 352 error.SetErrorString("Unable to compile function to call " 353 "__introspection_dispatch_thread_get_item_info"); 354 return return_value; 355 } 356 357 get_thread_item_info_caller = 358 m_get_thread_item_info_impl_code->GetFunctionCaller(); 359 360 if (!get_thread_item_info_caller) { 361 error.SetErrorString("Unable to compile function caller for " 362 "__introspection_dispatch_thread_get_item_info"); 363 return return_value; 364 } 365 366 ExpressionResults func_call_ret; 367 Value results; 368 func_call_ret = get_thread_item_info_caller->ExecuteFunction( 369 exe_ctx, &args_addr, options, diagnostics, results); 370 if (func_call_ret != eExpressionCompleted || !error.Success()) { 371 LLDB_LOGF(log, 372 "Unable to call " 373 "__introspection_dispatch_thread_get_item_info(), got " 374 "ExpressionResults %d, error contains %s", 375 func_call_ret, error.AsCString("")); 376 error.SetErrorString("Unable to call " 377 "__introspection_dispatch_thread_get_item_info() for " 378 "list of queues"); 379 return return_value; 380 } 381 382 return_value.item_buffer_ptr = m_process->ReadUnsignedIntegerFromMemory( 383 m_get_thread_item_info_return_buffer_addr, 8, LLDB_INVALID_ADDRESS, 384 error); 385 if (!error.Success() || 386 return_value.item_buffer_ptr == LLDB_INVALID_ADDRESS) { 387 return_value.item_buffer_ptr = LLDB_INVALID_ADDRESS; 388 return return_value; 389 } 390 391 return_value.item_buffer_size = m_process->ReadUnsignedIntegerFromMemory( 392 m_get_thread_item_info_return_buffer_addr + 8, 8, 0, error); 393 394 if (!error.Success()) { 395 return_value.item_buffer_ptr = LLDB_INVALID_ADDRESS; 396 return return_value; 397 } 398 399 LLDB_LOGF(log, 400 "AppleGetThreadItemInfoHandler called " 401 "__introspection_dispatch_thread_get_item_info (page_to_free " 402 "== 0x%" PRIx64 ", size = %" PRId64 403 "), returned page is at 0x%" PRIx64 ", size %" PRId64, 404 page_to_free, page_to_free_size, return_value.item_buffer_ptr, 405 return_value.item_buffer_size); 406 407 return return_value; 408 } 409