[Lldb-commits] [lldb] ee11ef6 - Launch state discoverable in Darwin, use for SafeToCallFunctions

Jason Molenda via lldb-commits lldb-commits at lists.llvm.org
Tue Dec 13 11:43:05 PST 2022


Author: Jason Molenda
Date: 2022-12-13T11:42:56-08:00
New Revision: ee11ef6dc0b293161dd916b64581eb37a231709b

URL: https://github.com/llvm/llvm-project/commit/ee11ef6dc0b293161dd916b64581eb37a231709b
DIFF: https://github.com/llvm/llvm-project/commit/ee11ef6dc0b293161dd916b64581eb37a231709b.diff

LOG: Launch state discoverable in Darwin, use for SafeToCallFunctions

The dynamic linker on Darwin, dyld, can provide status of
the process state for a few significant points early on,
most importantly, when libSystem has been initialized and it
is safe to call functions behind the scenes.  Pipe this
information up from debugserver to DynamicLoaderMacOS, for
the DynamicLoader::IsFullyInitialized() method, then have
Thread::SafeToCallFunctions use this information.  Finally,
for the two utility functions in the AppleObjCRuntimeV2
LanguageRuntime plugin that I was fixing, call this method
before running our utility functions to collect the list of
objc classes registered in the runtime.

User expressions will still be allowed to run any time -
we assume the user knows what they are doing - but these
two additional utility functions that they are unaware of
will be limited by this state.

Differential Revision: https://reviews.llvm.org/D139054
rdar://102436092
can probably make function calls.

Added: 
    lldb/test/API/macosx/early-process-launch/Makefile
    lldb/test/API/macosx/early-process-launch/TestEarlyProcessLaunch.py
    lldb/test/API/macosx/early-process-launch/main.c

Modified: 
    lldb/docs/lldb-gdb-remote.txt
    lldb/include/lldb/Target/Process.h
    lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp
    lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.h
    lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
    lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
    lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
    lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
    lldb/source/Target/Thread.cpp
    lldb/tools/debugserver/source/DNB.cpp
    lldb/tools/debugserver/source/DNB.h
    lldb/tools/debugserver/source/MacOSX/MachProcess.h
    lldb/tools/debugserver/source/MacOSX/MachProcess.mm
    lldb/tools/debugserver/source/RNBRemote.cpp
    lldb/tools/debugserver/source/RNBRemote.h

Removed: 
    


################################################################################
diff  --git a/lldb/docs/lldb-gdb-remote.txt b/lldb/docs/lldb-gdb-remote.txt
index 551e94d8b71ad..4930eb1ccb712 100644
--- a/lldb/docs/lldb-gdb-remote.txt
+++ b/lldb/docs/lldb-gdb-remote.txt
@@ -2153,3 +2153,21 @@ Example packet:
 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
index b9995c2a44326..47040137078f6 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -1321,6 +1321,15 @@ class Process : public std::enable_shared_from_this<Process>,
     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.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp
