diff --git a/lldb/docs/lldb-gdb-remote.txt b/lldb/docs/lldb-gdb-remote.txt --- a/lldb/docs/lldb-gdb-remote.txt +++ b/lldb/docs/lldb-gdb-remote.txt @@ -2153,3 +2153,21 @@ The data in this packet is a single a character, which should be '0' if the inferior process should be killed, or '1' if the server should remove all breakpoints and detach from the inferior. + +//---------------------------------------------------------------------- +// "jGetDyldProcessState" +// +// BRIEF +// This packet fetches the process launch state, as reported by libdyld on +// Darwin systems, most importantly to indicate when the system libraries +// have initialized sufficiently to safely call utility functions. +// +// +// LLDB SENDS: jGetDyldProcessState +// STUB REPLIES: {"process_state_value":48,"process_state string":"dyld_process_state_libSystem_initialized"} +// +// PRIORITY TO IMPLEMENT +// Low. This packet is needed to prevent lldb's utility functions for +// scanning the Objective-C class list from running very early in +// process startup. +//---------------------------------------------------------------------- diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -1321,6 +1321,15 @@ return StructuredData::ObjectSP(); } + // Get information about the launch state of the process, if possible. + // + // On Darwin systems, libdyld can report on process state, most importantly + // the startup stages where the system library is not yet initialized. + virtual lldb_private::StructuredData::ObjectSP + GetDynamicLoaderProcessState() { + return {}; + } + /// Print a user-visible warning about a module being built with /// optimization /// diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.h b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.h --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.h +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.h @@ -79,6 +79,8 @@ void DoClear() override; + bool IsFullyInitialized() override; + static bool NotifyBreakpointHit(void *baton, lldb_private::StoppointCallbackContext *context, @@ -106,6 +108,7 @@ // exec's when talking to // debugservers that don't support // the "reason:exec" annotation. + bool m_libsystem_fully_initalized; }; #endif // LLDB_SOURCE_PLUGINS_DYNAMICLOADER_MACOSX_DYLD_DYNAMICLOADERMACOS_H diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp @@ -79,7 +79,8 @@ : DynamicLoaderDarwin(process), m_image_infos_stop_id(UINT32_MAX), m_break_id(LLDB_INVALID_BREAK_ID), m_dyld_handover_break_id(LLDB_INVALID_BREAK_ID), m_mutex(), - m_maybe_image_infos_address(LLDB_INVALID_ADDRESS) {} + m_maybe_image_infos_address(LLDB_INVALID_ADDRESS), + m_libsystem_fully_initalized(false) {} // Destructor DynamicLoaderMacOS::~DynamicLoaderMacOS() { @@ -129,6 +130,7 @@ if (did_exec) { m_libpthread_module_wp.reset(); m_pthread_getspecific_addr.Clear(); + m_libsystem_fully_initalized = false; } return did_exec; } @@ -144,6 +146,33 @@ m_break_id = LLDB_INVALID_BREAK_ID; m_dyld_handover_break_id = LLDB_INVALID_BREAK_ID; + m_libsystem_fully_initalized = false; +} + +bool DynamicLoaderMacOS::IsFullyInitialized() { + if (m_libsystem_fully_initalized) + return true; + + StructuredData::ObjectSP process_state_sp( + m_process->GetDynamicLoaderProcessState()); + if (!process_state_sp) + return true; + if (process_state_sp->GetAsDictionary()->HasKey("error")) + return true; + if (!process_state_sp->GetAsDictionary()->HasKey("process_state string")) + return true; + std::string proc_state = process_state_sp->GetAsDictionary() + ->GetValueForKey("process_state string") + ->GetAsString() + ->GetValue() + .str(); + if (proc_state == "dyld_process_state_not_started" || + proc_state == "dyld_process_state_dyld_initialized" || + proc_state == "dyld_process_state_terminated_before_inits") { + return false; + } + m_libsystem_fully_initalized = true; + return true; } // Check if we have found DYLD yet diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h @@ -286,18 +286,24 @@ struct DescriptorMapUpdateResult { bool m_update_ran; + bool m_retry_update; uint32_t m_num_found; - DescriptorMapUpdateResult(bool ran, uint32_t found) { + DescriptorMapUpdateResult(bool ran, bool retry, uint32_t found) { m_update_ran = ran; + + m_retry_update = retry; + m_num_found = found; } - static DescriptorMapUpdateResult Fail() { return {false, 0}; } + static DescriptorMapUpdateResult Fail() { return {false, false, 0}; } static DescriptorMapUpdateResult Success(uint32_t found) { - return {true, found}; + return {true, false, found}; } + + static DescriptorMapUpdateResult Retry() { return {false, true, 0}; } }; /// Abstraction to read the Objective-C class info. @@ -395,6 +401,7 @@ uint32_t num_class_infos); enum class SharedCacheWarningReason { + eExpressionUnableToRun, eExpressionExecutionFailure, eNotEnoughClassesRead }; diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp @@ -1875,6 +1875,9 @@ if (!thread_sp) return DescriptorMapUpdateResult::Fail(); + if (!thread_sp->SafeToCallFunctions()) + return DescriptorMapUpdateResult::Retry(); + thread_sp->CalculateExecutionContext(exe_ctx); TypeSystemClang *ast = ScratchTypeSystemClang::GetForTarget(process->GetTarget()); @@ -2042,7 +2045,7 @@ } } - return DescriptorMapUpdateResult(success, num_class_infos); + return DescriptorMapUpdateResult(success, false, num_class_infos); } uint32_t AppleObjCRuntimeV2::ParseClassInfoArray(const DataExtractor &data, @@ -2137,6 +2140,9 @@ if (!thread_sp) return DescriptorMapUpdateResult::Fail(); + if (!thread_sp->SafeToCallFunctions()) + return DescriptorMapUpdateResult::Retry(); + thread_sp->CalculateExecutionContext(exe_ctx); TypeSystemClang *ast = ScratchTypeSystemClang::GetForTarget(process->GetTarget()); @@ -2314,7 +2320,7 @@ // Deallocate the memory we allocated for the ClassInfo array process->DeallocateMemory(class_infos_addr); - return DescriptorMapUpdateResult(success, num_class_infos); + return DescriptorMapUpdateResult(success, false, num_class_infos); } lldb::addr_t AppleObjCRuntimeV2::GetSharedCacheReadOnlyAddress() { @@ -2414,18 +2420,23 @@ LLDB_LOGF(log, "attempted to read objc class data - results: " - "[dynamic_update]: ran: %s, count: %" PRIu32 - " [shared_cache_update]: ran: %s, count: %" PRIu32, + "[dynamic_update]: ran: %s, retry: %s, count: %" PRIu32 + " [shared_cache_update]: ran: %s, retry: %s, count: %" PRIu32, dynamic_update_result.m_update_ran ? "yes" : "no", + dynamic_update_result.m_retry_update ? "yes" : "no", dynamic_update_result.m_num_found, shared_cache_update_result.m_update_ran ? "yes" : "no", + shared_cache_update_result.m_retry_update ? "yes" : "no", shared_cache_update_result.m_num_found); // warn if: // - we could not run either expression // - we found fewer than num_classes_to_warn_at classes total - if ((!shared_cache_update_result.m_update_ran) || - (!dynamic_update_result.m_update_ran)) + if (dynamic_update_result.m_retry_update || + shared_cache_update_result.m_retry_update) + WarnIfNoClassesCached(SharedCacheWarningReason::eExpressionUnableToRun); + else if ((!shared_cache_update_result.m_update_ran) || + (!dynamic_update_result.m_update_ran)) WarnIfNoClassesCached( SharedCacheWarningReason::eExpressionExecutionFailure); else if (dynamic_update_result.m_num_found + @@ -2504,6 +2515,12 @@ "reduce the quality of type information available.\n", debugger.GetID(), &m_no_classes_cached_warning); break; + case SharedCacheWarningReason::eExpressionUnableToRun: + Debugger::ReportWarning( + "could not execute support code to read Objective-C class data because " + "it's not yet safe to do so, and will be retried later.\n", + debugger.GetID(), nullptr); + break; } } diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -429,6 +429,8 @@ bool GetSharedCacheInfoSupported(); + bool GetDynamicLoaderProcessStateSupported(); + bool GetMemoryTaggingSupported(); bool UsesNativeSignals(); @@ -553,6 +555,7 @@ LazyBool m_supports_jThreadExtendedInfo = eLazyBoolCalculate; LazyBool m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolCalculate; LazyBool m_supports_jGetSharedCacheInfo = eLazyBoolCalculate; + LazyBool m_supports_jGetDyldProcessState = eLazyBoolCalculate; LazyBool m_supports_QPassSignals = eLazyBoolCalculate; LazyBool m_supports_error_string_reply = eLazyBoolCalculate; LazyBool m_supports_multiprocess = eLazyBoolCalculate; diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -613,6 +613,19 @@ return m_supports_jGetSharedCacheInfo; } +bool GDBRemoteCommunicationClient::GetDynamicLoaderProcessStateSupported() { + if (m_supports_jGetDyldProcessState == eLazyBoolCalculate) { + StringExtractorGDBRemote response; + m_supports_jGetDyldProcessState = eLazyBoolNo; + if (SendPacketAndWaitForResponse("jGetDyldProcessState", response) == + PacketResult::Success) { + if (!response.IsUnsupportedResponse()) + m_supports_jGetDyldProcessState = eLazyBoolYes; + } + } + return m_supports_jGetDyldProcessState; +} + bool GDBRemoteCommunicationClient::GetMemoryTaggingSupported() { if (m_supports_memory_tagging == eLazyBoolCalculate) { GetRemoteQSupported(); diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -222,6 +222,8 @@ StructuredData::ObjectSP GetSharedCacheInfo() override; + StructuredData::ObjectSP GetDynamicLoaderProcessState() override; + std::string HarmonizeThreadIdsForProfileData( StringExtractorGDBRemote &inputStringExtractor); diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -3829,6 +3829,29 @@ return object_sp; } +StructuredData::ObjectSP ProcessGDBRemote::GetDynamicLoaderProcessState() { + StructuredData::ObjectSP object_sp; + StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); + + if (m_gdb_comm.GetDynamicLoaderProcessStateSupported()) { + StringExtractorGDBRemote response; + response.SetResponseValidatorToJSON(); + if (m_gdb_comm.SendPacketAndWaitForResponse("jGetDyldProcessState", + response) == + GDBRemoteCommunication::PacketResult::Success) { + StringExtractorGDBRemote::ResponseType response_type = + response.GetResponseType(); + if (response_type == StringExtractorGDBRemote::eResponse) { + if (!response.Empty()) { + object_sp = + StructuredData::ParseJSON(std::string(response.GetStringRef())); + } + } + } + } + return object_sp; +} + StructuredData::ObjectSP ProcessGDBRemote::GetSharedCacheInfo() { StructuredData::ObjectSP object_sp; StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -1663,6 +1663,10 @@ bool Thread::SafeToCallFunctions() { Process *process = GetProcess().get(); if (process) { + DynamicLoader *loader = GetProcess()->GetDynamicLoader(); + if (loader && loader->IsFullyInitialized() == false) + return false; + SystemRuntime *runtime = process->GetSystemRuntime(); if (runtime) { return runtime->SafeToCallFunctionsOnThisThread(shared_from_this()); diff --git a/lldb/test/API/macosx/early-process-launch/Makefile b/lldb/test/API/macosx/early-process-launch/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/early-process-launch/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules diff --git a/lldb/test/API/macosx/early-process-launch/TestEarlyProcessLaunch.py b/lldb/test/API/macosx/early-process-launch/TestEarlyProcessLaunch.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/early-process-launch/TestEarlyProcessLaunch.py @@ -0,0 +1,57 @@ +"""Test that we don't read objc class tables early in process startup.""" + + +import time +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestEarlyProcessLaunch(TestBase): + + NO_DEBUG_INFO_TESTCASE = True + + @skipUnlessDarwin + @add_test_categories(['pyapi']) + def test_early_process_launch(self): + """Test that we don't read objc class tables early in proc startup""" + self.build() + + ### + ### Hit a breakpoint on the first malloc() call, which + ### is before libSystem has finished initializing. At + ### this point, we should not read the objc class tables. + ### Then continue to main(), which is past libSystem + ### initializing. Try again, and they should be read. + ### + ### Use the types logging to detect the difference. + + target, process, _, bkpt = lldbutil.run_to_name_breakpoint( + self, 'malloc') + + target.DisableAllBreakpoints() + target.BreakpointCreateByName("main") + + logfile_early = os.path.join(self.getBuildDir(), "types-log-early.txt") + self.addTearDownHook(lambda: self.runCmd("log disable lldb types")) + self.runCmd("log enable -f %s lldb types" % logfile_early) + self.runCmd("p global = 15") + + err = process.Continue() + self.assertTrue(err.Success()) + + logfile_later = os.path.join(self.getBuildDir(), "types-log-later.txt") + self.runCmd("log enable -f %s lldb types" % logfile_later) + self.runCmd("p global = 25") + + self.assertTrue(os.path.exists(logfile_early)) + self.assertTrue(os.path.exists(logfile_later)) + early_text = open(logfile_early).read() + later_text = open(logfile_later).read() + + self.assertIn("ran: no, retry: yes", early_text) + self.assertNotIn("ran: no, retry: yes", later_text) + + self.assertNotIn("ran: yes, retry: no", early_text) + self.assertIn("ran: yes, retry: no", later_text) diff --git a/lldb/test/API/macosx/early-process-launch/main.c b/lldb/test/API/macosx/early-process-launch/main.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/early-process-launch/main.c @@ -0,0 +1,2 @@ +int global = 10; +int main() { return global; } diff --git a/lldb/tools/debugserver/source/DNB.h b/lldb/tools/debugserver/source/DNB.h --- a/lldb/tools/debugserver/source/DNB.h +++ b/lldb/tools/debugserver/source/DNB.h @@ -157,6 +157,7 @@ nub_size_t DNBProcessGetStopCount(nub_process_t pid) DNB_EXPORT; uint32_t DNBProcessGetCPUType(nub_process_t pid) DNB_EXPORT; size_t DNBGetAllInfos(std::vector &proc_infos); +JSONGenerator::ObjectSP DNBGetDyldProcessState(nub_process_t pid); // Process executable and arguments const char *DNBProcessGetExecutablePath(nub_process_t pid); diff --git a/lldb/tools/debugserver/source/DNB.cpp b/lldb/tools/debugserver/source/DNB.cpp --- a/lldb/tools/debugserver/source/DNB.cpp +++ b/lldb/tools/debugserver/source/DNB.cpp @@ -599,6 +599,14 @@ return proc_infos.size(); } +JSONGenerator::ObjectSP DNBGetDyldProcessState(nub_process_t pid) { + MachProcessSP procSP; + if (GetProcessSP(pid, procSP)) { + return procSP->GetDyldProcessState(); + } + return {}; +} + static size_t GetAllInfosMatchingName(const char *full_process_name, std::vector &matching_proc_infos) { diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.h b/lldb/tools/debugserver/source/MacOSX/MachProcess.h --- a/lldb/tools/debugserver/source/MacOSX/MachProcess.h +++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.h @@ -362,6 +362,8 @@ DNBProfileDataScanType GetProfileScanType() { return m_profile_scan_type; } + JSONGenerator::ObjectSP GetDyldProcessState(); + private: enum { eMachProcessFlagsNone = 0, @@ -468,6 +470,7 @@ void (*m_dyld_process_info_release)(void *info); void (*m_dyld_process_info_get_cache)(void *info, void *cacheInfo); uint32_t (*m_dyld_process_info_get_platform)(void *info); + void (*m_dyld_process_info_get_state)(void *info, void *stateInfo); }; #endif // LLDB_TOOLS_DEBUGSERVER_SOURCE_MACOSX_MACHPROCESS_H diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.mm b/lldb/tools/debugserver/source/MacOSX/MachProcess.mm --- a/lldb/tools/debugserver/source/MacOSX/MachProcess.mm +++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.mm @@ -508,7 +508,6 @@ #define _POSIX_SPAWN_DISABLE_ASLR 0x0100 #endif - MachProcess::MachProcess() : m_pid(0), m_cpu_type(0), m_child_stdin(-1), m_child_stdout(-1), m_child_stderr(-1), m_path(), m_args(), m_task(this), @@ -516,8 +515,8 @@ m_stdio_mutex(PTHREAD_MUTEX_RECURSIVE), m_stdout_data(), m_profile_enabled(false), m_profile_interval_usec(0), m_profile_thread(0), m_profile_data_mutex(PTHREAD_MUTEX_RECURSIVE), m_profile_data(), - m_profile_events(0, eMachProcessProfileCancel), - m_thread_actions(), m_exception_messages(), + m_profile_events(0, eMachProcessProfileCancel), m_thread_actions(), + m_exception_messages(), m_exception_messages_mutex(PTHREAD_MUTEX_RECURSIVE), m_thread_list(), m_activities(), m_state(eStateUnloaded), m_state_mutex(PTHREAD_MUTEX_RECURSIVE), m_events(0, kAllEventsMask), @@ -528,7 +527,8 @@ m_dyld_process_info_create(nullptr), m_dyld_process_info_for_each_image(nullptr), m_dyld_process_info_release(nullptr), - m_dyld_process_info_get_cache(nullptr) { + m_dyld_process_info_get_cache(nullptr), + m_dyld_process_info_get_state(nullptr) { m_dyld_process_info_create = (void *(*)(task_t task, uint64_t timestamp, kern_return_t * kernelError)) dlsym(RTLD_DEFAULT, "_dyld_process_info_create"); @@ -542,6 +542,8 @@ RTLD_DEFAULT, "_dyld_process_info_get_cache"); m_dyld_process_info_get_platform = (uint32_t (*)(void *info))dlsym( RTLD_DEFAULT, "_dyld_process_info_get_platform"); + m_dyld_process_info_get_state = (void (*)(void *info, void *stateInfo))dlsym( + RTLD_DEFAULT, "_dyld_process_info_get_state"); DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__); } @@ -2430,6 +2432,84 @@ return m_task.GetDYLDAllImageInfosAddress(err); } +/// From dyld SPI header dyld_process_info.h +struct dyld_process_state_info { + uint64_t timestamp; + uint32_t imageCount; + uint32_t initialImageCount; + // one of dyld_process_state_* values + uint8_t dyldState; +}; +enum { + dyld_process_state_not_started = 0x00, + dyld_process_state_dyld_initialized = 0x10, + dyld_process_state_terminated_before_inits = 0x20, + dyld_process_state_libSystem_initialized = 0x30, + dyld_process_state_running_initializers = 0x40, + dyld_process_state_program_running = 0x50, + dyld_process_state_dyld_terminated = 0x60 +}; + +JSONGenerator::ObjectSP MachProcess::GetDyldProcessState() { + JSONGenerator::DictionarySP reply_sp(new JSONGenerator::Dictionary()); + if (!m_dyld_process_info_get_state) { + reply_sp->AddStringItem("error", + "_dyld_process_info_get_state unavailable"); + return reply_sp; + } + if (!m_dyld_process_info_create) { + reply_sp->AddStringItem("error", "_dyld_process_info_create unavailable"); + return reply_sp; + } + + kern_return_t kern_ret; + dyld_process_info info = + m_dyld_process_info_create(m_task.TaskPort(), 0, &kern_ret); + if (!info || kern_ret != KERN_SUCCESS) { + reply_sp->AddStringItem( + "error", "Unable to create dyld_process_info for inferior task"); + return reply_sp; + } + + struct dyld_process_state_info state_info; + m_dyld_process_info_get_state(info, &state_info); + reply_sp->AddIntegerItem("process_state_value", state_info.dyldState); + switch (state_info.dyldState) { + case dyld_process_state_not_started: + reply_sp->AddStringItem("process_state string", + "dyld_process_state_not_started"); + break; + case dyld_process_state_dyld_initialized: + reply_sp->AddStringItem("process_state string", + "dyld_process_state_dyld_initialized"); + break; + case dyld_process_state_terminated_before_inits: + reply_sp->AddStringItem("process_state string", + "dyld_process_state_terminated_before_inits"); + break; + case dyld_process_state_libSystem_initialized: + reply_sp->AddStringItem("process_state string", + "dyld_process_state_libSystem_initialized"); + break; + case dyld_process_state_running_initializers: + reply_sp->AddStringItem("process_state string", + "dyld_process_state_running_initializers"); + break; + case dyld_process_state_program_running: + reply_sp->AddStringItem("process_state string", + "dyld_process_state_program_running"); + break; + case dyld_process_state_dyld_terminated: + reply_sp->AddStringItem("process_state string", + "dyld_process_state_dyld_terminated"); + break; + }; + + m_dyld_process_info_release(info); + + return reply_sp; +} + size_t MachProcess::GetAvailableSTDERR(char *buf, size_t buf_size) { return 0; } void *MachProcess::STDIOThread(void *arg) { diff --git a/lldb/tools/debugserver/source/RNBRemote.h b/lldb/tools/debugserver/source/RNBRemote.h --- a/lldb/tools/debugserver/source/RNBRemote.h +++ b/lldb/tools/debugserver/source/RNBRemote.h @@ -136,6 +136,7 @@ speed_test, // 'qSpeedTest:' set_detach_on_error, // 'QSetDetachOnError:' query_transfer, // 'qXfer:' + json_query_dyld_process_state, // 'jGetDyldProcessState' unknown_type }; @@ -246,6 +247,7 @@ rnb_err_t HandlePacket_qXfer(const char *p); rnb_err_t HandlePacket_stop_process(const char *p); rnb_err_t HandlePacket_QSetDetachOnError(const char *p); + rnb_err_t HandlePacket_jGetDyldProcessState(const char *p); rnb_err_t SendStopReplyPacketForThread(nub_thread_t tid); rnb_err_t SendHexEncodedBytePacket(const char *header, const void *buf, size_t buf_len, const char *footer); diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp --- a/lldb/tools/debugserver/source/RNBRemote.cpp +++ b/lldb/tools/debugserver/source/RNBRemote.cpp @@ -499,6 +499,10 @@ "Test the maximum speed at which packet can be sent/received.")); t.push_back(Packet(query_transfer, &RNBRemote::HandlePacket_qXfer, NULL, "qXfer:", "Support the qXfer packet.")); + t.push_back(Packet(json_query_dyld_process_state, + &RNBRemote::HandlePacket_jGetDyldProcessState, NULL, + "jGetDyldProcessState", + "Query the process state from dyld.")); } void RNBRemote::FlushSTDIO() { @@ -5256,6 +5260,22 @@ return SendPacket(strm.str()); } +rnb_err_t RNBRemote::HandlePacket_jGetDyldProcessState(const char *p) { + const nub_process_t pid = m_ctx.ProcessID(); + if (pid == INVALID_NUB_PROCESS) + return SendPacket("E87"); + + JSONGenerator::ObjectSP dyld_state_sp = DNBGetDyldProcessState(pid); + if (dyld_state_sp) { + std::ostringstream strm; + dyld_state_sp->DumpBinaryEscaped(strm); + dyld_state_sp->Clear(); + if (strm.str().size() > 0) + return SendPacket(strm.str()); + } + return SendPacket("E88"); +} + // A helper function that retrieves a single integer value from // a one-level-deep JSON dictionary of key-value pairs. e.g. // jThreadExtendedInfo:{"plo_pthread_tsd_base_address_offset":0,"plo_pthread_tsd_base_offset":224,"plo_pthread_tsd_entry_size":8,"thread":144305}]