mib updated this revision to Diff 245939.
mib marked 6 inline comments as done.
mib edited the summary of this revision.
mib added a comment.
Addressed Pavel's comments.
Add "negative" tests.
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/bindings/interface/SBTarget.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/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
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
Index: lldb/test/API/functionalities/process_crash_info/main.c
===================================================================
--- /dev/null
+++ 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;
+}
Index: lldb/test/API/functionalities/process_crash_info/TestProcessCrashInfo.py
===================================================================
--- /dev/null
+++ 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_on_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())
Index: lldb/test/API/functionalities/process_crash_info/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/process_crash_info/Makefile
@@ -0,0 +1,4 @@
+C_SOURCES := main.c
+
+include Makefile.rules
+
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,38 @@
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);
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,129 @@
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 = {
Index: lldb/source/Commands/Options.td
===================================================================
--- lldb/source/Commands/Options.td
+++ lldb/source/Commands/Options.td
@@ -670,6 +670,11 @@
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. "
Index: lldb/source/Commands/CommandObjectProcess.cpp
===================================================================
--- lldb/source/Commands/CommandObjectProcess.cpp
+++ lldb/source/Commands/CommandObjectProcess.cpp
@@ -1201,6 +1201,8 @@
// CommandObjectProcessStatus
#pragma mark CommandObjectProcessStatus
+#define LLDB_OPTIONS_process_status
+#include "CommandOptions.inc"
class CommandObjectProcessStatus : public CommandObjectParsed {
public:
@@ -1209,13 +1211,57 @@
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 @@
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
Index: lldb/source/API/SBTarget.cpp
===================================================================
--- lldb/source/API/SBTarget.cpp
+++ lldb/source/API/SBTarget.cpp
@@ -2388,6 +2388,30 @@
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 @@
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 &));
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"
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,26 @@
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 different 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
Index: lldb/include/lldb/API/SBTarget.h
===================================================================
--- lldb/include/lldb/API/SBTarget.h
+++ lldb/include/lldb/API/SBTarget.h
@@ -819,6 +819,8 @@
void SetLaunchInfo(const lldb::SBLaunchInfo &launch_info);
+ SBStructuredData GetExtendedCrashInformation();
+
protected:
friend class SBAddress;
friend class SBBlock;
@@ -829,6 +831,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>
Index: lldb/bindings/interface/SBTarget.i
===================================================================
--- lldb/bindings/interface/SBTarget.i
+++ lldb/bindings/interface/SBTarget.i
@@ -949,6 +949,12 @@
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();
Index: lldb/bindings/interface/SBPlatform.i
===================================================================
--- lldb/bindings/interface/SBPlatform.i
+++ lldb/bindings/interface/SBPlatform.i
@@ -191,7 +191,6 @@
lldb::SBUnixSignals
GetUnixSignals();
-
};
} // namespace lldb
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits