[Lldb-commits] [lldb] d7c403e - [lldb/Plugins] Add ability to fetch crash information on crashed processes

Med Ismail Bennani via lldb-commits lldb-commits at lists.llvm.org
Fri Feb 21 13:44:53 PST 2020


Author: Med Ismail Bennani
Date: 2020-02-21T22:44:36+01:00
New Revision: d7c403e64043281b9c5883e3e034da5ebaf4985a

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

LOG: [lldb/Plugins] Add ability to fetch crash information on crashed processes

Currently, in macOS, when a process crashes, lldb halts inside the
implementation disassembly without yielding any useful information.
The only way to get more information is to detach from the process, then wait
for ReportCrash to generate a report, find the report, then see what error
message was included in it. Instead of waiting for this to happen, lldb could
locate the error_string and make it available to the user.

This patch addresses this issue by enabling the user to fetch extended
crash information for crashed processes using `process status --verbose`.

Depending on the platform, this will try to gather different crash information
into an structured data dictionnary. This dictionnary is generic and extensible,
as it contains an array for each different type of crash information.

On Darwin Platforms, lldb will iterate over each of the target's images,
extract their `__crash_info` section and generated a StructuredData::Array
containing, in each entry, the module spec, its UUID, the crash messages
and the abort cause. The array will be inserted into the platform's
`m_extended_crash_info` dictionnary and `FetchExtendedCrashInformation` will
return its JSON representation like this:

```
{
  "crash-info annotations": [
    {
      "abort-cause": 0,
      "image": "/usr/lib/system/libsystem_malloc.dylib",
      "message": "main(76483,0x1000cedc0) malloc: *** error for object 0x1003040a0: pointer being freed was not allocated",
      "message2": "",
      "uuid": "5747D0C9-900D-3306-8D70-1E2EA4B7E821"
    },
    ...
  ],
  ...
}
```

This crash information can also be fetched using the SB API or lldb-rpc protocol
using SBTarget::GetExtendedCrashInformation().

rdar://37736535

Differential Revision: https://reviews.llvm.org/D74657

Signed-off-by: Med Ismail Bennani <medismail.bennani at gmail.com>

Added: 
    lldb/test/API/functionalities/process_crash_info/Makefile
    lldb/test/API/functionalities/process_crash_info/TestProcessCrashInfo.py
    lldb/test/API/functionalities/process_crash_info/main.c

Modified: 
    lldb/bindings/interface/SBTarget.i
    lldb/include/lldb/API/SBTarget.h
    lldb/include/lldb/Target/Platform.h
    lldb/include/lldb/Target/Process.h
    lldb/source/API/SBTarget.cpp
    lldb/source/Commands/CommandObjectProcess.cpp
    lldb/source/Commands/Options.td
    lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
    lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h

Removed: 
    


################################################################################
diff  --git a/lldb/bindings/interface/SBTarget.i b/lldb/bindings/interface/SBTarget.i
index 371bf5c35ebd..aec7ab94942a 100644
--- a/lldb/bindings/interface/SBTarget.i
+++ b/lldb/bindings/interface/SBTarget.i
@@ -949,6 +949,12 @@ public:
     void
     SetLaunchInfo (const lldb::SBLaunchInfo &launch_info);
 
