mib updated this revision to Diff 245424.
mib retitled this revision from "[lldb/Target] Add process crash-info command" 
to "[lldb/Plugins] Add `platform process crash-info` command".
mib edited the summary of this revision.
mib added a comment.

Moved implementation from Process to Platform / PlatformDarwin and made it more 
generic and extensive so other platforms could also use it.
Added documentation and addressed comments.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D74657

Files:
  lldb/bindings/interface/SBPlatform.i
  lldb/include/lldb/API/SBPlatform.h
  lldb/include/lldb/API/SBStructuredData.h
  lldb/include/lldb/API/SBTarget.h
  lldb/include/lldb/Target/Platform.h
  lldb/include/lldb/Target/Process.h
  lldb/source/API/SBPlatform.cpp
  lldb/source/Commands/CommandObjectPlatform.cpp
  lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
  lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h
  lldb/source/Target/Platform.cpp
  lldb/test/API/commands/platform/process/crash-info/Makefile
  
lldb/test/API/commands/platform/process/crash-info/TestPlatformProcessCrashInfo.py
  lldb/test/API/commands/platform/process/crash-info/main.c

Index: lldb/test/API/commands/platform/process/crash-info/main.c
===================================================================
--- /dev/null
+++ lldb/test/API/commands/platform/process/crash-info/main.c
@@ -0,0 +1,7 @@
+#include <stdlib.h>
+int main() {
+  int *var = malloc(sizeof(int));
+  free(var);
+  free(var);
+  return 0;
+}
Index: lldb/test/API/commands/platform/process/crash-info/TestPlatformProcessCrashInfo.py
===================================================================
--- /dev/null
+++ lldb/test/API/commands/platform/process/crash-info/TestPlatformProcessCrashInfo.py
@@ -0,0 +1,63 @@
+"""
+Test lldb platform process crash info.
+"""
+
+import os
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class PlatformProcessCrashInfoTestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    def setUp(self):
+        TestBase.setUp(self)
+        self.runCmd("settings set auto-confirm true")
+
+    def tearDown(self):
+        self.runCmd("settings clear auto-confirm")
+        TestBase.tearDown(self)
+
+    @skipUnlessDarwin
+    def test_cli(self):
+        """Test that platform process crash-info fetches the extended crash
+        information array from the command-line properly."""
+        self.build()
+        exe = self.getBuildArtifact("a.out")
+        self.expect("file " + exe,
+                    patterns=["Current executable set to .*a.out"])
+
+        self.expect('process launch',
+                    patterns=["Process .* launched: .*a.out"])
+
+        self.expect('platform process crash-info',
+                    patterns=["\"message\".*pointer being freed was not allocated"])
+
+
+    @skipUnlessDarwin
+    def test_api(self):
+        """Test that platform process crash-info fetches the extended crash
+        information array from the api properly."""
+        self.build()
+        target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+        self.assertTrue(target, VALID_TARGET)
+
+        target.LaunchSimple(None, None, os.getcwd())
+
+        platform = target.GetPlatform()
+
+        stream = lldb.SBStream()
+        self.assertTrue(stream)
+
+        crash_info = platform.GetExtendedCrashInformation(target)
+
+        error = crash_info.GetAsJSON(stream)
+
+        self.assertTrue(error.Success())
+
+        self.assertTrue(crash_info.IsValid())
+
+        self.assertIn("pointer being freed was not allocated", stream.GetData())
Index: lldb/test/API/commands/platform/process/crash-info/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/commands/platform/process/crash-info/Makefile
@@ -0,0 +1,4 @@
+C_SOURCES := main.c
+
+include Makefile.rules
+
Index: lldb/source/Target/Platform.cpp
===================================================================
--- lldb/source/Target/Platform.cpp
+++ lldb/source/Target/Platform.cpp
@@ -384,7 +384,8 @@
       m_rsync_opts(), m_rsync_prefix(), m_supports_ssh(false), m_ssh_opts(),
       m_ignores_remote_hostname(false), m_trap_handlers(),
       m_calculated_trap_handlers(false),
