[Lldb-commits] [lldb] Add `target modules dump separate-debug-info` (PR #66035)
Tom Yang via lldb-commits
lldb-commits at lists.llvm.org
Thu Sep 14 23:50:10 PDT 2023
https://github.com/zhyty updated https://github.com/llvm/llvm-project/pull/66035
>From 9a52ac5193af2a8ddca2a5d922684935b043d0ef Mon Sep 17 00:00:00 2001
From: Tom Yang <toyang at fb.com>
Date: Mon, 11 Sep 2023 17:17:13 -0700
Subject: [PATCH] Add `target modules dump separate-debug-info`
Summary:
Add a new command
```
target modules dump separate-debug-info [-j] [<filename> [<filename> [...]]]
```
or
```
image dump separate-debug-info [-j] [<filename> [<filename> [...]]]
```
(since `image` is an alias for `target modules`).
This lists the separate debug info files and their current status (loaded or not loaded) for the specified modules. This diff implements this command for mach-O files with OSO and ELF files with dwo.
Example dwo:
```
(lldb) image dump separate-debug-info
Symbol file: /home/toyang/workspace/dwo-scratch/a.out
Type: "dwo"
Dwo ID Dwo Path
------------------ -----------------------------------------
0x9a429da5abb6faae /home/toyang/workspace/dwo-scratch/a-main.dwo
0xbcc129959e76ff33 /home/toyang/workspace/dwo-scratch/a-foo.dwo
(lldb) image dump separate-debug-info -j
[
{
"separate-debug-info-files": [
{
"comp_dir": "/home/toyang/workspace/dwo-scratch",
"dwo_id": 11115620165179865774,
"dwo_name": "a-main.dwo",
"loaded": true,
"resolved_dwo_path": "/home/toyang/workspace/dwo-scratch/a-main.dwo"
},
{
"comp_dir": "/home/toyang/workspace/dwo-scratch",
"dwo_id": 13601198072221073203,
"dwo_name": "a-foo.dwo",
"loaded": true,
"resolved_dwo_path": "/home/toyang/workspace/dwo-scratch/a-foo.dwo"
}
],
"symfile": "/home/toyang/workspace/dwo-scratch/a.out",
"type": "dwo"
}
]
```
Example dwo with missing dwo:
```
(lldb) image dump separate-debug-info
Symbol file: /home/toyang/workspace/dwo-scratch/a.out
Type: "dwo"
Dwo ID Dwo Path
------------------ -----------------------------------------
0x9a429da5abb6faae error: unable to locate .dwo debug file "/home/toyang/workspace/dwo-scratch/a-main.dwo" for skeleton DIE 0x0000000000000014
0xbcc129959e76ff33 error: unable to locate .dwo debug file "/home/toyang/workspace/dwo-scratch/a-foo.dwo" for skeleton DIE 0x000000000000003c
(lldb) image dump separate-debug-info -j
[
{
"separate-debug-info-files": [
{
"comp_dir": "/home/toyang/workspace/dwo-scratch",
"dwo_id": 11115620165179865774,
"dwo_name": "a-main.dwo",
"error": "unable to locate .dwo debug file \"/home/toyang/workspace/dwo-scratch/a-main.dwo\" for skeleton DIE 0x0000000000000014",
"loaded": false
},
{
"comp_dir": "/home/toyang/workspace/dwo-scratch",
"dwo_id": 13601198072221073203,
"dwo_name": "a-foo.dwo",
"error": "unable to locate .dwo debug file \"/home/toyang/workspace/dwo-scratch/a-foo.dwo\" for skeleton DIE 0x000000000000003c",
"loaded": false
}
],
"symfile": "/home/toyang/workspace/dwo-scratch/a.out",
"type": "dwo"
}
]
```
Example output with dwp:
```
(lldb) image dump separate-debug-info
Symbol file: /home/toyang/workspace/dwo-scratch/a.out
Type: "dwo"
Dwo ID Dwo Path
------------------ -----------------------------------------
0x9a429da5abb6faae /home/toyang/workspace/dwo-scratch/a.out.dwp(a-main.dwo)
0xbcc129959e76ff33 /home/toyang/workspace/dwo-scratch/a.out.dwp(a-foo.dwo)
(lldb) image dump separate-debug-info -j
[
{
"separate-debug-info-files": [
{
"comp_dir": "/home/toyang/workspace/dwo-scratch",
"dwo_id": 11115620165179865774,
"dwo_name": "a-main.dwo",
"loaded": true,
"resolved_dwo_path": "/home/toyang/workspace/dwo-scratch/a.out.dwp"
},
{
"comp_dir": "/home/toyang/workspace/dwo-scratch",
"dwo_id": 13601198072221073203,
"dwo_name": "a-foo.dwo",
"loaded": true,
"resolved_dwo_path": "/home/toyang/workspace/dwo-scratch/a.out.dwp"
}
],
"symfile": "/home/toyang/workspace/dwo-scratch/a.out",
"type": "dwo"
}
]
```
Example oso on my Mac (after manipulating the mod times with `touch`):
```
(lldb) image dump separate-debug-info
Symbol file: /Users/toyang/workspace/scratch/a.out
Type: "oso"
Mod Time Oso Path
------------------ ---------------------
0x0000000064e64868 /Users/toyang/workspace/scratch/foo.a(foo.o)
0x0000000064e64868 /Users/toyang/workspace/scratch/foo.a(main.o)
(lldb) image dump separate-debug-info -j
[
{
"separate-debug-info-files": [
{
"loaded": true,
"oso_mod_time": 1692813416,
"oso_path": "/Users/toyang/workspace/scratch/foo.a(foo.o)",
"so_file": "/Users/toyang/workspace/scratch/foo.cpp"
},
{
"loaded": true,
"oso_mod_time": 1692813416,
"oso_path": "/Users/toyang/workspace/scratch/foo.a(main.o)",
"so_file": "/Users/toyang/workspace/scratch/main.cpp"
}
],
"symfile": "/Users/toyang/workspace/scratch/a.out",
"type": "oso"
}
]
```
Test Plan:
Tested on Mac OS and Linux.
```
lldb-dotest -p TestDumpDwo
lldb-dotest -p TestDumpOso
```
Reviewers:
Subscribers:
Tasks:
Tags:
---
lldb/include/lldb/Symbol/SymbolFile.h | 13 +
lldb/source/Commands/CommandObjectTarget.cpp | 260 +++++++++++++++++-
lldb/source/Commands/Options.td | 5 +
.../SymbolFile/DWARF/SymbolFileDWARF.cpp | 71 ++++-
.../SymbolFile/DWARF/SymbolFileDWARF.h | 5 +
.../DWARF/SymbolFileDWARFDebugMap.cpp | 39 ++-
.../DWARF/SymbolFileDWARFDebugMap.h | 5 +
lldb/source/Symbol/SymbolFile.cpp | 1 +
.../dump-separate-debug-info/dwo/Makefile | 4 +
.../dwo/TestDumpDwo.py | 122 ++++++++
.../dump-separate-debug-info/dwo/foo.cpp | 3 +
.../target/dump-separate-debug-info/dwo/foo.h | 6 +
.../dump-separate-debug-info/dwo/main.cpp | 3 +
.../dump-separate-debug-info/oso/Makefile | 3 +
.../oso/TestDumpOso.py | 120 ++++++++
.../dump-separate-debug-info/oso/foo.cpp | 3 +
.../target/dump-separate-debug-info/oso/foo.h | 6 +
.../dump-separate-debug-info/oso/main.cpp | 3 +
18 files changed, 667 insertions(+), 5 deletions(-)
create mode 100644 lldb/test/API/commands/target/dump-separate-debug-info/dwo/Makefile
create mode 100644 lldb/test/API/commands/target/dump-separate-debug-info/dwo/TestDumpDwo.py
create mode 100644 lldb/test/API/commands/target/dump-separate-debug-info/dwo/foo.cpp
create mode 100644 lldb/test/API/commands/target/dump-separate-debug-info/dwo/foo.h
create mode 100644 lldb/test/API/commands/target/dump-separate-debug-info/dwo/main.cpp
create mode 100644 lldb/test/API/commands/target/dump-separate-debug-info/oso/Makefile
create mode 100644 lldb/test/API/commands/target/dump-separate-debug-info/oso/TestDumpOso.py
create mode 100644 lldb/test/API/commands/target/dump-separate-debug-info/oso/foo.cpp
create mode 100644 lldb/test/API/commands/target/dump-separate-debug-info/oso/foo.h
create mode 100644 lldb/test/API/commands/target/dump-separate-debug-info/oso/main.cpp
diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h
index 8de752816cf94ee..512dd9acb86db61 100644
--- a/lldb/include/lldb/Symbol/SymbolFile.h
+++ b/lldb/include/lldb/Symbol/SymbolFile.h
@@ -22,6 +22,7 @@
#include "lldb/Symbol/TypeList.h"
#include "lldb/Symbol/TypeSystem.h"
#include "lldb/Target/Statistics.h"
+#include "lldb/Utility/StructuredData.h"
#include "lldb/Utility/XcodeSDK.h"
#include "lldb/lldb-private.h"
#include "llvm/ADT/DenseSet.h"
@@ -434,6 +435,18 @@ class SymbolFile : public PluginInterface {
virtual bool GetDebugInfoHadFrameVariableErrors() const = 0;
virtual void SetDebugInfoHadFrameVariableErrors() = 0;
+ /// Return true if separate debug info files are supported and this function
+ /// succeeded, false otherwise.
+ ///
+ /// \param[out] d
+ /// If this function succeeded, then this will be a dictionary that
+ /// contains the keys "type", "symfile", and "separate-debug-info-files".
+ /// "type" can be used to assume the structure of each object in
+ /// "separate-debug-info-files".
+ virtual bool GetSeparateDebugInfo(StructuredData::Dictionary &d) {
+ return false;
+ };
+
virtual lldb::TypeSP
MakeType(lldb::user_id_t uid, ConstString name,
std::optional<uint64_t> byte_size, SymbolContextScope *context,
diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp
index 33330ef0926d61f..82b83a142c6000c 100644
--- a/lldb/source/Commands/CommandObjectTarget.cpp
+++ b/lldb/source/Commands/CommandObjectTarget.cpp
@@ -52,6 +52,7 @@
#include "lldb/Utility/FileSpec.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/State.h"
+#include "lldb/Utility/StructuredData.h"
#include "lldb/Utility/Timer.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-private-enumerations.h"
@@ -61,6 +62,7 @@
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/FrontendActions.h"
#include "llvm/ADT/ScopeExit.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormatAdapters.h"
@@ -1462,6 +1464,87 @@ static bool DumpModuleSymbolFile(Stream &strm, Module *module) {
return false;
}
+static bool GetSeparateDebugInfoList(StructuredData::Array &list,
+ Module *module) {
+ if (module) {
+ if (SymbolFile *symbol_file = module->GetSymbolFile(true)) {
+ StructuredData::Dictionary d;
+ if (symbol_file->GetSeparateDebugInfo(d)) {
+ list.AddItem(
+ std::make_shared<StructuredData::Dictionary>(std::move(d)));
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static void DumpDwoFilesTable(Stream &strm,
+ StructuredData::Array &dwo_listings) {
+ strm.PutCString("Dwo ID Dwo Path");
+ strm.EOL();
+ strm.PutCString(
+ "------------------ -----------------------------------------");
+ strm.EOL();
+ dwo_listings.ForEach([&strm](StructuredData::Object *dwo) {
+ StructuredData::Dictionary *dict = dwo->GetAsDictionary();
+ if (!dict)
+ return false;
+
+ uint64_t dwo_id;
+ if (dict->GetValueForKeyAsInteger("dwo_id", dwo_id))
+ strm.Printf("0x%16.16" PRIx64 " ", dwo_id);
+ else
+ strm.Printf("0x???????????????? ");
+
+ llvm::StringRef error;
+ if (dict->GetValueForKeyAsString("error", error))
+ strm << "error: " << error;
+ else {
+ llvm::StringRef resolved_dwo_path;
+ if (dict->GetValueForKeyAsString("resolved_dwo_path",
+ resolved_dwo_path)) {
+ strm << resolved_dwo_path;
+ if (resolved_dwo_path.ends_with(".dwp")) {
+ llvm::StringRef dwo_name;
+ if (dict->GetValueForKeyAsString("dwo_name", dwo_name))
+ strm << "(" << dwo_name << ")";
+ }
+ }
+ }
+ strm.EOL();
+ return true;
+ });
+}
+
+static void DumpOsoFilesTable(Stream &strm,
+ StructuredData::Array &oso_listings) {
+ strm.PutCString("Mod Time Oso Path");
+ strm.EOL();
+ strm.PutCString("------------------ ---------------------");
+ strm.EOL();
+ oso_listings.ForEach([&strm](StructuredData::Object *oso) {
+ StructuredData::Dictionary *dict = oso->GetAsDictionary();
+ if (!dict)
+ return false;
+
+ uint32_t oso_mod_time;
+ if (dict->GetValueForKeyAsInteger("oso_mod_time", oso_mod_time))
+ strm.Printf("0x%16.16" PRIx32 " ", oso_mod_time);
+
+ llvm::StringRef error;
+ if (dict->GetValueForKeyAsString("error", error))
+ strm << "error: " << error;
+ else {
+ llvm::StringRef oso_path;
+ if (dict->GetValueForKeyAsString("oso_path", oso_path))
+ strm << oso_path;
+ }
+ strm.EOL();
+ return true;
+ });
+}
+
static void DumpAddress(ExecutionContextScope *exe_scope,
const Address &so_addr, bool verbose, bool all_ranges,
Stream &strm) {
@@ -2462,6 +2545,176 @@ class CommandObjectTargetModulesDumpLineTable
CommandOptions m_options;
};
+#pragma mark CommandObjectTargetModulesDumpSeparateDebugInfoFiles
+#define LLDB_OPTIONS_target_modules_dump_separate_debug_info
+#include "CommandOptions.inc"
+
+// Image debug separate debug info dumping command
+
+class CommandObjectTargetModulesDumpSeparateDebugInfoFiles
+ : public CommandObjectTargetModulesModuleAutoComplete {
+public:
+ CommandObjectTargetModulesDumpSeparateDebugInfoFiles(
+ CommandInterpreter &interpreter)
+ : CommandObjectTargetModulesModuleAutoComplete(
+ interpreter, "target modules dump separate-debug-info",
+ "List the separate debug info symbol files for one or more target "
+ "modules.",
+ nullptr, eCommandRequiresTarget) {}
+
+ ~CommandObjectTargetModulesDumpSeparateDebugInfoFiles() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'j':
+ m_json.SetCurrentValue(true);
+ m_json.SetOptionWasSet();
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_json.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::ArrayRef(g_target_modules_dump_separate_debug_info_options);
+ }
+
+ OptionValueBoolean m_json = false;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target = GetSelectedTarget();
+ uint32_t num_dumped = 0;
+
+ uint32_t addr_byte_size = target.GetArchitecture().GetAddressByteSize();
+ result.GetOutputStream().SetAddressByteSize(addr_byte_size);
+ result.GetErrorStream().SetAddressByteSize(addr_byte_size);
+
+ StructuredData::Array separate_debug_info_lists_by_module;
+ if (command.GetArgumentCount() == 0) {
+ // Dump all sections for all modules images
+ const ModuleList &target_modules = target.GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
+ const size_t num_modules = target_modules.GetSize();
+ if (num_modules == 0) {
+ result.AppendError("the target has no associated executable images");
+ return false;
+ }
+ for (ModuleSP module_sp : target_modules.ModulesNoLocking()) {
+ if (INTERRUPT_REQUESTED(
+ GetDebugger(),
+ "Interrupted in dumping all "
+ "separate debug info with {0} of {1} modules dumped",
+ num_dumped, num_modules))
+ break;
+
+ if (GetSeparateDebugInfoList(separate_debug_info_lists_by_module,
+ module_sp.get()))
+ num_dumped++;
+ }
+ } else {
+ // Dump specified images (by basename or fullpath)
+ const char *arg_cstr;
+ for (int arg_idx = 0;
+ (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr;
+ ++arg_idx) {
+ ModuleList module_list;
+ const size_t num_matches =
+ FindModulesByName(&target, arg_cstr, module_list, true);
+ if (num_matches > 0) {
+ for (size_t i = 0; i < num_matches; ++i) {
+ if (INTERRUPT_REQUESTED(GetDebugger(),
+ "Interrupted dumping {0} "
+ "of {1} requested modules",
+ i, num_matches))
+ break;
+ Module *module = module_list.GetModulePointerAtIndex(i);
+ if (GetSeparateDebugInfoList(separate_debug_info_lists_by_module,
+ module))
+ num_dumped++;
+ }
+ } else
+ result.AppendWarningWithFormat(
+ "Unable to find an image that matches '%s'.\n", arg_cstr);
+ }
+ }
+
+ if (num_dumped > 0) {
+ Stream &strm = result.GetOutputStream();
+ if (m_options.m_json) {
+ separate_debug_info_lists_by_module.Dump(strm,
+ /*pretty_print=*/true);
+ } else {
+ // List the debug info files in human readable form.
+ separate_debug_info_lists_by_module.ForEach(
+ [&result, &strm](StructuredData::Object *obj) {
+ if (!obj) {
+ return false;
+ }
+
+ // Each item in `separate_debug_info_lists_by_module` should be a
+ // valid structured data dictionary.
+ StructuredData::Dictionary *separate_debug_info_list =
+ obj->GetAsDictionary();
+ if (!separate_debug_info_list) {
+ return false;
+ }
+
+ llvm::StringRef type;
+ llvm::StringRef symfile;
+ StructuredData::Array *files;
+ assert(separate_debug_info_list->GetValueForKeyAsString("type",
+ type));
+ assert(separate_debug_info_list->GetValueForKeyAsString("symfile",
+ symfile));
+ assert(separate_debug_info_list->GetValueForKeyAsArray(
+ "separate-debug-info-files", files));
+
+ strm << "Symbol file: " << symfile;
+ strm.EOL();
+ strm << "Type: \"" << type << "\"";
+ strm.EOL();
+ if (type == "dwo") {
+ DumpDwoFilesTable(strm, *files);
+ } else if (type == "oso") {
+ DumpOsoFilesTable(strm, *files);
+ } else {
+ result.AppendWarningWithFormat(
+ "Found unsupported debug info type '%s'.\n",
+ type.str().c_str());
+ }
+ return true;
+ });
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError("no matching executable images found");
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
#pragma mark CommandObjectTargetModulesDump
// Dump multi-word command for target modules
@@ -2475,7 +2728,8 @@ class CommandObjectTargetModulesDump : public CommandObjectMultiword {
"Commands for dumping information about one or more target "
"modules.",
"target modules dump "
- "[objfile|symtab|sections|ast|symfile|line-table|pcm-info] "
+ "[objfile|symtab|sections|ast|symfile|line-table|pcm-info|separate-"
+ "debug-info] "
"[<file1> <file2> ...]") {
LoadSubCommand("objfile",
CommandObjectSP(
@@ -2499,6 +2753,10 @@ class CommandObjectTargetModulesDump : public CommandObjectMultiword {
"pcm-info",
CommandObjectSP(
new CommandObjectTargetModulesDumpClangPCMInfo(interpreter)));
+ LoadSubCommand("separate-debug-info",
+ CommandObjectSP(
+ new CommandObjectTargetModulesDumpSeparateDebugInfoFiles(
+ interpreter)));
}
~CommandObjectTargetModulesDump() override = default;
diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index 04830b8b990efae..ce00d81db3c5efb 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -8,6 +8,11 @@ let Command = "target modules dump symtab" in {
Desc<"Do not demangle symbol names before showing them.">;
}
+let Command = "target modules dump separate debug info" in {
+ def tm_json : Option<"json", "j">, Group<1>,
+ Desc<"Output the details in JSON format.">;
+}
+
let Command = "help" in {
def help_hide_aliases : Option<"hide-aliases", "a">,
Desc<"Hide aliases in the command list.">;
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 04c729e333a9854..2c8e71eb012f21e 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -10,6 +10,7 @@
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
#include "llvm/Support/Casting.h"
+#include "llvm/Support/Format.h"
#include "llvm/Support/Threading.h"
#include "lldb/Core/Module.h"
@@ -24,6 +25,7 @@
#include "lldb/Utility/RegularExpression.h"
#include "lldb/Utility/Scalar.h"
#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StructuredData.h"
#include "lldb/Utility/Timer.h"
#include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h"
@@ -1740,11 +1742,10 @@ SymbolFileDWARF::GetDwoSymbolFileForCompileUnit(
// it. Or it's absolute.
found = FileSystem::Instance().Exists(dwo_file);
+ const char *comp_dir =
+ cu_die.GetAttributeValueAsString(dwarf_cu, DW_AT_comp_dir, nullptr);
if (!found) {
// It could be a relative path that also uses DW_AT_COMP_DIR.
- const char *comp_dir =
- cu_die.GetAttributeValueAsString(dwarf_cu, DW_AT_comp_dir, nullptr);
-
if (comp_dir) {
dwo_file.SetFile(comp_dir, FileSpec::Style::native);
if (!dwo_file.IsRelative()) {
@@ -4214,6 +4215,70 @@ void SymbolFileDWARF::DumpClangAST(Stream &s) {
clang->Dump(s.AsRawOstream());
}
+bool SymbolFileDWARF::GetSeparateDebugInfo(StructuredData::Dictionary &d) {
+ StructuredData::Array separate_debug_info_files;
+ DWARFDebugInfo &info = DebugInfo();
+ const size_t num_cus = info.GetNumUnits();
+ for (size_t cu_idx = 0; cu_idx < num_cus; cu_idx++) {
+ DWARFUnit *unit = info.GetUnitAtIndex(cu_idx);
+ DWARFCompileUnit *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(unit);
+ if (dwarf_cu == nullptr)
+ continue;
+
+ // Check if this is a DWO unit by checking if it has a DWO ID.
+ // NOTE: it seems that `DWARFUnit::IsDWOUnit` is always false?
+ if (!dwarf_cu->GetDWOId().has_value())
+ continue;
+
+ StructuredData::DictionarySP dwo_data =
+ std::make_shared<StructuredData::Dictionary>();
+ const uint64_t dwo_id = dwarf_cu->GetDWOId().value();
+ dwo_data->AddIntegerItem("dwo_id", dwo_id);
+
+ if (const DWARFBaseDIE die = dwarf_cu->GetUnitDIEOnly()) {
+ const char *dwo_name = GetDWOName(*dwarf_cu, *die.GetDIE());
+ if (dwo_name) {
+ dwo_data->AddStringItem("dwo_name", dwo_name);
+ } else {
+ dwo_data->AddStringItem("error", "missing dwo name");
+ }
+
+ const char *comp_dir = die.GetDIE()->GetAttributeValueAsString(
+ dwarf_cu, DW_AT_comp_dir, nullptr);
+ if (comp_dir) {
+ dwo_data->AddStringItem("comp_dir", comp_dir);
+ }
+ } else {
+ dwo_data->AddStringItem(
+ "error",
+ llvm::formatv("unable to get unit DIE for DWARFUnit at {0:x}",
+ dwarf_cu->GetOffset())
+ .str());
+ }
+
+ // If we have a DWO symbol file, that means we were able to successfully
+ // load it.
+ SymbolFile *dwo_symfile = dwarf_cu->GetDwoSymbolFile();
+ if (dwo_symfile) {
+ dwo_data->AddStringItem(
+ "resolved_dwo_path",
+ dwo_symfile->GetObjectFile()->GetFileSpec().GetPath());
+ } else {
+ dwo_data->AddStringItem("error",
+ dwarf_cu->GetDwoError().AsCString("unknown"));
+ }
+ dwo_data->AddBooleanItem("loaded", dwo_symfile != nullptr);
+ separate_debug_info_files.AddItem(dwo_data);
+ }
+
+ d.AddStringItem("type", "dwo");
+ d.AddStringItem("symfile", GetMainObjectFile()->GetFileSpec().GetPath());
+ d.AddItem("separate-debug-info-files",
+ std::make_shared<StructuredData::Array>(
+ std::move(separate_debug_info_files)));
+ return true;
+}
+
SymbolFileDWARFDebugMap *SymbolFileDWARF::GetDebugMapSymfile() {
if (m_debug_map_symfile == nullptr) {
lldb::ModuleSP module_sp(m_debug_map_module_wp.lock());
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
index 191a5abcf265abd..f75d1ebac9cb8f1 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
@@ -30,6 +30,7 @@
#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/Flags.h"
#include "lldb/Utility/RangeMap.h"
+#include "lldb/Utility/StructuredData.h"
#include "lldb/lldb-private.h"
#include "DWARFContext.h"
@@ -282,6 +283,10 @@ class SymbolFileDWARF : public lldb_private::SymbolFileCommon {
void DumpClangAST(lldb_private::Stream &s) override;
+ /// List separate dwo files.
+ bool
+ GetSeparateDebugInfo(lldb_private::StructuredData::Dictionary &d) override;
+
lldb_private::DWARFContext &GetDWARFContext() { return m_context; }
const std::shared_ptr<SymbolFileDWARFDwo> &GetDwpSymbolFile();
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
index eadedd32e1a4aaf..4e194939814b686 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
@@ -18,8 +18,9 @@
#include "lldb/Host/FileSystem.h"
#include "lldb/Utility/RangeMap.h"
#include "lldb/Utility/RegularExpression.h"
-#include "lldb/Utility/Timer.h"
#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/Utility/Timer.h"
//#define DEBUG_OSO_DMAP // DO NOT CHECKIN WITH THIS NOT COMMENTED OUT
@@ -1271,6 +1272,42 @@ void SymbolFileDWARFDebugMap::DumpClangAST(Stream &s) {
});
}
+bool SymbolFileDWARFDebugMap::GetSeparateDebugInfo(
+ lldb_private::StructuredData::Dictionary &d) {
+ StructuredData::Array separate_debug_info_files;
+ const uint32_t cu_count = GetNumCompileUnits();
+ for (uint32_t cu_idx = 0; cu_idx < cu_count; ++cu_idx) {
+ const auto &info = m_compile_unit_infos[cu_idx];
+ StructuredData::DictionarySP oso_data =
+ std::make_shared<StructuredData::Dictionary>();
+ oso_data->AddStringItem("so_file", info.so_file.GetPath());
+ oso_data->AddStringItem("oso_path", info.oso_path);
+ oso_data->AddIntegerItem("oso_mod_time",
+ (uint32_t)llvm::sys::toTimeT(info.oso_mod_time));
+
+ bool loaded_successfully = false;
+ if (GetModuleByOSOIndex(cu_idx)) {
+ // If we have a valid pointer to the module, we successfully
+ // loaded the oso if there are no load errors.
+ if (!info.oso_load_error.Fail()) {
+ loaded_successfully = true;
+ }
+ }
+ if (!loaded_successfully) {
+ oso_data->AddStringItem("error", info.oso_load_error.AsCString());
+ }
+ oso_data->AddBooleanItem("loaded", loaded_successfully);
+ separate_debug_info_files.AddItem(oso_data);
+ }
+
+ d.AddStringItem("type", "oso");
+ d.AddStringItem("symfile", GetMainObjectFile()->GetFileSpec().GetPath());
+ d.AddItem("separate-debug-info-files",
+ std::make_shared<StructuredData::Array>(
+ std::move(separate_debug_info_files)));
+ return true;
+}
+
lldb::CompUnitSP
SymbolFileDWARFDebugMap::GetCompileUnit(SymbolFileDWARF *oso_dwarf, DWARFCompileUnit &dwarf_cu) {
if (oso_dwarf) {
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
index 881fd4c45ff05a0..0dc4235cf090f7b 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
@@ -19,6 +19,7 @@
#include <vector>
#include "UniqueDWARFASTType.h"
+#include "lldb/Utility/StructuredData.h"
class SymbolFileDWARF;
class DWARFCompileUnit;
@@ -148,6 +149,10 @@ class SymbolFileDWARFDebugMap : public lldb_private::SymbolFileCommon {
void DumpClangAST(lldb_private::Stream &s) override;
+ /// List separate oso files.
+ bool
+ GetSeparateDebugInfo(lldb_private::StructuredData::Dictionary &d) override;
+
// PluginInterface protocol
llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
diff --git a/lldb/source/Symbol/SymbolFile.cpp b/lldb/source/Symbol/SymbolFile.cpp
index b271efd07bfe36f..7dcee8ced0ea11b 100644
--- a/lldb/source/Symbol/SymbolFile.cpp
+++ b/lldb/source/Symbol/SymbolFile.cpp
@@ -18,6 +18,7 @@
#include "lldb/Symbol/VariableList.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StructuredData.h"
#include "lldb/lldb-private.h"
#include <future>
diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/dwo/Makefile b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/Makefile
new file mode 100644
index 000000000000000..3b6d788b2b0130a
--- /dev/null
+++ b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/Makefile
@@ -0,0 +1,4 @@
+CXX_SOURCES := main.cpp foo.cpp
+CFLAGS_EXTRAS := -gsplit-dwarf
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/dwo/TestDumpDwo.py b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/TestDumpDwo.py
new file mode 100644
index 000000000000000..7e19f5208842418
--- /dev/null
+++ b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/TestDumpDwo.py
@@ -0,0 +1,122 @@
+"""
+Test 'target modules dump separate-debug-info' for dwo files.
+"""
+
+import json
+import os
+
+from lldbsuite.test import lldbtest, lldbutil
+from lldbsuite.test.decorators import *
+
+
+class TestDumpDWO(lldbtest.TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def get_dwos_from_json(self):
+ """Returns a dictionary of `symfile` -> {`dwo_name` -> dwo_info object}."""
+ result = {}
+ output = json.loads(self.res.GetOutput())
+ for symfile_entry in output:
+ dwo_dict = {}
+ for dwo_entry in symfile_entry["separate-debug-info-files"]:
+ dwo_dict[dwo_entry["dwo_name"]] = dwo_entry
+ result[symfile_entry["symfile"]] = dwo_dict
+ return result
+
+ @skipIfRemote
+ @skipIfDarwin
+ def test_dwos_loaded_json_output(self):
+ self.build()
+ exe = self.getBuildArtifact("a.out")
+ main_dwo = self.getBuildArtifact("main.dwo")
+ foo_dwo = self.getBuildArtifact("foo.dwo")
+
+ # Make sure dwo files exist
+ self.assertTrue(os.path.exists(main_dwo), f'Make sure "{main_dwo}" file exists')
+ self.assertTrue(os.path.exists(foo_dwo), f'Make sure "{foo_dwo}" file exists')
+
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target, lldbtest.VALID_TARGET)
+
+ self.runCmd("target modules dump separate-debug-info --json")
+
+ # Check the output
+ output = self.get_dwos_from_json()
+ self.assertTrue(output[exe]["main.dwo"]["loaded"])
+ self.assertTrue(output[exe]["foo.dwo"]["loaded"])
+
+ @skipIfRemote
+ @skipIfDarwin
+ def test_dwos_not_loaded_json_output(self):
+ self.build()
+ exe = self.getBuildArtifact("a.out")
+ main_dwo = self.getBuildArtifact("main.dwo")
+ foo_dwo = self.getBuildArtifact("foo.dwo")
+
+ # REMOVE the dwo files
+ os.unlink(main_dwo)
+ os.unlink(foo_dwo)
+
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target, lldbtest.VALID_TARGET)
+
+ self.runCmd("target modules dump separate-debug-info --json")
+
+ # Check the output
+ output = self.get_dwos_from_json()
+ self.assertFalse(output[exe]["main.dwo"]["loaded"])
+ self.assertFalse(output[exe]["foo.dwo"]["loaded"])
+ self.assertIn("error", output[exe]["main.dwo"])
+ self.assertIn("error", output[exe]["foo.dwo"])
+
+ @skipIfRemote
+ @skipIfDarwin
+ def test_dwos_loaded_table_output(self):
+ self.build()
+ exe = self.getBuildArtifact("a.out")
+ main_dwo = self.getBuildArtifact("main.dwo")
+ foo_dwo = self.getBuildArtifact("foo.dwo")
+
+ # Make sure dwo files exist
+ self.assertTrue(os.path.exists(main_dwo), f'Make sure "{main_dwo}" file exists')
+ self.assertTrue(os.path.exists(foo_dwo), f'Make sure "{foo_dwo}" file exists')
+
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target, lldbtest.VALID_TARGET)
+
+ self.expect(
+ "target modules dump separate-debug-info",
+ patterns=[
+ "Symbol file: .*?a\.out",
+ 'Type: "dwo"',
+ "Dwo ID\s+Dwo Path",
+ "0x[a-zA-Z0-9]{16}.*main\.dwo",
+ "0x[a-zA-Z0-9]{16}.*foo\.dwo",
+ ],
+ )
+
+ @skipIfRemote
+ @skipIfDarwin
+ def test_dwos_not_loaded_table_output(self):
+ self.build()
+ exe = self.getBuildArtifact("a.out")
+ main_dwo = self.getBuildArtifact("main.dwo")
+ foo_dwo = self.getBuildArtifact("foo.dwo")
+
+ # REMOVE the dwo files
+ os.unlink(main_dwo)
+ os.unlink(foo_dwo)
+
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target, lldbtest.VALID_TARGET)
+
+ self.expect(
+ "target modules dump separate-debug-info",
+ patterns=[
+ "Symbol file: .*?a\.out",
+ 'Type: "dwo"',
+ "Dwo ID\s+Dwo Path",
+ "0x[a-zA-Z0-9]{16}.*error:.*main\.dwo",
+ "0x[a-zA-Z0-9]{16}.*error:.*foo\.dwo",
+ ],
+ )
diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/dwo/foo.cpp b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/foo.cpp
new file mode 100644
index 000000000000000..28e2b6e768df4e7
--- /dev/null
+++ b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/foo.cpp
@@ -0,0 +1,3 @@
+#include "foo.h"
+
+int foo() { return 1; }
diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/dwo/foo.h b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/foo.h
new file mode 100644
index 000000000000000..4ec598ad513eb91
--- /dev/null
+++ b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/foo.h
@@ -0,0 +1,6 @@
+#ifndef FOO_H
+#define FOO_H
+
+int foo();
+
+#endif
diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/dwo/main.cpp b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/main.cpp
new file mode 100644
index 000000000000000..8087e682432798b
--- /dev/null
+++ b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/main.cpp
@@ -0,0 +1,3 @@
+#include "foo.h"
+
+int main() { return foo(); }
diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/oso/Makefile b/lldb/test/API/commands/target/dump-separate-debug-info/oso/Makefile
new file mode 100644
index 000000000000000..7df22699c57d573
--- /dev/null
+++ b/lldb/test/API/commands/target/dump-separate-debug-info/oso/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp foo.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/oso/TestDumpOso.py b/lldb/test/API/commands/target/dump-separate-debug-info/oso/TestDumpOso.py
new file mode 100644
index 000000000000000..5d1c7d182264ced
--- /dev/null
+++ b/lldb/test/API/commands/target/dump-separate-debug-info/oso/TestDumpOso.py
@@ -0,0 +1,120 @@
+"""
+Test 'target modules dump separate-debug-info' for oso files.
+"""
+
+import json
+import os
+
+from lldbsuite.test import lldbtest, lldbutil
+from lldbsuite.test.decorators import *
+
+
+class TestDumpOso(lldbtest.TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def get_osos_from_json(self):
+ """Returns a dictionary of `symfile` -> {`OSO_PATH` -> oso_info object}."""
+ result = {}
+ output = json.loads(self.res.GetOutput())
+ for symfile_entry in output:
+ oso_dict = {}
+ for oso_entry in symfile_entry["separate-debug-info-files"]:
+ oso_dict[oso_entry["oso_path"]] = oso_entry
+ result[symfile_entry["symfile"]] = oso_dict
+ return result
+
+ @skipIfRemote
+ @skipUnlessDarwin
+ def test_shows_oso_loaded_json_output(self):
+ self.build(debug_info="dwarf")
+ exe = self.getBuildArtifact("a.out")
+ main_o = self.getBuildArtifact("main.o")
+ foo_o = self.getBuildArtifact("foo.o")
+
+ # Make sure o files exist
+ self.assertTrue(os.path.exists(main_o), f'Make sure "{main_o}" file exists')
+ self.assertTrue(os.path.exists(foo_o), f'Make sure "{foo_o}" file exists')
+
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target, lldbtest.VALID_TARGET)
+
+ self.runCmd("target modules dump separate-debug-info --json")
+
+ # Check the output
+ osos = self.get_osos_from_json()
+ self.assertTrue(osos[exe][main_o]["loaded"])
+ self.assertTrue(osos[exe][foo_o]["loaded"])
+
+ @skipIfRemote
+ @skipUnlessDarwin
+ def test_shows_oso_not_loaded_json_output(self):
+ self.build(debug_info="dwarf")
+ exe = self.getBuildArtifact("a.out")
+ main_o = self.getBuildArtifact("main.o")
+ foo_o = self.getBuildArtifact("foo.o")
+
+ # REMOVE the o files
+ os.unlink(main_o)
+ os.unlink(foo_o)
+
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target, lldbtest.VALID_TARGET)
+
+ self.runCmd("target modules dump separate-debug-info --json")
+
+ # Check the output
+ osos = self.get_osos_from_json()
+ self.assertFalse(osos[exe][main_o]["loaded"])
+ self.assertFalse(osos[exe][foo_o]["loaded"])
+
+ @skipIfRemote
+ @skipUnlessDarwin
+ def test_shows_oso_loaded_table_output(self):
+ self.build(debug_info="dwarf")
+ exe = self.getBuildArtifact("a.out")
+ main_o = self.getBuildArtifact("main.o")
+ foo_o = self.getBuildArtifact("foo.o")
+
+ # Make sure o files exist
+ self.assertTrue(os.path.exists(main_o), f'Make sure "{main_o}" file exists')
+ self.assertTrue(os.path.exists(foo_o), f'Make sure "{foo_o}" file exists')
+
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target, lldbtest.VALID_TARGET)
+
+ self.expect(
+ "target modules dump separate-debug-info",
+ patterns=[
+ "Symbol file: .*?a\.out",
+ 'Type: "oso"',
+ "Mod Time\s+Oso Path",
+ "0x[a-zA-Z0-9]{16}.*main\.o",
+ "0x[a-zA-Z0-9]{16}.*foo\.o",
+ ],
+ )
+
+ @skipIfRemote
+ @skipUnlessDarwin
+ def test_shows_oso_not_loaded_table_output(self):
+ self.build(debug_info="dwarf")
+ exe = self.getBuildArtifact("a.out")
+ main_o = self.getBuildArtifact("main.o")
+ foo_o = self.getBuildArtifact("foo.o")
+
+ # REMOVE the o files
+ os.unlink(main_o)
+ os.unlink(foo_o)
+
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target, lldbtest.VALID_TARGET)
+
+ self.expect(
+ "target modules dump separate-debug-info",
+ patterns=[
+ "Symbol file: .*?a\.out",
+ 'Type: "oso"',
+ "Mod Time\s+Oso Path",
+ "0x[a-zA-Z0-9]{16}.*error:.*main\.o",
+ "0x[a-zA-Z0-9]{16}.*error:.*foo\.o",
+ ],
+ )
diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/oso/foo.cpp b/lldb/test/API/commands/target/dump-separate-debug-info/oso/foo.cpp
new file mode 100644
index 000000000000000..28e2b6e768df4e7
--- /dev/null
+++ b/lldb/test/API/commands/target/dump-separate-debug-info/oso/foo.cpp
@@ -0,0 +1,3 @@
+#include "foo.h"
+
+int foo() { return 1; }
diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/oso/foo.h b/lldb/test/API/commands/target/dump-separate-debug-info/oso/foo.h
new file mode 100644
index 000000000000000..4ec598ad513eb91
--- /dev/null
+++ b/lldb/test/API/commands/target/dump-separate-debug-info/oso/foo.h
@@ -0,0 +1,6 @@
+#ifndef FOO_H
+#define FOO_H
+
+int foo();
+
+#endif
diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/oso/main.cpp b/lldb/test/API/commands/target/dump-separate-debug-info/oso/main.cpp
new file mode 100644
index 000000000000000..8087e682432798b
--- /dev/null
+++ b/lldb/test/API/commands/target/dump-separate-debug-info/oso/main.cpp
@@ -0,0 +1,3 @@
+#include "foo.h"
+
+int main() { return foo(); }
More information about the lldb-commits
mailing list