Skip to content

Commit 64d78d8

Browse files
zhytyTom Yang
andauthored
Add target modules dump separate-debug-info (#66035)
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 Err Dwo Path ------------------ --- ----------------------------------------- 0x9a429da5abb6faae /home/toyang/workspace/scratch-dwo/a-main.dwo 0xbcc129959e76ff33 /home/toyang/workspace/scratch-dwo/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 Err Dwo Path ------------------ --- ----------------------------------------- 0x9a429da5abb6faae E unable to locate .dwo debug file "/home/toyang/workspace/scratch-dwo/b.out-main.dwo" for skeleton DIE 0x0000000000000014 0xbcc129959e76ff33 E unable to locate .dwo debug file "/home/toyang/workspace/scratch-dwo/b.out-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 Err 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: ``` (lldb) image dump separate-debug-info Symbol file: /Users/toyang/workspace/scratch/a.out Type: "oso" Mod Time Err 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 ``` --------- Co-authored-by: Tom Yang <[email protected]>
1 parent dd0f642 commit 64d78d8

File tree

18 files changed

+667
-5
lines changed

18 files changed

+667
-5
lines changed

lldb/include/lldb/Symbol/SymbolFile.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "lldb/Symbol/TypeList.h"
2323
#include "lldb/Symbol/TypeSystem.h"
2424
#include "lldb/Target/Statistics.h"
25+
#include "lldb/Utility/StructuredData.h"
2526
#include "lldb/Utility/XcodeSDK.h"
2627
#include "lldb/lldb-private.h"
2728
#include "llvm/ADT/DenseSet.h"
@@ -434,6 +435,18 @@ class SymbolFile : public PluginInterface {
434435
virtual bool GetDebugInfoHadFrameVariableErrors() const = 0;
435436
virtual void SetDebugInfoHadFrameVariableErrors() = 0;
436437

438+
/// Return true if separate debug info files are supported and this function
439+
/// succeeded, false otherwise.
440+
///
441+
/// \param[out] d
442+
/// If this function succeeded, then this will be a dictionary that
443+
/// contains the keys "type", "symfile", and "separate-debug-info-files".
444+
/// "type" can be used to assume the structure of each object in
445+
/// "separate-debug-info-files".
446+
virtual bool GetSeparateDebugInfo(StructuredData::Dictionary &d) {
447+
return false;
448+
};
449+
437450
virtual lldb::TypeSP
438451
MakeType(lldb::user_id_t uid, ConstString name,
439452
std::optional<uint64_t> byte_size, SymbolContextScope *context,

lldb/source/Commands/CommandObjectTarget.cpp

Lines changed: 259 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
#include "lldb/Utility/FileSpec.h"
5353
#include "lldb/Utility/LLDBLog.h"
5454
#include "lldb/Utility/State.h"
55+
#include "lldb/Utility/StructuredData.h"
5556
#include "lldb/Utility/Timer.h"
5657
#include "lldb/lldb-enumerations.h"
5758
#include "lldb/lldb-private-enumerations.h"
@@ -61,6 +62,7 @@
6162
#include "clang/Frontend/CompilerInvocation.h"
6263
#include "clang/Frontend/FrontendActions.h"
6364
#include "llvm/ADT/ScopeExit.h"
65+
#include "llvm/ADT/StringRef.h"
6466
#include "llvm/Support/FileSystem.h"
6567
#include "llvm/Support/FormatAdapters.h"
6668

@@ -1462,6 +1464,87 @@ static bool DumpModuleSymbolFile(Stream &strm, Module *module) {
14621464
return false;
14631465
}
14641466

1467+
static bool GetSeparateDebugInfoList(StructuredData::Array &list,
1468+
Module *module) {
1469+
if (module) {
1470+
if (SymbolFile *symbol_file = module->GetSymbolFile(/*can_create=*/true)) {
1471+
StructuredData::Dictionary d;
1472+
if (symbol_file->GetSeparateDebugInfo(d)) {
1473+
list.AddItem(
1474+
std::make_shared<StructuredData::Dictionary>(std::move(d)));
1475+
return true;
1476+
}
1477+
}
1478+
}
1479+
return false;
1480+
}
1481+
1482+
static void DumpDwoFilesTable(Stream &strm,
1483+
StructuredData::Array &dwo_listings) {
1484+
strm.PutCString("Dwo ID Err Dwo Path");
1485+
strm.EOL();
1486+
strm.PutCString(
1487+
"------------------ --- -----------------------------------------");
1488+
strm.EOL();
1489+
dwo_listings.ForEach([&strm](StructuredData::Object *dwo) {
1490+
StructuredData::Dictionary *dict = dwo->GetAsDictionary();
1491+
if (!dict)
1492+
return false;
1493+
1494+
uint64_t dwo_id;
1495+
if (dict->GetValueForKeyAsInteger("dwo_id", dwo_id))
1496+
strm.Printf("0x%16.16" PRIx64 " ", dwo_id);
1497+
else
1498+
strm.Printf("0x???????????????? ");
1499+
1500+
llvm::StringRef error;
1501+
if (dict->GetValueForKeyAsString("error", error))
1502+
strm << "E " << error;
1503+
else {
1504+
llvm::StringRef resolved_dwo_path;
1505+
if (dict->GetValueForKeyAsString("resolved_dwo_path",
1506+
resolved_dwo_path)) {
1507+
strm << " " << resolved_dwo_path;
1508+
if (resolved_dwo_path.ends_with(".dwp")) {
1509+
llvm::StringRef dwo_name;
1510+
if (dict->GetValueForKeyAsString("dwo_name", dwo_name))
1511+
strm << "(" << dwo_name << ")";
1512+
}
1513+
}
1514+
}
1515+
strm.EOL();
1516+
return true;
1517+
});
1518+
}
1519+
1520+
static void DumpOsoFilesTable(Stream &strm,
1521+
StructuredData::Array &oso_listings) {
1522+
strm.PutCString("Mod Time Err Oso Path");
1523+
strm.EOL();
1524+
strm.PutCString("------------------ --- ---------------------");
1525+
strm.EOL();
1526+
oso_listings.ForEach([&strm](StructuredData::Object *oso) {
1527+
StructuredData::Dictionary *dict = oso->GetAsDictionary();
1528+
if (!dict)
1529+
return false;
1530+
1531+
uint32_t oso_mod_time;
1532+
if (dict->GetValueForKeyAsInteger("oso_mod_time", oso_mod_time))
1533+
strm.Printf("0x%16.16" PRIx32 " ", oso_mod_time);
1534+
1535+
llvm::StringRef error;
1536+
if (dict->GetValueForKeyAsString("error", error))
1537+
strm << "E " << error;
1538+
else {
1539+
llvm::StringRef oso_path;
1540+
if (dict->GetValueForKeyAsString("oso_path", oso_path))
1541+
strm << " " << oso_path;
1542+
}
1543+
strm.EOL();
1544+
return true;
1545+
});
1546+
}
1547+
14651548
static void DumpAddress(ExecutionContextScope *exe_scope,
14661549
const Address &so_addr, bool verbose, bool all_ranges,
14671550
Stream &strm) {
@@ -2462,6 +2545,176 @@ class CommandObjectTargetModulesDumpLineTable
24622545
CommandOptions m_options;
24632546
};
24642547

2548+
#pragma mark CommandObjectTargetModulesDumpSeparateDebugInfoFiles
2549+
#define LLDB_OPTIONS_target_modules_dump_separate_debug_info
2550+
#include "CommandOptions.inc"
2551+
2552+
// Image debug separate debug info dumping command
2553+
2554+
class CommandObjectTargetModulesDumpSeparateDebugInfoFiles
2555+
: public CommandObjectTargetModulesModuleAutoComplete {
2556+
public:
2557+
CommandObjectTargetModulesDumpSeparateDebugInfoFiles(
2558+
CommandInterpreter &interpreter)
2559+
: CommandObjectTargetModulesModuleAutoComplete(
2560+
interpreter, "target modules dump separate-debug-info",
2561+
"List the separate debug info symbol files for one or more target "
2562+
"modules.",
2563+
nullptr, eCommandRequiresTarget) {}
2564+
2565+
~CommandObjectTargetModulesDumpSeparateDebugInfoFiles() override = default;
2566+
2567+
Options *GetOptions() override { return &m_options; }
2568+
2569+
class CommandOptions : public Options {
2570+
public:
2571+
CommandOptions() = default;
2572+
2573+
~CommandOptions() override = default;
2574+
2575+
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
2576+
ExecutionContext *execution_context) override {
2577+
Status error;
2578+
const int short_option = m_getopt_table[option_idx].val;
2579+
2580+
switch (short_option) {
2581+
case 'j':
2582+
m_json.SetCurrentValue(true);
2583+
m_json.SetOptionWasSet();
2584+
break;
2585+
2586+
default:
2587+
llvm_unreachable("Unimplemented option");
2588+
}
2589+
return error;
2590+
}
2591+
2592+
void OptionParsingStarting(ExecutionContext *execution_context) override {
2593+
m_json.Clear();
2594+
}
2595+
2596+
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
2597+
return llvm::ArrayRef(g_target_modules_dump_separate_debug_info_options);
2598+
}
2599+
2600+
OptionValueBoolean m_json = false;
2601+
};
2602+
2603+
protected:
2604+
bool DoExecute(Args &command, CommandReturnObject &result) override {
2605+
Target &target = GetSelectedTarget();
2606+
uint32_t num_dumped = 0;
2607+
2608+
uint32_t addr_byte_size = target.GetArchitecture().GetAddressByteSize();
2609+
result.GetOutputStream().SetAddressByteSize(addr_byte_size);
2610+
result.GetErrorStream().SetAddressByteSize(addr_byte_size);
2611+
2612+
StructuredData::Array separate_debug_info_lists_by_module;
2613+
if (command.GetArgumentCount() == 0) {
2614+
// Dump all sections for all modules images
2615+
const ModuleList &target_modules = target.GetImages();
2616+
std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
2617+
const size_t num_modules = target_modules.GetSize();
2618+
if (num_modules == 0) {
2619+
result.AppendError("the target has no associated executable images");
2620+
return false;
2621+
}
2622+
for (ModuleSP module_sp : target_modules.ModulesNoLocking()) {
2623+
if (INTERRUPT_REQUESTED(
2624+
GetDebugger(),
2625+
"Interrupted in dumping all "
2626+
"separate debug info with {0} of {1} modules dumped",
2627+
num_dumped, num_modules))
2628+
break;
2629+
2630+
if (GetSeparateDebugInfoList(separate_debug_info_lists_by_module,
2631+
module_sp.get()))
2632+
num_dumped++;
2633+
}
2634+
} else {
2635+
// Dump specified images (by basename or fullpath)
2636+
const char *arg_cstr;
2637+
for (int arg_idx = 0;
2638+
(arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr;
2639+
++arg_idx) {
2640+
ModuleList module_list;
2641+
const size_t num_matches =
2642+
FindModulesByName(&target, arg_cstr, module_list, true);
2643+
if (num_matches > 0) {
2644+
for (size_t i = 0; i < num_matches; ++i) {
2645+
if (INTERRUPT_REQUESTED(GetDebugger(),
2646+
"Interrupted dumping {0} "
2647+
"of {1} requested modules",
2648+
i, num_matches))
2649+
break;
2650+
Module *module = module_list.GetModulePointerAtIndex(i);
2651+
if (GetSeparateDebugInfoList(separate_debug_info_lists_by_module,
2652+
module))
2653+
num_dumped++;
2654+
}
2655+
} else
2656+
result.AppendWarningWithFormat(
2657+
"Unable to find an image that matches '%s'.\n", arg_cstr);
2658+
}
2659+
}
2660+
2661+
if (num_dumped > 0) {
2662+
Stream &strm = result.GetOutputStream();
2663+
if (m_options.m_json) {
2664+
separate_debug_info_lists_by_module.Dump(strm,
2665+
/*pretty_print=*/true);
2666+
} else {
2667+
// List the debug info files in human readable form.
2668+
separate_debug_info_lists_by_module.ForEach(
2669+
[&result, &strm](StructuredData::Object *obj) {
2670+
if (!obj) {
2671+
return false;
2672+
}
2673+
2674+
// Each item in `separate_debug_info_lists_by_module` should be a
2675+
// valid structured data dictionary.
2676+
StructuredData::Dictionary *separate_debug_info_list =
2677+
obj->GetAsDictionary();
2678+
if (!separate_debug_info_list) {
2679+
return false;
2680+
}
2681+
2682+
llvm::StringRef type;
2683+
llvm::StringRef symfile;
2684+
StructuredData::Array *files;
2685+
assert(separate_debug_info_list->GetValueForKeyAsString("type",
2686+
type));
2687+
assert(separate_debug_info_list->GetValueForKeyAsString("symfile",
2688+
symfile));
2689+
assert(separate_debug_info_list->GetValueForKeyAsArray(
2690+
"separate-debug-info-files", files));
2691+
2692+
strm << "Symbol file: " << symfile;
2693+
strm.EOL();
2694+
strm << "Type: \"" << type << "\"";
2695+
strm.EOL();
2696+
if (type == "dwo") {
2697+
DumpDwoFilesTable(strm, *files);
2698+
} else if (type == "oso") {
2699+
DumpOsoFilesTable(strm, *files);
2700+
} else {
2701+
result.AppendWarningWithFormat(
2702+
"Found unsupported debug info type '%s'.\n",
2703+
type.str().c_str());
2704+
}
2705+
return true;
2706+
});
2707+
}
2708+
result.SetStatus(eReturnStatusSuccessFinishResult);
2709+
} else {
2710+
result.AppendError("no matching executable images found");
2711+
}
2712+
return result.Succeeded();
2713+
}
2714+
2715+
CommandOptions m_options;
2716+
};
2717+
24652718
#pragma mark CommandObjectTargetModulesDump
24662719

24672720
// Dump multi-word command for target modules
@@ -2475,7 +2728,8 @@ class CommandObjectTargetModulesDump : public CommandObjectMultiword {
24752728
"Commands for dumping information about one or more target "
24762729
"modules.",
24772730
"target modules dump "
2478-
"[objfile|symtab|sections|ast|symfile|line-table|pcm-info] "
2731+
"[objfile|symtab|sections|ast|symfile|line-table|pcm-info|separate-"
2732+
"debug-info] "
24792733
"[<file1> <file2> ...]") {
24802734
LoadSubCommand("objfile",
24812735
CommandObjectSP(
@@ -2499,6 +2753,10 @@ class CommandObjectTargetModulesDump : public CommandObjectMultiword {
24992753
"pcm-info",
25002754
CommandObjectSP(
25012755
new CommandObjectTargetModulesDumpClangPCMInfo(interpreter)));
2756+
LoadSubCommand("separate-debug-info",
2757+
CommandObjectSP(
2758+
new CommandObjectTargetModulesDumpSeparateDebugInfoFiles(
2759+
interpreter)));
25022760
}
25032761

25042762
~CommandObjectTargetModulesDump() override = default;

lldb/source/Commands/Options.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ let Command = "target modules dump symtab" in {
88
Desc<"Do not demangle symbol names before showing them.">;
99
}
1010

11+
let Command = "target modules dump separate debug info" in {
12+
def tm_json : Option<"json", "j">, Group<1>,
13+
Desc<"Output the details in JSON format.">;
14+
}
15+
1116
let Command = "help" in {
1217
def help_hide_aliases : Option<"hide-aliases", "a">,
1318
Desc<"Hide aliases in the command list.">;

0 commit comments

Comments
 (0)