-      m_module_cache(std::make_unique<ModuleCache>()) {
+      m_module_cache(std::make_unique<ModuleCache>()),
+      m_extended_crash_info(nullptr) {
   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
   LLDB_LOGF(log, "%p Platform::Platform()", static_cast<void *>(this));
 }
Index: lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h
===================================================================
--- lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h
+++ lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h
@@ -12,6 +12,7 @@
 #include "Plugins/Platform/POSIX/PlatformPOSIX.h"
 #include "lldb/Host/FileSystem.h"
 #include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/StructuredData.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/FileSystem.h"
 
@@ -84,7 +85,44 @@
     iPhoneOS,
   };
 
+  lldb_private::StructuredData::DictionarySP
+  FetchExtendedCrashInformation(lldb_private::Target &target) override;
+
 protected:
+  // This struct should only be available in macOS but could be used on other
+  // platforms. #ifdef HAVE_CRASHREPORTERCLIENT_H #include
+  // <CrashReporterClient.h> #endif
+  struct CrashInfoAnnotations {
+    uint64_t version;          // unsigned long
+    uint64_t message;          // char *
+    uint64_t signature_string; // char *
+    uint64_t backtrace;        // char *
+    uint64_t message2;         // char *
+    uint64_t thread;           // uint64_t
+    uint64_t dialog_mode;      // unsigned int
+    uint64_t abort_cause;      // unsigned int
+  };
+
+  /// Extract the `__crash_info` annotations from each of of the target's
+  /// modules.
+  ///
+  /// If the platform have a crashed processes with a `__crash_info` section,
+  /// extract the section to gather the messages annotations and the abort
+  /// cause.
+  ///
+  /// \param[in] target
+  ///     The target running the crashed process.
+  /// \param[in] log
+  ///     The log pointer initialized with the right logging channel
+  ///
+  /// \return
+  ///     A structured data array containing at each entry in each entry, the
+  ///     module spec, its UUID, the crash messages and the abort cause. Or a
+  ///     nullptr if the process has no crash information entry.
+  lldb_private::StructuredData::ArraySP
+  ExtractCrashInfoAnnotations(lldb_private::Target &target,
+                              lldb_private::Log *log);
+
   void ReadLibdispatchOffsetsAddress(lldb_private::Process *process);
 
   void ReadLibdispatchOffsets(lldb_private::Process *process);
Index: lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
===================================================================
--- lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
+++ lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
@@ -19,6 +19,7 @@
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/Module.h"
 #include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/Section.h"
 #include "lldb/Host/Host.h"
 #include "lldb/Host/HostInfo.h"
 #include "lldb/Host/XML.h"
@@ -1501,6 +1502,125 @@
   return std::make_tuple(version, build);
 }
 
