This revision was automatically updated to reflect the committed changes.
Closed by commit rGee11ef6dc0b2: Launch state discoverable in Darwin, use for 
SafeToCallFunctions (authored by jasonmolenda).

Changed prior to commit:
  https://reviews.llvm.org/D139054?vs=480277&id=482575#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D139054/new/

https://reviews.llvm.org/D139054

Files:
  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/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
  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

Index: lldb/tools/debugserver/source/RNBRemote.h
===================================================================
--- lldb/tools/debugserver/source/RNBRemote.h
+++ 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);
Index: lldb/tools/debugserver/source/RNBRemote.cpp
===================================================================
--- lldb/tools/debugserver/source/RNBRemote.cpp
+++ 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}]
Index: lldb/tools/debugserver/source/MacOSX/MachProcess.mm
===================================================================
--- lldb/tools/debugserver/source/MacOSX/MachProcess.mm
+++ 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) {
Index: lldb/tools/debugserver/source/MacOSX/MachProcess.h
===================================================================
--- lldb/tools/debugserver/source/MacOSX/MachProcess.h
+++ 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
Index: lldb/tools/debugserver/source/DNB.h
===================================================================
--- lldb/tools/debugserver/source/DNB.h
+++ 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<struct kinfo_proc> &proc_infos);
+JSONGenerator::ObjectSP DNBGetDyldProcessState(nub_process_t pid);
 
 // Process executable and arguments
 const char *DNBProcessGetExecutablePath(nub_process_t pid);
Index: lldb/tools/debugserver/source/DNB.cpp
===================================================================
--- lldb/tools/debugserver/source/DNB.cpp
+++ 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<struct kinfo_proc> &matching_proc_infos) {
Index: lldb/test/API/macosx/early-process-launch/main.c
===================================================================
--- /dev/null
+++ lldb/test/API/macosx/early-process-launch/main.c
@@ -0,0 +1,2 @@
+int global = 10;
+int main() { return global; }
Index: lldb/test/API/macosx/early-process-launch/TestEarlyProcessLaunch.py
===================================================================
--- /dev/null
+++ 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)
Index: lldb/test/API/macosx/early-process-launch/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/macosx/early-process-launch/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
Index: lldb/source/Target/Thread.cpp
===================================================================
--- lldb/source/Target/Thread.cpp
+++ 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());
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ 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);
 
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ 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());
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ 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;
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ 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();
Index: lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
===================================================================
--- lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
+++ 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
   };
Index: lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
===================================================================
--- lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
+++ 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;
   }
 }
 
Index: lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.h
===================================================================
--- lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.h
+++ 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
Index: lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp
===================================================================
--- lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp
+++ 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
Index: lldb/include/lldb/Target/Process.h
===================================================================
--- lldb/include/lldb/Target/Process.h
+++ 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
   ///
Index: lldb/docs/lldb-gdb-remote.txt
===================================================================
--- lldb/docs/lldb-gdb-remote.txt
+++ 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.
+//----------------------------------------------------------------------
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to