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