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