+StructuredData::DictionarySP
+PlatformDarwin::FetchExtendedCrashInformation(lldb_private::Target &target) {
+  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+  StructuredData::ArraySP annotations =
+      ExtractCrashInfoAnnotations(target, log);
+
+  if (!annotations) {
+    LLDB_LOG(log, "Couldn't extract crash information annotations");
+  }
+
+  if (!m_extended_crash_info)
+    m_extended_crash_info = std::make_shared<StructuredData::Dictionary>();
+
+  if (annotations)
+    m_extended_crash_info->AddItem("crash-info annotations", annotations);
+
+  return m_extended_crash_info;
+}
+
+StructuredData::ArraySP
+PlatformDarwin::ExtractCrashInfoAnnotations(Target &target, Log *log) {
+  ProcessSP process_sp = target.GetProcessSP();
+  StructuredData::ArraySP array_sp = std::make_shared<StructuredData::Array>();
+
+  for (ModuleSP module : target.GetImages().Modules()) {
+    SectionList *sections = module->GetSectionList();
+
+    std::string module_name = module->GetSpecificationDescription();
+
+    if (!sections) {
+      LLDB_LOG(log, "Module {0} doesn't have any section!", module_name);
+      continue;
+    }
+
+    ConstString section_name("__crash_info");
+    SectionSP crash_info = sections->FindSectionByName(section_name);
+    if (!crash_info) {
+      LLDB_LOG(log, "Module {0} doesn't have section {1}!", module_name,
+               section_name);
+      continue;
+    }
+
+    addr_t load_addr = crash_info->GetLoadBaseAddress(&target);
+
+    if (load_addr == LLDB_INVALID_ADDRESS) {
+      LLDB_LOG(log, "Module {0} has an invalid '{1}' section load address: {2}",
+               module_name, section_name, load_addr);
+      continue;
+    }
+
+    Status error;
+    CrashInfoAnnotations annotations;
+    size_t expected_size = sizeof(CrashInfoAnnotations);
+    size_t bytes_read = process_sp->ReadMemoryFromInferior(
+        load_addr, &annotations, expected_size, error);
+
+    if (expected_size != bytes_read || error.Fail()) {
+      LLDB_LOG(log, "Failed to read {0} section from memory in module {1}: {2}",
+               section_name, module_name, error);
+      continue;
+    }
+
+    // initial support added for version 5
+    if (annotations.version < 5) {
+      LLDB_LOG(log,
+               "Annotation version lower than 5 unsupported! Module {0} has "
+               "version {1} instead.",
+               module_name, annotations.version);
+      continue;
+    }
+
+    if (!annotations.message) {
+      LLDB_LOG(log, "No message available for module {0}.", module_name);
+      continue;
+    }
+
+    std::string message;
+    bytes_read =
+        process_sp->ReadCStringFromMemory(annotations.message, message, error);
+
+    if (message.empty() || bytes_read != message.size() || error.Fail()) {
+      LLDB_LOG(log, "Failed to read the message from memory in module {0}: {1}",
+               module_name, error);
+      continue;
+    }
+
+    // Remove trailing newline from message
+    if (message.back() == '\n')
+      message.pop_back();
+
+    if (!annotations.message2) {
+      LLDB_LOG(log, "No message2 available for module {0}.", module_name);
+    }
+
+    std::string message2;
+    bytes_read = process_sp->ReadCStringFromMemory(annotations.message2,
+                                                   message2, error);
+
+    if (!message2.empty() && bytes_read == message2.size() && error.Success()) {
+      if (message2.back() == '\n')
+        message2.pop_back();
+    }
+
+    StructuredData::DictionarySP entry_sp =
+        std::make_shared<StructuredData::Dictionary>();
+
+    entry_sp->AddStringItem("image", module->GetFileSpec().GetPath(false));
+    entry_sp->AddStringItem("uuid", module->GetUUID().GetAsString());
+    entry_sp->AddStringItem("message", message);
+    entry_sp->AddStringItem("message2", message2);
+    entry_sp->AddIntegerItem("abort-cause", annotations.abort_cause);
+
+    array_sp->AddItem(entry_sp);
+  }
+
+  return array_sp;
+}
+
 void PlatformDarwin::AddClangModuleCompilationOptionsForSDKType(
     Target *target, std::vector<std::string> &options, SDKType sdk_type) {
   const std::vector<std::string> apple_arguments = {
Index: lldb/source/Commands/CommandObjectPlatform.cpp
===================================================================
--- lldb/source/Commands/CommandObjectPlatform.cpp
+++ lldb/source/Commands/CommandObjectPlatform.cpp
@@ -1517,14 +1517,64 @@
   CommandOptions m_options;
 };
 
+// CommandObjectPlatformProcessCrashInfo
+#pragma mark CommandObjectPlatformProcessCrashInfo
+
+class CommandObjectPlatformProcessCrashInfo : public CommandObjectParsed {
+public:
+  CommandObjectPlatformProcessCrashInfo(CommandInterpreter &interpreter)
+      : CommandObjectParsed(interpreter, "platform process crash-info",
+                            "Fetch the platform's process crash information",
+                            "process crash-info",
+                            eCommandRequiresProcess |
+                                eCommandTryTargetAPILock) {}
+
+  ~CommandObjectPlatformProcessCrashInfo() override = default;
+
+  bool DoExecute(Args &command, CommandReturnObject &result) override {
+    Stream &strm = result.GetOutputStream();
+    result.SetStatus(eReturnStatusSuccessFinishNoResult);
+
+    PlatformSP platform_sp(
+        GetDebugger().GetPlatformList().GetSelectedPlatform());
+    if (!platform_sp) {
+      result.AppendError("Couldn'retrieve the selected platform");
+      return result.Succeeded();
+    }
+
+    if (command.GetArgumentCount()) {
+      result.AppendError("`process crash-info` takes no arguments");
+      return result.Succeeded();
+    }
+
+    // No need to check "process" for validity as eCommandRequiresProcess
+    // ensures it is valid
+    Target &target = GetSelectedTarget();
+    ProcessSP process_sp = target.GetProcessSP();
+
+    StructuredData::ObjectSP crash_info_sp =
+        platform_sp->FetchExtendedCrashInformation(target);
+
+    if (!crash_info_sp) {
+      result.AppendError("Couldn't fetch the crash information");
+      return result.Succeeded();
+    }
+
+    crash_info_sp->Dump(strm);
+
+    return result.Succeeded();
+  }
+};
+
 class CommandObjectPlatformProcess : public CommandObjectMultiword {
 public:
   // Constructors and Destructors
   CommandObjectPlatformProcess(CommandInterpreter &interpreter)
-      : CommandObjectMultiword(interpreter, "platform process",
-                               "Commands to query, launch and attach to "
-                               "processes on the current platform.",
-                               "platform process [attach|launch|list] ...") {
+      : CommandObjectMultiword(
+            interpreter, "platform process",
+            "Commands to query, launch and attach to "
+            "processes on the current platform.",
+            "platform process [attach|launch|list|crash-info] ...") {
     LoadSubCommand(
         "attach",
         CommandObjectSP(new CommandObjectPlatformProcessAttach(interpreter)));
@@ -1535,6 +1585,9 @@
                                interpreter)));
     LoadSubCommand("list", CommandObjectSP(new CommandObjectPlatformProcessList(
                                interpreter)));
+    LoadSubCommand("crash-info",
+                   CommandObjectSP(
+                       new CommandObjectPlatformProcessCrashInfo(interpreter)));
   }
 
   ~CommandObjectPlatformProcess() override = default;
