//===-- SystemRuntimeMacOSX.cpp -------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "Plugins/Process/Utility/HistoryThread.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Section.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/ProcessStructReader.h" #include "lldb/Target/Queue.h" #include "lldb/Target/QueueList.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/StreamString.h" #include "SystemRuntimeMacOSX.h" #include using namespace lldb; using namespace lldb_private; LLDB_PLUGIN_DEFINE(SystemRuntimeMacOSX) // Create an instance of this class. This function is filled into the plugin // info class that gets handed out by the plugin factory and allows the lldb to // instantiate an instance of this class. SystemRuntime *SystemRuntimeMacOSX::CreateInstance(Process *process) { bool create = false; if (!create) { create = true; Module *exe_module = process->GetTarget().GetExecutableModulePointer(); if (exe_module) { ObjectFile *object_file = exe_module->GetObjectFile(); if (object_file) { create = (object_file->GetStrata() == ObjectFile::eStrataUser); } } if (create) { const llvm::Triple &triple_ref = process->GetTarget().GetArchitecture().GetTriple(); switch (triple_ref.getOS()) { case llvm::Triple::Darwin: case llvm::Triple::MacOSX: case llvm::Triple::IOS: case llvm::Triple::TvOS: case llvm::Triple::WatchOS: // NEED_BRIDGEOS_TRIPLE case llvm::Triple::BridgeOS: create = triple_ref.getVendor() == llvm::Triple::Apple; break; default: create = false; break; } } } if (create) return new SystemRuntimeMacOSX(process); return nullptr; } // Constructor SystemRuntimeMacOSX::SystemRuntimeMacOSX(Process *process) : SystemRuntime(process), m_break_id(LLDB_INVALID_BREAK_ID), m_mutex(), m_get_queues_handler(process), m_get_pending_items_handler(process), m_get_item_info_handler(process), m_get_thread_item_info_handler(process), m_page_to_free(LLDB_INVALID_ADDRESS), m_page_to_free_size(0), m_lib_backtrace_recording_info(), m_dispatch_queue_offsets_addr(LLDB_INVALID_ADDRESS), m_libdispatch_offsets(), m_libpthread_layout_offsets_addr(LLDB_INVALID_ADDRESS), m_libpthread_offsets(), m_dispatch_tsd_indexes_addr(LLDB_INVALID_ADDRESS), m_libdispatch_tsd_indexes(), m_dispatch_voucher_offsets_addr(LLDB_INVALID_ADDRESS), m_libdispatch_voucher_offsets() {} // Destructor SystemRuntimeMacOSX::~SystemRuntimeMacOSX() { Clear(true); } void SystemRuntimeMacOSX::Detach() { m_get_queues_handler.Detach(); m_get_pending_items_handler.Detach(); m_get_item_info_handler.Detach(); m_get_thread_item_info_handler.Detach(); } // Clear out the state of this class. void SystemRuntimeMacOSX::Clear(bool clear_process) { std::lock_guard guard(m_mutex); if (m_process->IsAlive() && LLDB_BREAK_ID_IS_VALID(m_break_id)) m_process->ClearBreakpointSiteByID(m_break_id); if (clear_process) m_process = nullptr; m_break_id = LLDB_INVALID_BREAK_ID; } std::string SystemRuntimeMacOSX::GetQueueNameFromThreadQAddress(addr_t dispatch_qaddr) { std::string dispatch_queue_name; if (dispatch_qaddr == LLDB_INVALID_ADDRESS || dispatch_qaddr == 0) return ""; ReadLibdispatchOffsets(); if (m_libdispatch_offsets.IsValid()) { // dispatch_qaddr is from a thread_info(THREAD_IDENTIFIER_INFO) call for a // thread - deref it to get the address of the dispatch_queue_t structure // for this thread's queue. Status error; addr_t dispatch_queue_addr = m_process->ReadPointerFromMemory(dispatch_qaddr, error); if (error.Success()) { if (m_libdispatch_offsets.dqo_version >= 4) { // libdispatch versions 4+, pointer to dispatch name is in the queue // structure. addr_t pointer_to_label_address = dispatch_queue_addr + m_libdispatch_offsets.dqo_label; addr_t label_addr = m_process->ReadPointerFromMemory(pointer_to_label_address, error); if (error.Success()) { m_process->ReadCStringFromMemory(label_addr, dispatch_queue_name, error); } } else { // libdispatch versions 1-3, dispatch name is a fixed width char array // in the queue structure. addr_t label_addr = dispatch_queue_addr + m_libdispatch_offsets.dqo_label; dispatch_queue_name.resize(m_libdispatch_offsets.dqo_label_size, '\0'); size_t bytes_read = m_process->ReadMemory(label_addr, &dispatch_queue_name[0], m_libdispatch_offsets.dqo_label_size, error); if (bytes_read < m_libdispatch_offsets.dqo_label_size) dispatch_queue_name.erase(bytes_read); } } } return dispatch_queue_name; } lldb::addr_t SystemRuntimeMacOSX::GetLibdispatchQueueAddressFromThreadQAddress( addr_t dispatch_qaddr) { addr_t libdispatch_queue_t_address = LLDB_INVALID_ADDRESS; Status error; libdispatch_queue_t_address = m_process->ReadPointerFromMemory(dispatch_qaddr, error); if (!error.Success()) { libdispatch_queue_t_address = LLDB_INVALID_ADDRESS; } return libdispatch_queue_t_address; } lldb::QueueKind SystemRuntimeMacOSX::GetQueueKind(addr_t dispatch_queue_addr) { if (dispatch_queue_addr == LLDB_INVALID_ADDRESS || dispatch_queue_addr == 0) return eQueueKindUnknown; QueueKind kind = eQueueKindUnknown; ReadLibdispatchOffsets(); if (m_libdispatch_offsets.IsValid() && m_libdispatch_offsets.dqo_version >= 4) { Status error; uint64_t width = m_process->ReadUnsignedIntegerFromMemory( dispatch_queue_addr + m_libdispatch_offsets.dqo_width, m_libdispatch_offsets.dqo_width_size, 0, error); if (error.Success()) { if (width == 1) { kind = eQueueKindSerial; } if (width > 1) { kind = eQueueKindConcurrent; } } } return kind; } void SystemRuntimeMacOSX::AddThreadExtendedInfoPacketHints( lldb_private::StructuredData::ObjectSP dict_sp) { StructuredData::Dictionary *dict = dict_sp->GetAsDictionary(); if (dict) { ReadLibpthreadOffsets(); if (m_libpthread_offsets.IsValid()) { dict->AddIntegerItem("plo_pthread_tsd_base_offset", m_libpthread_offsets.plo_pthread_tsd_base_offset); dict->AddIntegerItem( "plo_pthread_tsd_base_address_offset", m_libpthread_offsets.plo_pthread_tsd_base_address_offset); dict->AddIntegerItem("plo_pthread_tsd_entry_size", m_libpthread_offsets.plo_pthread_tsd_entry_size); } ReadLibdispatchTSDIndexes(); if (m_libdispatch_tsd_indexes.IsValid()) { dict->AddIntegerItem("dti_queue_index", m_libdispatch_tsd_indexes.dti_queue_index); dict->AddIntegerItem("dti_voucher_index", m_libdispatch_tsd_indexes.dti_voucher_index); dict->AddIntegerItem("dti_qos_class_index", m_libdispatch_tsd_indexes.dti_qos_class_index); } } } bool SystemRuntimeMacOSX::SafeToCallFunctionsOnThisThread(ThreadSP thread_sp) { if (thread_sp && thread_sp->GetStackFrameCount() > 0 && thread_sp->GetFrameWithConcreteFrameIndex(0)) { const SymbolContext sym_ctx( thread_sp->GetFrameWithConcreteFrameIndex(0)->GetSymbolContext( eSymbolContextSymbol)); static ConstString g_select_symbol("__select"); if (sym_ctx.GetFunctionName() == g_select_symbol) { return false; } } return true; } lldb::queue_id_t SystemRuntimeMacOSX::GetQueueIDFromThreadQAddress(lldb::addr_t dispatch_qaddr) { queue_id_t queue_id = LLDB_INVALID_QUEUE_ID; if (dispatch_qaddr == LLDB_INVALID_ADDRESS || dispatch_qaddr == 0) return queue_id; ReadLibdispatchOffsets(); if (m_libdispatch_offsets.IsValid()) { // dispatch_qaddr is from a thread_info(THREAD_IDENTIFIER_INFO) call for a // thread - deref it to get the address of the dispatch_queue_t structure // for this thread's queue. Status error; uint64_t dispatch_queue_addr = m_process->ReadPointerFromMemory(dispatch_qaddr, error); if (error.Success()) { addr_t serialnum_address = dispatch_queue_addr + m_libdispatch_offsets.dqo_serialnum; queue_id_t serialnum = m_process->ReadUnsignedIntegerFromMemory( serialnum_address, m_libdispatch_offsets.dqo_serialnum_size, LLDB_INVALID_QUEUE_ID, error); if (error.Success()) { queue_id = serialnum; } } } return queue_id; } void SystemRuntimeMacOSX::ReadLibdispatchOffsetsAddress() { if (m_dispatch_queue_offsets_addr != LLDB_INVALID_ADDRESS) return; static ConstString g_dispatch_queue_offsets_symbol_name( "dispatch_queue_offsets"); const Symbol *dispatch_queue_offsets_symbol = nullptr; // libdispatch symbols were in libSystem.B.dylib up through Mac OS X 10.6 // ("Snow Leopard") ModuleSpec libSystem_module_spec(FileSpec("libSystem.B.dylib")); ModuleSP module_sp(m_process->GetTarget().GetImages().FindFirstModule( libSystem_module_spec)); if (module_sp) dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType( g_dispatch_queue_offsets_symbol_name, eSymbolTypeData); // libdispatch symbols are in their own dylib as of Mac OS X 10.7 ("Lion") // and later if (dispatch_queue_offsets_symbol == nullptr) { ModuleSpec libdispatch_module_spec(FileSpec("libdispatch.dylib")); module_sp = m_process->GetTarget().GetImages().FindFirstModule( libdispatch_module_spec); if (module_sp) dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType( g_dispatch_queue_offsets_symbol_name, eSymbolTypeData); } if (dispatch_queue_offsets_symbol) m_dispatch_queue_offsets_addr = dispatch_queue_offsets_symbol->GetLoadAddress(&m_process->GetTarget()); } void SystemRuntimeMacOSX::ReadLibdispatchOffsets() { if (m_libdispatch_offsets.IsValid()) return; ReadLibdispatchOffsetsAddress(); uint8_t memory_buffer[sizeof(struct LibdispatchOffsets)]; DataExtractor data(memory_buffer, sizeof(memory_buffer), m_process->GetByteOrder(), m_process->GetAddressByteSize()); Status error; if (m_process->ReadMemory(m_dispatch_queue_offsets_addr, memory_buffer, sizeof(memory_buffer), error) == sizeof(memory_buffer)) { lldb::offset_t data_offset = 0; // The struct LibdispatchOffsets is a series of uint16_t's - extract them // all in one big go. data.GetU16(&data_offset, &m_libdispatch_offsets.dqo_version, sizeof(struct LibdispatchOffsets) / sizeof(uint16_t)); } } void SystemRuntimeMacOSX::ReadLibpthreadOffsetsAddress() { if (m_libpthread_layout_offsets_addr != LLDB_INVALID_ADDRESS) return; static ConstString g_libpthread_layout_offsets_symbol_name( "pthread_layout_offsets"); const Symbol *libpthread_layout_offsets_symbol = nullptr; ModuleSpec libpthread_module_spec(FileSpec("libsystem_pthread.dylib")); ModuleSP module_sp(m_process->GetTarget().GetImages().FindFirstModule( libpthread_module_spec)); if (module_sp) { libpthread_layout_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType( g_libpthread_layout_offsets_symbol_name, eSymbolTypeData); if (libpthread_layout_offsets_symbol) { m_libpthread_layout_offsets_addr = libpthread_layout_offsets_symbol->GetLoadAddress( &m_process->GetTarget()); } } } void SystemRuntimeMacOSX::ReadLibpthreadOffsets() { if (m_libpthread_offsets.IsValid()) return; ReadLibpthreadOffsetsAddress(); if (m_libpthread_layout_offsets_addr != LLDB_INVALID_ADDRESS) { uint8_t memory_buffer[sizeof(struct LibpthreadOffsets)]; DataExtractor data(memory_buffer, sizeof(memory_buffer), m_process->GetByteOrder(), m_process->GetAddressByteSize()); Status error; if (m_process->ReadMemory(m_libpthread_layout_offsets_addr, memory_buffer, sizeof(memory_buffer), error) == sizeof(memory_buffer)) { lldb::offset_t data_offset = 0; // The struct LibpthreadOffsets is a series of uint16_t's - extract them // all in one big go. data.GetU16(&data_offset, &m_libpthread_offsets.plo_version, sizeof(struct LibpthreadOffsets) / sizeof(uint16_t)); } } } void SystemRuntimeMacOSX::ReadLibdispatchTSDIndexesAddress() { if (m_dispatch_tsd_indexes_addr != LLDB_INVALID_ADDRESS) return; static ConstString g_libdispatch_tsd_indexes_symbol_name( "dispatch_tsd_indexes"); const Symbol *libdispatch_tsd_indexes_symbol = nullptr; ModuleSpec libpthread_module_spec(FileSpec("libdispatch.dylib")); ModuleSP module_sp(m_process->GetTarget().GetImages().FindFirstModule( libpthread_module_spec)); if (module_sp) { libdispatch_tsd_indexes_symbol = module_sp->FindFirstSymbolWithNameAndType( g_libdispatch_tsd_indexes_symbol_name, eSymbolTypeData); if (libdispatch_tsd_indexes_symbol) { m_dispatch_tsd_indexes_addr = libdispatch_tsd_indexes_symbol->GetLoadAddress( &m_process->GetTarget()); } } } void SystemRuntimeMacOSX::ReadLibdispatchTSDIndexes() { if (m_libdispatch_tsd_indexes.IsValid()) return; ReadLibdispatchTSDIndexesAddress(); if (m_dispatch_tsd_indexes_addr != LLDB_INVALID_ADDRESS) { // We don't need to check the version number right now, it will be at least 2, // but keep this code around to fetch just the version # for the future where // we need to fetch alternate versions of the struct. #if 0 uint16_t dti_version = 2; Address dti_struct_addr; if (m_process->GetTarget().ResolveLoadAddress (m_dispatch_tsd_indexes_addr, dti_struct_addr)) { Status error; uint16_t version = m_process->GetTarget().ReadUnsignedIntegerFromMemory (dti_struct_addr, false, 2, UINT16_MAX, error); if (error.Success() && dti_version != UINT16_MAX) { dti_version = version; } } #endif TypeSystemClang *ast_ctx = ScratchTypeSystemClang::GetForTarget(m_process->GetTarget()); if (m_dispatch_tsd_indexes_addr != LLDB_INVALID_ADDRESS) { CompilerType uint16 = ast_ctx->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 16); CompilerType dispatch_tsd_indexes_s = ast_ctx->CreateRecordType( nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "__lldb_dispatch_tsd_indexes_s", clang::TTK_Struct, lldb::eLanguageTypeC); TypeSystemClang::StartTagDeclarationDefinition(dispatch_tsd_indexes_s); TypeSystemClang::AddFieldToRecordType(dispatch_tsd_indexes_s, "dti_version", uint16, lldb::eAccessPublic, 0); TypeSystemClang::AddFieldToRecordType(dispatch_tsd_indexes_s, "dti_queue_index", uint16, lldb::eAccessPublic, 0); TypeSystemClang::AddFieldToRecordType(dispatch_tsd_indexes_s, "dti_voucher_index", uint16, lldb::eAccessPublic, 0); TypeSystemClang::AddFieldToRecordType(dispatch_tsd_indexes_s, "dti_qos_class_index", uint16, lldb::eAccessPublic, 0); TypeSystemClang::CompleteTagDeclarationDefinition(dispatch_tsd_indexes_s); ProcessStructReader struct_reader(m_process, m_dispatch_tsd_indexes_addr, dispatch_tsd_indexes_s); m_libdispatch_tsd_indexes.dti_version = struct_reader.GetField(ConstString("dti_version")); m_libdispatch_tsd_indexes.dti_queue_index = struct_reader.GetField(ConstString("dti_queue_index")); m_libdispatch_tsd_indexes.dti_voucher_index = struct_reader.GetField(ConstString("dti_voucher_index")); m_libdispatch_tsd_indexes.dti_qos_class_index = struct_reader.GetField(ConstString("dti_qos_class_index")); } } } ThreadSP SystemRuntimeMacOSX::GetExtendedBacktraceThread(ThreadSP real_thread, ConstString type) { ThreadSP originating_thread_sp; if (BacktraceRecordingHeadersInitialized() && type == "libdispatch") { Status error; // real_thread is either an actual, live thread (in which case we need to // call into libBacktraceRecording to find its originator) or it is an // extended backtrace itself, in which case we get the token from it and // call into libBacktraceRecording to find the originator of that token. if (real_thread->GetExtendedBacktraceToken() != LLDB_INVALID_ADDRESS) { originating_thread_sp = GetExtendedBacktraceFromItemRef( real_thread->GetExtendedBacktraceToken()); } else { ThreadSP cur_thread_sp( m_process->GetThreadList().GetExpressionExecutionThread()); AppleGetThreadItemInfoHandler::GetThreadItemInfoReturnInfo ret = m_get_thread_item_info_handler.GetThreadItemInfo( *cur_thread_sp.get(), real_thread->GetID(), m_page_to_free, m_page_to_free_size, error); m_page_to_free = LLDB_INVALID_ADDRESS; m_page_to_free_size = 0; if (ret.item_buffer_ptr != 0 && ret.item_buffer_ptr != LLDB_INVALID_ADDRESS && ret.item_buffer_size > 0) { DataBufferHeap data(ret.item_buffer_size, 0); if (m_process->ReadMemory(ret.item_buffer_ptr, data.GetBytes(), ret.item_buffer_size, error) && error.Success()) { DataExtractor extractor(data.GetBytes(), data.GetByteSize(), m_process->GetByteOrder(), m_process->GetAddressByteSize()); ItemInfo item = ExtractItemInfoFromBuffer(extractor); originating_thread_sp = std::make_shared( *m_process, item.enqueuing_thread_id, item.enqueuing_callstack); originating_thread_sp->SetExtendedBacktraceToken( item.item_that_enqueued_this); originating_thread_sp->SetQueueName( item.enqueuing_queue_label.c_str()); originating_thread_sp->SetQueueID(item.enqueuing_queue_serialnum); // originating_thread_sp->SetThreadName // (item.enqueuing_thread_label.c_str()); } m_page_to_free = ret.item_buffer_ptr; m_page_to_free_size = ret.item_buffer_size; } } } return originating_thread_sp; } ThreadSP SystemRuntimeMacOSX::GetExtendedBacktraceFromItemRef(lldb::addr_t item_ref) { ThreadSP return_thread_sp; AppleGetItemInfoHandler::GetItemInfoReturnInfo ret; ThreadSP cur_thread_sp( m_process->GetThreadList().GetExpressionExecutionThread()); Status error; ret = m_get_item_info_handler.GetItemInfo(*cur_thread_sp.get(), item_ref, m_page_to_free, m_page_to_free_size, error); m_page_to_free = LLDB_INVALID_ADDRESS; m_page_to_free_size = 0; if (ret.item_buffer_ptr != 0 && ret.item_buffer_ptr != LLDB_INVALID_ADDRESS && ret.item_buffer_size > 0) { DataBufferHeap data(ret.item_buffer_size, 0); if (m_process->ReadMemory(ret.item_buffer_ptr, data.GetBytes(), ret.item_buffer_size, error) && error.Success()) { DataExtractor extractor(data.GetBytes(), data.GetByteSize(), m_process->GetByteOrder(), m_process->GetAddressByteSize()); ItemInfo item = ExtractItemInfoFromBuffer(extractor); return_thread_sp = std::make_shared( *m_process, item.enqueuing_thread_id, item.enqueuing_callstack); return_thread_sp->SetExtendedBacktraceToken(item.item_that_enqueued_this); return_thread_sp->SetQueueName(item.enqueuing_queue_label.c_str()); return_thread_sp->SetQueueID(item.enqueuing_queue_serialnum); // return_thread_sp->SetThreadName // (item.enqueuing_thread_label.c_str()); m_page_to_free = ret.item_buffer_ptr; m_page_to_free_size = ret.item_buffer_size; } } return return_thread_sp; } ThreadSP SystemRuntimeMacOSX::GetExtendedBacktraceForQueueItem(QueueItemSP queue_item_sp, ConstString type) { ThreadSP extended_thread_sp; if (type != "libdispatch") return extended_thread_sp; extended_thread_sp = std::make_shared( *m_process, queue_item_sp->GetEnqueueingThreadID(), queue_item_sp->GetEnqueueingBacktrace()); extended_thread_sp->SetExtendedBacktraceToken( queue_item_sp->GetItemThatEnqueuedThis()); extended_thread_sp->SetQueueName(queue_item_sp->GetQueueLabel().c_str()); extended_thread_sp->SetQueueID(queue_item_sp->GetEnqueueingQueueID()); // extended_thread_sp->SetThreadName // (queue_item_sp->GetThreadLabel().c_str()); return extended_thread_sp; } /* Returns true if we were able to get the version / offset information * out of libBacktraceRecording. false means we were unable to retrieve * this; the queue_info_version field will be 0. */ bool SystemRuntimeMacOSX::BacktraceRecordingHeadersInitialized() { if (m_lib_backtrace_recording_info.queue_info_version != 0) return true; addr_t queue_info_version_address = LLDB_INVALID_ADDRESS; addr_t queue_info_data_offset_address = LLDB_INVALID_ADDRESS; addr_t item_info_version_address = LLDB_INVALID_ADDRESS; addr_t item_info_data_offset_address = LLDB_INVALID_ADDRESS; Target &target = m_process->GetTarget(); static ConstString introspection_dispatch_queue_info_version( "__introspection_dispatch_queue_info_version"); SymbolContextList sc_list; m_process->GetTarget().GetImages().FindSymbolsWithNameAndType( introspection_dispatch_queue_info_version, eSymbolTypeData, sc_list); if (!sc_list.IsEmpty()) { SymbolContext sc; sc_list.GetContextAtIndex(0, sc); AddressRange addr_range; sc.GetAddressRange(eSymbolContextSymbol, 0, false, addr_range); queue_info_version_address = addr_range.GetBaseAddress().GetLoadAddress(&target); } sc_list.Clear(); static ConstString introspection_dispatch_queue_info_data_offset( "__introspection_dispatch_queue_info_data_offset"); m_process->GetTarget().GetImages().FindSymbolsWithNameAndType( introspection_dispatch_queue_info_data_offset, eSymbolTypeData, sc_list); if (!sc_list.IsEmpty()) { SymbolContext sc; sc_list.GetContextAtIndex(0, sc); AddressRange addr_range; sc.GetAddressRange(eSymbolContextSymbol, 0, false, addr_range); queue_info_data_offset_address = addr_range.GetBaseAddress().GetLoadAddress(&target); } sc_list.Clear(); static ConstString introspection_dispatch_item_info_version( "__introspection_dispatch_item_info_version"); m_process->GetTarget().GetImages().FindSymbolsWithNameAndType( introspection_dispatch_item_info_version, eSymbolTypeData, sc_list); if (!sc_list.IsEmpty()) { SymbolContext sc; sc_list.GetContextAtIndex(0, sc); AddressRange addr_range; sc.GetAddressRange(eSymbolContextSymbol, 0, false, addr_range); item_info_version_address = addr_range.GetBaseAddress().GetLoadAddress(&target); } sc_list.Clear(); static ConstString introspection_dispatch_item_info_data_offset( "__introspection_dispatch_item_info_data_offset"); m_process->GetTarget().GetImages().FindSymbolsWithNameAndType( introspection_dispatch_item_info_data_offset, eSymbolTypeData, sc_list); if (!sc_list.IsEmpty()) { SymbolContext sc; sc_list.GetContextAtIndex(0, sc); AddressRange addr_range; sc.GetAddressRange(eSymbolContextSymbol, 0, false, addr_range); item_info_data_offset_address = addr_range.GetBaseAddress().GetLoadAddress(&target); } if (queue_info_version_address != LLDB_INVALID_ADDRESS && queue_info_data_offset_address != LLDB_INVALID_ADDRESS && item_info_version_address != LLDB_INVALID_ADDRESS && item_info_data_offset_address != LLDB_INVALID_ADDRESS) { Status error; m_lib_backtrace_recording_info.queue_info_version = m_process->ReadUnsignedIntegerFromMemory(queue_info_version_address, 2, 0, error); if (error.Success()) { m_lib_backtrace_recording_info.queue_info_data_offset = m_process->ReadUnsignedIntegerFromMemory( queue_info_data_offset_address, 2, 0, error); if (error.Success()) { m_lib_backtrace_recording_info.item_info_version = m_process->ReadUnsignedIntegerFromMemory(item_info_version_address, 2, 0, error); if (error.Success()) { m_lib_backtrace_recording_info.item_info_data_offset = m_process->ReadUnsignedIntegerFromMemory( item_info_data_offset_address, 2, 0, error); if (!error.Success()) { m_lib_backtrace_recording_info.queue_info_version = 0; } } else { m_lib_backtrace_recording_info.queue_info_version = 0; } } else { m_lib_backtrace_recording_info.queue_info_version = 0; } } } return m_lib_backtrace_recording_info.queue_info_version != 0; } const std::vector & SystemRuntimeMacOSX::GetExtendedBacktraceTypes() { if (m_types.size() == 0) { m_types.push_back(ConstString("libdispatch")); // We could have pthread as another type in the future if we have a way of // gathering that information & it's useful to distinguish between them. } return m_types; } void SystemRuntimeMacOSX::PopulateQueueList( lldb_private::QueueList &queue_list) { if (BacktraceRecordingHeadersInitialized()) { AppleGetQueuesHandler::GetQueuesReturnInfo queue_info_pointer; ThreadSP cur_thread_sp( m_process->GetThreadList().GetExpressionExecutionThread()); if (cur_thread_sp) { Status error; queue_info_pointer = m_get_queues_handler.GetCurrentQueues( *cur_thread_sp.get(), m_page_to_free, m_page_to_free_size, error); m_page_to_free = LLDB_INVALID_ADDRESS; m_page_to_free_size = 0; if (error.Success()) { if (queue_info_pointer.count > 0 && queue_info_pointer.queues_buffer_size > 0 && queue_info_pointer.queues_buffer_ptr != 0 && queue_info_pointer.queues_buffer_ptr != LLDB_INVALID_ADDRESS) { PopulateQueuesUsingLibBTR(queue_info_pointer.queues_buffer_ptr, queue_info_pointer.queues_buffer_size, queue_info_pointer.count, queue_list); } } } } // We either didn't have libBacktraceRecording (and need to create the queues // list based on threads) or we did get the queues list from // libBacktraceRecording but some special queues may not be included in its // information. This is needed because libBacktraceRecording will only list // queues with pending or running items by default - but the magic com.apple // .main-thread queue on thread 1 is always around. for (ThreadSP thread_sp : m_process->Threads()) { if (thread_sp->GetAssociatedWithLibdispatchQueue() != eLazyBoolNo) { if (thread_sp->GetQueueID() != LLDB_INVALID_QUEUE_ID) { if (queue_list.FindQueueByID(thread_sp->GetQueueID()).get() == nullptr) { QueueSP queue_sp(new Queue(m_process->shared_from_this(), thread_sp->GetQueueID(), thread_sp->GetQueueName())); if (thread_sp->ThreadHasQueueInformation()) { queue_sp->SetKind(thread_sp->GetQueueKind()); queue_sp->SetLibdispatchQueueAddress( thread_sp->GetQueueLibdispatchQueueAddress()); queue_list.AddQueue(queue_sp); } else { queue_sp->SetKind( GetQueueKind(thread_sp->GetQueueLibdispatchQueueAddress())); queue_sp->SetLibdispatchQueueAddress( thread_sp->GetQueueLibdispatchQueueAddress()); queue_list.AddQueue(queue_sp); } } } } } } // Returns either an array of introspection_dispatch_item_info_ref's for the // pending items on a queue or an array introspection_dispatch_item_info_ref's // and code addresses for the pending items on a queue. The information about // each of these pending items then needs to be fetched individually by passing // the ref to libBacktraceRecording. SystemRuntimeMacOSX::PendingItemsForQueue SystemRuntimeMacOSX::GetPendingItemRefsForQueue(lldb::addr_t queue) { PendingItemsForQueue pending_item_refs; AppleGetPendingItemsHandler::GetPendingItemsReturnInfo pending_items_pointer; ThreadSP cur_thread_sp( m_process->GetThreadList().GetExpressionExecutionThread()); if (cur_thread_sp) { Status error; pending_items_pointer = m_get_pending_items_handler.GetPendingItems( *cur_thread_sp.get(), queue, m_page_to_free, m_page_to_free_size, error); m_page_to_free = LLDB_INVALID_ADDRESS; m_page_to_free_size = 0; if (error.Success()) { if (pending_items_pointer.count > 0 && pending_items_pointer.items_buffer_size > 0 && pending_items_pointer.items_buffer_ptr != 0 && pending_items_pointer.items_buffer_ptr != LLDB_INVALID_ADDRESS) { DataBufferHeap data(pending_items_pointer.items_buffer_size, 0); if (m_process->ReadMemory( pending_items_pointer.items_buffer_ptr, data.GetBytes(), pending_items_pointer.items_buffer_size, error)) { DataExtractor extractor(data.GetBytes(), data.GetByteSize(), m_process->GetByteOrder(), m_process->GetAddressByteSize()); // We either have an array of // void* item_ref // (old style) or we have a structure returned which looks like // // struct introspection_dispatch_pending_item_info_s { // void *item_ref; // void *function_or_block; // }; // // struct introspection_dispatch_pending_items_array_s { // uint32_t version; // uint32_t size_of_item_info; // introspection_dispatch_pending_item_info_s items[]; // } offset_t offset = 0; int i = 0; uint32_t version = extractor.GetU32(&offset); if (version == 1) { pending_item_refs.new_style = true; uint32_t item_size = extractor.GetU32(&offset); uint32_t start_of_array_offset = offset; while (offset < pending_items_pointer.items_buffer_size && static_cast(i) < pending_items_pointer.count) { offset = start_of_array_offset + (i * item_size); ItemRefAndCodeAddress item; item.item_ref = extractor.GetAddress(&offset); item.code_address = extractor.GetAddress(&offset); pending_item_refs.item_refs_and_code_addresses.push_back(item); i++; } } else { offset = 0; pending_item_refs.new_style = false; while (offset < pending_items_pointer.items_buffer_size && static_cast(i) < pending_items_pointer.count) { ItemRefAndCodeAddress item; item.item_ref = extractor.GetAddress(&offset); item.code_address = LLDB_INVALID_ADDRESS; pending_item_refs.item_refs_and_code_addresses.push_back(item); i++; } } } m_page_to_free = pending_items_pointer.items_buffer_ptr; m_page_to_free_size = pending_items_pointer.items_buffer_size; } } } return pending_item_refs; } void SystemRuntimeMacOSX::PopulatePendingItemsForQueue(Queue *queue) { if (BacktraceRecordingHeadersInitialized()) { PendingItemsForQueue pending_item_refs = GetPendingItemRefsForQueue(queue->GetLibdispatchQueueAddress()); for (ItemRefAndCodeAddress pending_item : pending_item_refs.item_refs_and_code_addresses) { Address addr; m_process->GetTarget().ResolveLoadAddress(pending_item.code_address, addr); QueueItemSP queue_item_sp(new QueueItem(queue->shared_from_this(), m_process->shared_from_this(), pending_item.item_ref, addr)); queue->PushPendingQueueItem(queue_item_sp); } } } void SystemRuntimeMacOSX::CompleteQueueItem(QueueItem *queue_item, addr_t item_ref) { AppleGetItemInfoHandler::GetItemInfoReturnInfo ret; ThreadSP cur_thread_sp( m_process->GetThreadList().GetExpressionExecutionThread()); Status error; ret = m_get_item_info_handler.GetItemInfo(*cur_thread_sp.get(), item_ref, m_page_to_free, m_page_to_free_size, error); m_page_to_free = LLDB_INVALID_ADDRESS; m_page_to_free_size = 0; if (ret.item_buffer_ptr != 0 && ret.item_buffer_ptr != LLDB_INVALID_ADDRESS && ret.item_buffer_size > 0) { DataBufferHeap data(ret.item_buffer_size, 0); if (m_process->ReadMemory(ret.item_buffer_ptr, data.GetBytes(), ret.item_buffer_size, error) && error.Success()) { DataExtractor extractor(data.GetBytes(), data.GetByteSize(), m_process->GetByteOrder(), m_process->GetAddressByteSize()); ItemInfo item = ExtractItemInfoFromBuffer(extractor); queue_item->SetItemThatEnqueuedThis(item.item_that_enqueued_this); queue_item->SetEnqueueingThreadID(item.enqueuing_thread_id); queue_item->SetEnqueueingQueueID(item.enqueuing_queue_serialnum); queue_item->SetStopID(item.stop_id); queue_item->SetEnqueueingBacktrace(item.enqueuing_callstack); queue_item->SetThreadLabel(item.enqueuing_thread_label); queue_item->SetQueueLabel(item.enqueuing_queue_label); queue_item->SetTargetQueueLabel(item.target_queue_label); } m_page_to_free = ret.item_buffer_ptr; m_page_to_free_size = ret.item_buffer_size; } } void SystemRuntimeMacOSX::PopulateQueuesUsingLibBTR( lldb::addr_t queues_buffer, uint64_t queues_buffer_size, uint64_t count, lldb_private::QueueList &queue_list) { Status error; DataBufferHeap data(queues_buffer_size, 0); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYSTEM_RUNTIME)); if (m_process->ReadMemory(queues_buffer, data.GetBytes(), queues_buffer_size, error) == queues_buffer_size && error.Success()) { // We've read the information out of inferior memory; free it on the next // call we make m_page_to_free = queues_buffer; m_page_to_free_size = queues_buffer_size; DataExtractor extractor(data.GetBytes(), data.GetByteSize(), m_process->GetByteOrder(), m_process->GetAddressByteSize()); offset_t offset = 0; uint64_t queues_read = 0; // The information about the queues is stored in this format (v1): typedef // struct introspection_dispatch_queue_info_s { // uint32_t offset_to_next; // dispatch_queue_t queue; // uint64_t serialnum; // queue's serialnum in the process, as // provided by libdispatch // uint32_t running_work_items_count; // uint32_t pending_work_items_count; // // char data[]; // Starting here, we have variable-length data: // // char queue_label[]; // } introspection_dispatch_queue_info_s; while (queues_read < count && offset < queues_buffer_size) { offset_t start_of_this_item = offset; uint32_t offset_to_next = extractor.GetU32(&offset); offset += 4; // Skip over the 4 bytes of reserved space addr_t queue = extractor.GetAddress(&offset); uint64_t serialnum = extractor.GetU64(&offset); uint32_t running_work_items_count = extractor.GetU32(&offset); uint32_t pending_work_items_count = extractor.GetU32(&offset); // Read the first field of the variable length data offset = start_of_this_item + m_lib_backtrace_recording_info.queue_info_data_offset; const char *queue_label = extractor.GetCStr(&offset); if (queue_label == nullptr) queue_label = ""; offset_t start_of_next_item = start_of_this_item + offset_to_next; offset = start_of_next_item; LLDB_LOGF(log, "SystemRuntimeMacOSX::PopulateQueuesUsingLibBTR added " "queue with dispatch_queue_t 0x%" PRIx64 ", serial number 0x%" PRIx64 ", running items %d, pending items %d, name '%s'", queue, serialnum, running_work_items_count, pending_work_items_count, queue_label); QueueSP queue_sp( new Queue(m_process->shared_from_this(), serialnum, queue_label)); queue_sp->SetNumRunningWorkItems(running_work_items_count); queue_sp->SetNumPendingWorkItems(pending_work_items_count); queue_sp->SetLibdispatchQueueAddress(queue); queue_sp->SetKind(GetQueueKind(queue)); queue_list.AddQueue(queue_sp); queues_read++; } } } SystemRuntimeMacOSX::ItemInfo SystemRuntimeMacOSX::ExtractItemInfoFromBuffer( lldb_private::DataExtractor &extractor) { ItemInfo item; offset_t offset = 0; item.item_that_enqueued_this = extractor.GetAddress(&offset); item.function_or_block = extractor.GetAddress(&offset); item.enqueuing_thread_id = extractor.GetU64(&offset); item.enqueuing_queue_serialnum = extractor.GetU64(&offset); item.target_queue_serialnum = extractor.GetU64(&offset); item.enqueuing_callstack_frame_count = extractor.GetU32(&offset); item.stop_id = extractor.GetU32(&offset); offset = m_lib_backtrace_recording_info.item_info_data_offset; for (uint32_t i = 0; i < item.enqueuing_callstack_frame_count; i++) { item.enqueuing_callstack.push_back(extractor.GetAddress(&offset)); } item.enqueuing_thread_label = extractor.GetCStr(&offset); item.enqueuing_queue_label = extractor.GetCStr(&offset); item.target_queue_label = extractor.GetCStr(&offset); return item; } void SystemRuntimeMacOSX::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance); } void SystemRuntimeMacOSX::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } lldb_private::ConstString SystemRuntimeMacOSX::GetPluginNameStatic() { static ConstString g_name("systemruntime-macosx"); return g_name; } const char *SystemRuntimeMacOSX::GetPluginDescriptionStatic() { return "System runtime plugin for Mac OS X native libraries."; } // PluginInterface protocol lldb_private::ConstString SystemRuntimeMacOSX::GetPluginName() { return GetPluginNameStatic(); } uint32_t SystemRuntimeMacOSX::GetPluginVersion() { return 1; }