+    %feature("autodoc", "
+    Returns the platform's process extended crash information.") GetExtendedCrashInformation;
+    lldb::SBStructuredData
+    GetExtendedCrashInformation ();
+
+
     void SetCollectingStats(bool v);
 
     bool GetCollectingStats();

diff  --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h
index c950c12daf17..f95c89d9a47a 100644
--- a/lldb/include/lldb/API/SBTarget.h
+++ b/lldb/include/lldb/API/SBTarget.h
@@ -819,6 +819,8 @@ class LLDB_API SBTarget {
 
   void SetLaunchInfo(const lldb::SBLaunchInfo &launch_info);
 
+  SBStructuredData GetExtendedCrashInformation();
+
 protected:
   friend class SBAddress;
   friend class SBBlock;
@@ -829,6 +831,7 @@ class LLDB_API SBTarget {
   friend class SBFunction;
   friend class SBInstruction;
   friend class SBModule;
+  friend class SBPlatform;
   friend class SBProcess;
   friend class SBSection;
   friend class SBSourceManager;

diff  --git a/lldb/include/lldb/Target/Platform.h b/lldb/include/lldb/Target/Platform.h
index 2431f94644d1..79bbc130ef86 100644
--- a/lldb/include/lldb/Target/Platform.h
+++ b/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,26 @@ class Platform : public PluginInterface {
   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 process 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 
diff erent type of crash information.
+  ///
+  /// \param[in] target
+  ///     The target running the crashed process.
+  ///
+  /// \return
+  ///     A structured data dictionnary containing at each entry, the crash
+  ///     information type as the entry key and the matching  an array as the
+  ///     entry value. \b nullptr if not implemented or  if the process has no
+  ///     crash information entry. \b error if an error occured.
+  virtual llvm::Expected<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

diff  --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h
index 44f8efd7e97c..87f61c66bb41 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -1267,7 +1267,7 @@ class Process : public std::enable_shared_from_this<Process>,
   ///     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

diff  --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp
index b90e77280d24..b07120b4c9f3 100644
--- a/lldb/source/API/SBTarget.cpp
+++ b/lldb/source/API/SBTarget.cpp
@@ -2388,6 +2388,30 @@ void SBTarget::SetLaunchInfo(const lldb::SBLaunchInfo &launch_info) {
     m_opaque_sp->SetProcessLaunchInfo(launch_info.ref());
 }
 
+SBStructuredData SBTarget::GetExtendedCrashInformation() {
+  LLDB_RECORD_METHOD_NO_ARGS(lldb::SBStructuredData, SBTarget,
+                             GetExtendedCrashInformation);
+  SBStructuredData data;
+  TargetSP target_sp(GetSP());
+  if (!target_sp)
+    return LLDB_RECORD_RESULT(data);
+
+  PlatformSP platform_sp = target_sp->GetPlatform();
+
+  if (!target_sp)
+    return LLDB_RECORD_RESULT(data);
+
+  auto expected_data =
+      platform_sp->FetchExtendedCrashInformation(*target_sp.get());
+
+  if (!expected_data)
+    return LLDB_RECORD_RESULT(data);
+
+  StructuredData::ObjectSP fetched_data = *expected_data;
+  data.m_impl_up->SetObjectSP(fetched_data);
+  return LLDB_RECORD_RESULT(data);
+}
+
 namespace lldb_private {
 namespace repro {
 
@@ -2630,6 +2654,8 @@ void RegisterMethods<SBTarget>(Registry &R) {
   LLDB_REGISTER_METHOD_CONST(lldb::SBLaunchInfo, SBTarget, GetLaunchInfo, ());
   LLDB_REGISTER_METHOD(void, SBTarget, SetLaunchInfo,
                        (const lldb::SBLaunchInfo &));
+  LLDB_REGISTER_METHOD(lldb::SBStructuredData, SBTarget,
+                       GetExtendedCrashInformation, ());
   LLDB_REGISTER_METHOD(
       size_t, SBTarget, ReadMemory,
       (const lldb::SBAddress, void *, size_t, lldb::SBError &));

diff  --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp
index 01451222cd8b..4ee085e97e4c 100644
--- a/lldb/source/Commands/CommandObjectProcess.cpp
+++ b/lldb/source/Commands/CommandObjectProcess.cpp
@@ -1201,6 +1201,8 @@ class CommandObjectProcessSaveCore : public CommandObjectParsed {
 
 // CommandObjectProcessStatus
 #pragma mark CommandObjectProcessStatus
+#define LLDB_OPTIONS_process_status
+#include "CommandOptions.inc"
 
 class CommandObjectProcessStatus : public CommandObjectParsed {
 public:
@@ -1209,13 +1211,57 @@ class CommandObjectProcessStatus : public CommandObjectParsed {
             interpreter, "process status",
             "Show status and stop location for the current target process.",
             "process status",
-            eCommandRequiresProcess | eCommandTryTargetAPILock) {}
+            eCommandRequiresProcess | eCommandTryTargetAPILock),
+        m_options() {}
 
   ~CommandObjectProcessStatus() override = default;
 
+  Options *GetOptions() override { return &m_options; }
+
+  class CommandOptions : public Options {
+  public:
+    CommandOptions() : Options(), m_verbose(false) {}
+
+    ~CommandOptions() override = default;
+
+    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+                          ExecutionContext *execution_context) override {
+      const int short_option = m_getopt_table[option_idx].val;
+
+      switch (short_option) {
+      case 'v':
+        m_verbose = true;
+        break;
+      default:
+        llvm_unreachable("Unimplemented option");
+      }
+
+      return {};
+    }
+
+    void OptionParsingStarting(ExecutionContext *execution_context) override {
+      m_verbose = false;
+    }
+
+    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+      return llvm::makeArrayRef(g_process_status_options);
+    }
+
+    // Instance variables to hold the values for command options.
+    bool m_verbose;
+  };
+
+protected:
   bool DoExecute(Args &command, CommandReturnObject &result) override {
     Stream &strm = result.GetOutputStream();
     result.SetStatus(eReturnStatusSuccessFinishNoResult);
+
+    if (command.GetArgumentCount()) {
+      result.AppendError("'process status' takes no arguments");
+      result.SetStatus(eReturnStatusFailed);
+      return result.Succeeded();
+    }
+
     // No need to check "process" for validity as eCommandRequiresProcess
     // ensures it is valid
     Process *process = m_exe_ctx.GetProcessPtr();
@@ -1227,8 +1273,37 @@ class CommandObjectProcessStatus : public CommandObjectParsed {
     process->GetStatus(strm);
     process->GetThreadStatus(strm, only_threads_with_stop_reason, start_frame,
                              num_frames, num_frames_with_source, stop_format);
+
+    if (m_options.m_verbose) {
+      PlatformSP platform_sp = process->GetTarget().GetPlatform();
+      if (!platform_sp) {
+        result.AppendError("Couldn'retrieve the target's platform");
+        result.SetStatus(eReturnStatusFailed);
+        return result.Succeeded();
+      }
+
+      auto expected_crash_info =
+          platform_sp->FetchExtendedCrashInformation(process->GetTarget());
+
+      if (!expected_crash_info) {
+        result.AppendError(llvm::toString(expected_crash_info.takeError()));
+        result.SetStatus(eReturnStatusFailed);
+        return result.Succeeded();
+      }
+
+      StructuredData::DictionarySP crash_info_sp = *expected_crash_info;
+
+      if (crash_info_sp) {
+        strm.PutCString("Extended Crash Information:\n");
+        crash_info_sp->Dump(strm);
+      }
+    }
+
     return result.Succeeded();
   }
+
+private:
+  CommandOptions m_options;
 };
 
 // CommandObjectProcessHandle

diff  --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index 5512f73e7912..1456630c892f 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -670,6 +670,11 @@ let Command = "process handle" in {
     Desc<"Whether or not the signal should be passed to the process.">;
 }
 
+let Command = "process status" in {
+  def process_status_verbose : Option<"verbose", "v">, Group<1>,
+    Desc<"Show verbose process status including extended crash information.">;
+}
+
 let Command = "script import" in {
   def script_import_allow_reload : Option<"allow-reload", "r">, Group<1>,
     Desc<"Allow the script to be loaded even if it was already loaded before. "

diff  --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
index 2a745f78ce71..3790bd09a1f1 100644
--- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
+++ b/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,129 @@ PlatformDarwin::ParseVersionBuildDir(llvm::StringRef dir) {
   return std::make_tuple(version, build);
 }
 
+llvm::Expected<StructuredData::DictionarySP>
+PlatformDarwin::FetchExtendedCrashInformation(lldb_private::Target &target) {
+  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+  StructuredData::ArraySP annotations = ExtractCrashInfoAnnotations(target);
+
+  if (!annotations || !annotations->GetSize()) {
+    LLDB_LOG(log, "Couldn't extract crash information annotations");
+    return nullptr;
+  }
+
+  StructuredData::DictionarySP extended_crash_info =
+      std::make_shared<StructuredData::Dictionary>();
+
+  extended_crash_info->AddItem("crash-info annotations", annotations);
+
+  return extended_crash_info;
+}
+
+StructuredData::ArraySP
+PlatformDarwin::ExtractCrashInfoAnnotations(Target &target) {
+  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+  ConstString section_name("__crash_info");
+  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();
+
+    // The DYDL module is skipped since it's always loaded when running the
+    // binary.
+    if (module_name == "/usr/lib/dyld")
+      continue;
+
+    if (!sections) {
+      LLDB_LOG(log, "Module {0} doesn't have any section!", module_name);
+      continue;
+    }
+
+    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 = {

diff  --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h
index 28e458dd4ff5..302dac413423 100644
--- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h
+++ b/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,38 @@ class PlatformDarwin : public PlatformPOSIX {
     iPhoneOS,
   };
 
+  llvm::Expected<lldb_private::StructuredData::DictionarySP>
+  FetchExtendedCrashInformation(lldb_private::Target &target) override;
+
 protected:
+  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.
+  ///
+  /// \return
+  ///     A  structured data array containing at each entry in each entry, the
+  ///     module spec, its UUID, the crash messages and the abort cause.
+  ///     \b nullptr if process has no crash information annotations.
+  lldb_private::StructuredData::ArraySP
+  ExtractCrashInfoAnnotations(lldb_private::Target &target);
+
   void ReadLibdispatchOffsetsAddress(lldb_private::Process *process);
 
   void ReadLibdispatchOffsets(lldb_private::Process *process);

diff  --git a/lldb/test/API/functionalities/process_crash_info/Makefile b/lldb/test/API/functionalities/process_crash_info/Makefile
new file mode 100644
index 000000000000..692ba1732285
--- /dev/null
+++ b/lldb/test/API/functionalities/process_crash_info/Makefile
@@ -0,0 +1,4 @@
+C_SOURCES := main.c
+
+include Makefile.rules
+

diff  --git a/lldb/test/API/functionalities/process_crash_info/TestProcessCrashInfo.py b/lldb/test/API/functionalities/process_crash_info/TestProcessCrashInfo.py
new file mode 100644
index 000000000000..979efe0a160d
--- /dev/null
+++ b/lldb/test/API/functionalities/process_crash_info/TestProcessCrashInfo.py
@@ -0,0 +1,97 @@
+"""
+Test lldb 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")
+        self.source = "main.c"
+        self.line = 3
+
+    def tearDown(self):
+        self.runCmd("settings clear auto-confirm")
+        TestBase.tearDown(self)
+
+    @skipUnlessDarwin
+    def test_cli(self):
+        """Test that `process status --verbose` fetches the extended crash
+        information dictionnary 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('process status --verbose',
+                    patterns=["\"message\".*pointer being freed was not allocated"])
+
+
+    @skipUnlessDarwin
+    def test_api(self):
+        """Test that lldb can fetch a crashed process' extended crash information
+        dictionnary 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())
+
+        stream = lldb.SBStream()
+        self.assertTrue(stream)
+
+        crash_info = target.GetExtendedCrashInformation()
+
+        error = crash_info.GetAsJSON(stream)
+
+        self.assertTrue(error.Success())
+
+        self.assertTrue(crash_info.IsValid())
+
+        self.assertIn("pointer being freed was not allocated", stream.GetData())
+
+    @skipUnlessDarwin
+    def test_before_launch(self):
+        """Test that lldb doesn't fetch the extended crash information
+        dictionnary from if the process wasn't launched yet."""
+        self.build()
+        target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+        self.assertTrue(target, VALID_TARGET)
+
+        stream = lldb.SBStream()
+        self.assertTrue(stream)
+
+        crash_info = target.GetExtendedCrashInformation()
+
+        error = crash_info.GetAsJSON(stream)
+        self.assertFalse(error.Success())
+        self.assertIn("No structured data.", error.GetCString())
+
+    @skipUnlessDarwin
+    def test_on_sane_process(self):
+        """Test that lldb doesn't fetch the extended crash information
+        dictionnary from a 'sane' stopped process."""
+        self.build()
+        target, _, _, _ = lldbutil.run_to_line_breakpoint(self, lldb.SBFileSpec(self.source),
+                                        self.line)
+
+        stream = lldb.SBStream()
+        self.assertTrue(stream)
+
+        crash_info = target.GetExtendedCrashInformation()
+
+        error = crash_info.GetAsJSON(stream)
+        self.assertFalse(error.Success())
+        self.assertIn("No structured data.", error.GetCString())

diff  --git a/lldb/test/API/functionalities/process_crash_info/main.c b/lldb/test/API/functionalities/process_crash_info/main.c
new file mode 100644
index 000000000000..49733512898b
--- /dev/null
+++ b/lldb/test/API/functionalities/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;
+}


        


More information about the lldb-commits mailing list