Index: lldb/source/API/SBPlatform.cpp
===================================================================
--- lldb/source/API/SBPlatform.cpp
+++ lldb/source/API/SBPlatform.cpp
@@ -11,7 +11,9 @@
 #include "lldb/API/SBError.h"
 #include "lldb/API/SBFileSpec.h"
 #include "lldb/API/SBLaunchInfo.h"
+#include "lldb/API/SBTarget.h"
 #include "lldb/API/SBUnixSignals.h"
+#include "lldb/Core/StructuredDataImpl.h"
 #include "lldb/Host/File.h"
 #include "lldb/Target/Platform.h"
 #include "lldb/Target/Target.h"
@@ -643,6 +645,21 @@
   return LLDB_RECORD_RESULT(SBUnixSignals());
 }
 
+SBStructuredData SBPlatform::GetExtendedCrashInformation(SBTarget &target) {
+  LLDB_RECORD_METHOD(lldb::SBStructuredData, SBPlatform,
+                     GetExtendedCrashInformation, (lldb::SBTarget &), target);
+
+  SBStructuredData data;
+  PlatformSP platform_sp(GetSP());
+  if (!platform_sp)
+    return LLDB_RECORD_RESULT(data);
+
+  StructuredData::ObjectSP fetched_data =
+      platform_sp->FetchExtendedCrashInformation(*target.GetSP().get());
+  data.m_impl_up->SetObjectSP(fetched_data);
+  return LLDB_RECORD_RESULT(data);
+}
+
 namespace lldb_private {
 namespace repro {
 
@@ -736,6 +753,8 @@
                        (const char *, uint32_t));
   LLDB_REGISTER_METHOD_CONST(lldb::SBUnixSignals, SBPlatform, GetUnixSignals,
                              ());
+  LLDB_REGISTER_METHOD(lldb::SBStructuredData, SBPlatform,
+                       GetExtendedCrashInformation, (lldb::SBTarget &));
 }
 
 }
Index: lldb/include/lldb/Target/Process.h
===================================================================
--- lldb/include/lldb/Target/Process.h
+++ lldb/include/lldb/Target/Process.h
@@ -1267,7 +1267,7 @@
   ///     LLDB_INVALID_ADDRESS.
   ///
   /// \return