index 63b0b96e8ea3f..74db38e4f4efe 100644
--- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp
+++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp
@@ -79,7 +79,8 @@ DynamicLoaderMacOS::DynamicLoaderMacOS(Process *process)
     : 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 @@ bool DynamicLoaderMacOS::ProcessDidExec() {
   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 @@ void DynamicLoaderMacOS::DoClear() {
 
   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/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.h b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.h
index b6e4ce8e5c37d..641c3824e9e71 100644
--- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.h
+++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.h
@@ -79,6 +79,8 @@ class DynamicLoaderMacOS : public lldb_private::DynamicLoaderDarwin {
 
   void DoClear() override;
 
+  bool IsFullyInitialized() override;
+
   static bool
   NotifyBreakpointHit(void *baton,
                       lldb_private::StoppointCallbackContext *context,
@@ -106,6 +108,7 @@ class DynamicLoaderMacOS : public lldb_private::DynamicLoaderDarwin {
                                             // 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/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
index 1c2447b2bec5f..ac47d0bbe8a6b 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
@@ -1875,6 +1875,9 @@ AppleObjCRuntimeV2::DynamicClassInfoExtractor::UpdateISAToDescriptorMap(
   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 @@ AppleObjCRuntimeV2::DynamicClassInfoExtractor::UpdateISAToDescriptorMap(
     }
   }
 
-  return DescriptorMapUpdateResult(success, num_class_infos);
+  return DescriptorMapUpdateResult(success, false, num_class_infos);
 }
 
 uint32_t AppleObjCRuntimeV2::ParseClassInfoArray(const DataExtractor &data,
@@ -2137,6 +2140,9 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() {
   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 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() {
   // 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 @@ void AppleObjCRuntimeV2::UpdateISAToDescriptorMapIfNeeded() {
 
       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 @@ void AppleObjCRuntimeV2::WarnIfNoClassesCached(
         "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/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
index 1a8e9d4aa339c..fe6bf29c01eb6 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
@@ -286,18 +286,24 @@ class AppleObjCRuntimeV2 : public AppleObjCRuntime {
 
   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 @@ class AppleObjCRuntimeV2 : public AppleObjCRuntime {
                                uint32_t num_class_infos);
 
   enum class SharedCacheWarningReason {
+    eExpressionUnableToRun,
     eExpressionExecutionFailure,
     eNotEnoughClassesRead
   };

diff  --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index a8af26a82b08e..29e9620eb433c 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -613,6 +613,19 @@ bool GDBRemoteCommunicationClient::GetSharedCacheInfoSupported() {
   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/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index c5911b9f0784f..4f37c0e23e52f 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -429,6 +429,8 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
 
   bool GetSharedCacheInfoSupported();
 
+  bool GetDynamicLoaderProcessStateSupported();
+
   bool GetMemoryTaggingSupported();
 
   bool UsesNativeSignals();
@@ -553,6 +555,7 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
   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/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 3c67e26fd84fc..07b41be0bc984 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -3829,6 +3829,29 @@ ProcessGDBRemote::GetLoadedDynamicLibrariesInfos_sender(
   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/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index 0f826ccab56f7..685f400344177 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -222,6 +222,8 @@ class ProcessGDBRemote : public Process,
 
   StructuredData::ObjectSP GetSharedCacheInfo() override;
 
+  StructuredData::ObjectSP GetDynamicLoaderProcessState() override;
+
   std::string HarmonizeThreadIdsForProfileData(
       StringExtractorGDBRemote &inputStringExtractor);
 

diff  --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index 6c3381cf6e9e5..ade24dac65f32 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/source/Target/Thread.cpp
@@ -1663,6 +1663,10 @@ addr_t Thread::GetThreadLocalData(const ModuleSP module,
 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
index 0000000000000..10495940055b6
--- /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
index 0000000000000..7ab52cb73e801
--- /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 
diff erence.
+
+        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
index 0000000000000..2d3e6ad14c7cc
--- /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.cpp b/lldb/tools/debugserver/source/DNB.cpp
index d38c151d9f808..a95238c6279e1 100644
--- a/lldb/tools/debugserver/source/DNB.cpp
+++ b/lldb/tools/debugserver/source/DNB.cpp
@@ -599,6 +599,14 @@ size_t DNBGetAllInfos(std::vector<struct kinfo_proc> &proc_infos) {
   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<struct kinfo_proc> &matching_proc_infos) {

diff  --git a/lldb/tools/debugserver/source/DNB.h b/lldb/tools/debugserver/source/DNB.h
index 15d11fa794162..3108f1ae7a20e 100644
--- a/lldb/tools/debugserver/source/DNB.h
+++ b/lldb/tools/debugserver/source/DNB.h
@@ -157,6 +157,7 @@ nub_size_t DNBProcessGetAvailableProfileData(nub_process_t pid, char *buf,
 nub_size_t DNBProcessGetStopCount(nub_process_t pid) DNB_EXPORT;
 uint32_t DNBProcessGetCPUType(nub_process_t pid) DNB_EXPORT;
 size_t DNBGetAllInfos(std::vector<struct kinfo_proc> &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/MacOSX/MachProcess.h b/lldb/tools/debugserver/source/MacOSX/MachProcess.h
index 2ff48879a5bfd..0874019c4aeab 100644
--- a/lldb/tools/debugserver/source/MacOSX/MachProcess.h
+++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.h
@@ -362,6 +362,8 @@ class MachProcess {
 
   DNBProfileDataScanType GetProfileScanType() { return m_profile_scan_type; }
 
+  JSONGenerator::ObjectSP GetDyldProcessState();
+
 private:
   enum {
     eMachProcessFlagsNone = 0,
@@ -468,6 +470,7 @@ class MachProcess {
   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
index 8f3d2004601b1..3aa2a84bc359f 100644
--- a/lldb/tools/debugserver/source/MacOSX/MachProcess.mm
+++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.mm
@@ -508,7 +508,6 @@ static bool FBSAddEventDataToOptions(NSMutableDictionary *options,
 #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 @@ static bool FBSAddEventDataToOptions(NSMutableDictionary *options,
       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 @@ static bool FBSAddEventDataToOptions(NSMutableDictionary *options,
       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 @@ static bool FBSAddEventDataToOptions(NSMutableDictionary *options,
       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 @@ static bool mach_header_validity_test(uint32_t magic, uint32_t cputype) {
   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.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp
index 5dad4cfb43ffc..c7a7f494a455e 100644
--- a/lldb/tools/debugserver/source/RNBRemote.cpp
+++ b/lldb/tools/debugserver/source/RNBRemote.cpp
@@ -499,6 +499,10 @@ void RNBRemote::CreatePacketTable() {
       "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 @@ rnb_err_t RNBRemote::HandlePacket_qGDBServerVersion(const char *p) {
   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}]

diff  --git a/lldb/tools/debugserver/source/RNBRemote.h b/lldb/tools/debugserver/source/RNBRemote.h
index 41b9aede7d40b..dad390ae0b632 100644
--- a/lldb/tools/debugserver/source/RNBRemote.h
+++ b/lldb/tools/debugserver/source/RNBRemote.h
@@ -136,6 +136,7 @@ class RNBRemote {
     speed_test,                         // 'qSpeedTest:'
     set_detach_on_error,                // 'QSetDetachOnError:'
     query_transfer,                     // 'qXfer:'
+    json_query_dyld_process_state,      // 'jGetDyldProcessState'
     unknown_type
   };
 
@@ -246,6 +247,7 @@ class RNBRemote {
   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);


        


More information about the lldb-commits mailing list