-  ///     A StructureDataSP object which, if non-empty, will contain the
+  ///     A StructuredDataSP object which, if non-empty, will contain the
   ///     information the DynamicLoader needs to get the initial scan of
   ///     solibs resolved.
   virtual lldb_private::StructuredData::ObjectSP
Index: lldb/include/lldb/Target/Platform.h
===================================================================
--- lldb/include/lldb/Target/Platform.h
+++ lldb/include/lldb/Target/Platform.h
@@ -23,6 +23,7 @@
 #include "lldb/Utility/ArchSpec.h"
 #include "lldb/Utility/ConstString.h"
 #include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/StructuredData.h"
 #include "lldb/Utility/Timeout.h"
 #include "lldb/Utility/UserIDResolver.h"
 #include "lldb/lldb-private-forward.h"
@@ -823,6 +824,25 @@
   virtual size_t ConnectToWaitingProcesses(lldb_private::Debugger &debugger,
                                            lldb_private::Status &error);
 
+  /// Gather all of crash informations into a structured data dictionnary.
+  ///
+  /// If the platform have a crashed processes with crash information entries,
+  /// gather all the entries into an structured data dictionnary or return a
+  /// nullptr. This dictionnary is generic and extensible, as it contains an
+  /// array for each different type of crash information.
+  ///
+  /// \param[in] target
+  ///     The target running the crashed process.
+  ///
+  /// \return
+  ///     A Structured Data Dictionnary containing at each entry an array for
+  ///     each different crash information type. Or a nullptr if the process has
+  ///     no crash information entry.
+  virtual StructuredData::DictionarySP
+  FetchExtendedCrashInformation(lldb_private::Target &target) {
+    return nullptr;
+  }
+
 protected:
   bool m_is_host;
   // Set to true when we are able to actually set the OS version while being
@@ -858,6 +878,7 @@
   std::vector<ConstString> m_trap_handlers;
   bool m_calculated_trap_handlers;
   const std::unique_ptr<ModuleCache> m_module_cache;
+  StructuredData::DictionarySP m_extended_crash_info;
 
   /// Ask the Platform subclass to fill in the list of trap handler names
   ///
Index: lldb/include/lldb/API/SBTarget.h
===================================================================
--- lldb/include/lldb/API/SBTarget.h
+++ lldb/include/lldb/API/SBTarget.h
@@ -829,6 +829,7 @@
   friend class SBFunction;
   friend class SBInstruction;
   friend class SBModule;
+  friend class SBPlatform;
   friend class SBProcess;
   friend class SBSection;
   friend class SBSourceManager;
Index: lldb/include/lldb/API/SBStructuredData.h
===================================================================
--- lldb/include/lldb/API/SBStructuredData.h
+++ lldb/include/lldb/API/SBStructuredData.h
@@ -90,6 +90,7 @@
 protected:
   friend class SBTraceOptions;
   friend class SBDebugger;
+  friend class SBPlatform;
   friend class SBTarget;
   friend class SBThread;
   friend class SBThreadPlan;
Index: lldb/include/lldb/API/SBPlatform.h
===================================================================
--- lldb/include/lldb/API/SBPlatform.h
+++ lldb/include/lldb/API/SBPlatform.h
@@ -10,6 +10,7 @@
 #define LLDB_API_SBPLATFORM_H
 
 #include "lldb/API/SBDefines.h"
+#include "lldb/API/SBStructuredData.h"
 
 #include <functional>
 
@@ -152,6 +153,8 @@
 
   SBUnixSignals GetUnixSignals() const;
 
+  SBStructuredData GetExtendedCrashInformation(SBTarget &target);
+
 protected:
   friend class SBDebugger;
   friend class SBTarget;
Index: lldb/bindings/interface/SBPlatform.i
===================================================================
--- lldb/bindings/interface/SBPlatform.i
+++ lldb/bindings/interface/SBPlatform.i
@@ -192,6 +192,11 @@
     lldb::SBUnixSignals
     GetUnixSignals();
 
+    %feature("autodoc", "
+    Returns the platform's process extended crash information.") GetExtendedCrashInformation;
+    lldb::SBStructuredData
+    GetExtendedCrashInformation (lldb::SBTarget& target);
+
 };
 
 } // namespace lldb
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to