Skip to content

Commit 7bd06c4

Browse files
authored
Defer loading all DWOs by default when dumping separate_debug-info (#146166)
### Summary Currently `target modules dump separate separate-debug-info` automatically loads up all DWO files, even if deferred loading is enabled through debug_names. Then, as expected all DWO files (assuming there is no error loading it), get marked as "loaded". This change adds the option `--force-load-all-debug-info` or `-f` for short to force loading all debug_info up, if it hasn't been loaded yet. Otherwise, it will change default behavior to not load all debug info so that the correct DWO files will show up for each modules as "loaded" or not "loaded", which could be helpful in cases where we want to know which particular DWO files were loaded. ### Testing #### Unit Tests Added additional unit tests `test_dwos_load_json_with_debug_names_default` and `test_dwos_load_json_with_debug_names_force_load_all` to test both default behavior and loading with the new flag `--force-load-all-debug-info`, and changed expected behavior in `test_dwos_loaded_symbols_on_demand`. ``` bin/lldb-dotest -p TestDumpDwo ~/llvm-project/lldb/test/API/commands/target/dump-separate-debug-info/dwo ``` #### Manual Testing Compiled a simple binary w/ `--gsplit-dwarf --gpubnames` and loaded it up: ``` (lldb) target create "./a.out" Current executable set to '/home/qxy11/hello-world/a.out' (x86_64). (lldb) help target modules dump separate-debug-info List the separate debug info symbol files for one or more target modules. Syntax: target modules dump separate-debug-info <cmd-options> [<filename> [<filename> [...]]] Command Options Usage: target modules dump separate-debug-info [-efj] [<filename> [<filename> [...]]] -e ( --errors-only ) Filter to show only debug info files with errors. -f ( --force-load-all-debug-info ) Load all debug info files. -j ( --json ) Output the details in JSON format. This command takes options and free-form arguments. If your arguments resemble option specifiers (i.e., they start with a - or --), you must use ' -- ' between the end of the command options and the beginning of the arguments. (lldb) target modules dump separate-debug-info --j [ { "separate-debug-info-files": [ { ... "dwo_name": "main.dwo", "loaded": false }, { ... "dwo_name": "foo.dwo", "loaded": false }, { ... "dwo_name": "bar.dwo", "loaded": false } ], } ] (lldb) b main Breakpoint 1: where = a.out`main + 15 at main.cc:3:12, address = 0x00000000000011ff (lldb) target modules dump separate-debug-info --j [ { "separate-debug-info-files": [ { ... "dwo_name": "main.dwo", "loaded": true, "resolved_dwo_path": "/home/qxy11/hello-world/main.dwo" }, { ... "dwo_name": "foo.dwo", "loaded": false }, { ... "dwo_name": "bar.dwo", "loaded": false } ], } ] (lldb) b foo Breakpoint 2: where = a.out`foo(int) + 11 at foo.cc:12:11, address = 0x000000000000121b (lldb) target modules dump separate-debug-info --j [ { "separate-debug-info-files": [ { ... "dwo_name": "main.dwo", "loaded": true, "resolved_dwo_path": "/home/qxy11/hello-world/main.dwo" }, { ... "dwo_name": "foo.dwo", "loaded": true, "resolved_dwo_path": "/home/qxy11/hello-world/foo.dwo" }, { ... "dwo_name": "bar.dwo", "loaded": false } ], } ] (lldb) b bar Breakpoint 3: where = a.out`bar(int) + 11 at bar.cc:10:9, address = 0x000000000000126b (lldb) target modules dump separate-debug-info --j [ { "separate-debug-info-files": [ { ... "dwo_name": "main.dwo", "loaded": true, "resolved_dwo_path": "/home/qxy11/hello-world/main.dwo" }, { ... "dwo_name": "foo.dwo", "loaded": true, "resolved_dwo_path": "/home/qxy11/hello-world/foo.dwo" }, { ... "dwo_name": "bar.dwo", "loaded": true, "resolved_dwo_path": "/home/qxy11/hello-world/bar.dwo" } ], } ] ```
1 parent 6512ca7 commit 7bd06c4

File tree

10 files changed

+106
-18
lines changed

10 files changed

+106
-18
lines changed

lldb/include/lldb/Symbol/SymbolFile.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,8 +467,11 @@ class SymbolFile : public PluginInterface {
467467
/// If true, then only return separate debug info files that encountered
468468
/// errors during loading. If false, then return all expected separate
469469
/// debug info files, regardless of whether they were successfully loaded.
470+
/// \param load_all_debug_info
471+
/// If true, force loading any symbol files if they are not yet loaded.
470472
virtual bool GetSeparateDebugInfo(StructuredData::Dictionary &d,
471-
bool errors_only) {
473+
bool errors_only,
474+
bool load_all_debug_info = false) {
472475
return false;
473476
};
474477

lldb/include/lldb/Symbol/SymbolFileOnDemand.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,10 @@ class SymbolFileOnDemand : public lldb_private::SymbolFile {
223223
return m_sym_file_impl->SetDebugInfoHadFrameVariableErrors();
224224
}
225225

226-
bool GetSeparateDebugInfo(StructuredData::Dictionary &d,
227-
bool errors_only) override {
228-
return m_sym_file_impl->GetSeparateDebugInfo(d, errors_only);
226+
bool GetSeparateDebugInfo(StructuredData::Dictionary &d, bool errors_only,
227+
bool load_all_debug_info = false) override {
228+
return m_sym_file_impl->GetSeparateDebugInfo(d, errors_only,
229+
load_all_debug_info);
229230
}
230231

231232
lldb::TypeSP MakeType(lldb::user_id_t uid, ConstString name,

lldb/source/Commands/CommandObjectTarget.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,11 +1412,13 @@ static bool DumpModuleSymbolFile(Stream &strm, Module *module) {
14121412
}
14131413

14141414
static bool GetSeparateDebugInfoList(StructuredData::Array &list,
1415-
Module *module, bool errors_only) {
1415+
Module *module, bool errors_only,
1416+
bool load_all_debug_info) {
14161417
if (module) {
14171418
if (SymbolFile *symbol_file = module->GetSymbolFile(/*can_create=*/true)) {
14181419
StructuredData::Dictionary d;
1419-
if (symbol_file->GetSeparateDebugInfo(d, errors_only)) {
1420+
if (symbol_file->GetSeparateDebugInfo(d, errors_only,
1421+
load_all_debug_info)) {
14201422
list.AddItem(
14211423
std::make_shared<StructuredData::Dictionary>(std::move(d)));
14221424
return true;
@@ -2522,6 +2524,10 @@ class CommandObjectTargetModulesDumpSeparateDebugInfoFiles
25222524
const int short_option = m_getopt_table[option_idx].val;
25232525

25242526
switch (short_option) {
2527+
case 'f':
2528+
m_load_all_debug_info.SetCurrentValue(true);
2529+
m_load_all_debug_info.SetOptionWasSet();
2530+
break;
25252531
case 'j':
25262532
m_json.SetCurrentValue(true);
25272533
m_json.SetOptionWasSet();
@@ -2539,6 +2545,7 @@ class CommandObjectTargetModulesDumpSeparateDebugInfoFiles
25392545
void OptionParsingStarting(ExecutionContext *execution_context) override {
25402546
m_json.Clear();
25412547
m_errors_only.Clear();
2548+
m_load_all_debug_info.Clear();
25422549
}
25432550

25442551
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
@@ -2547,6 +2554,7 @@ class CommandObjectTargetModulesDumpSeparateDebugInfoFiles
25472554

25482555
OptionValueBoolean m_json = false;
25492556
OptionValueBoolean m_errors_only = false;
2557+
OptionValueBoolean m_load_all_debug_info = false;
25502558
};
25512559

25522560
protected:
@@ -2578,7 +2586,8 @@ class CommandObjectTargetModulesDumpSeparateDebugInfoFiles
25782586

25792587
if (GetSeparateDebugInfoList(separate_debug_info_lists_by_module,
25802588
module_sp.get(),
2581-
bool(m_options.m_errors_only)))
2589+
bool(m_options.m_errors_only),
2590+
bool(m_options.m_load_all_debug_info)))
25822591
num_dumped++;
25832592
}
25842593
} else {
@@ -2599,7 +2608,8 @@ class CommandObjectTargetModulesDumpSeparateDebugInfoFiles
25992608
break;
26002609
Module *module = module_list.GetModulePointerAtIndex(i);
26012610
if (GetSeparateDebugInfoList(separate_debug_info_lists_by_module,
2602-
module, bool(m_options.m_errors_only)))
2611+
module, bool(m_options.m_errors_only),
2612+
bool(m_options.m_load_all_debug_info)))
26032613
num_dumped++;
26042614
}
26052615
} else

lldb/source/Commands/Options.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ let Command = "target modules dump separate debug info" in {
1313
Desc<"Output the details in JSON format.">;
1414
def tm_errors_only : Option<"errors-only", "e">, Group<1>,
1515
Desc<"Filter to show only debug info files with errors.">;
16+
def tm_force_load_all_debug_info : Option<"force-load-all-debug-info", "f">,
17+
Group<1>,
18+
Desc<"Load all debug info files.">;
1619
}
1720

1821
let Command = "help" in {

lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4139,7 +4139,8 @@ void SymbolFileDWARF::DumpClangAST(Stream &s, llvm::StringRef filter) {
41394139
}
41404140

41414141
bool SymbolFileDWARF::GetSeparateDebugInfo(StructuredData::Dictionary &d,
4142-
bool errors_only) {
4142+
bool errors_only,
4143+
bool load_all_debug_info) {
41434144
StructuredData::Array separate_debug_info_files;
41444145
DWARFDebugInfo &info = DebugInfo();
41454146
const size_t num_cus = info.GetNumUnits();
@@ -4182,7 +4183,7 @@ bool SymbolFileDWARF::GetSeparateDebugInfo(StructuredData::Dictionary &d,
41824183

41834184
// If we have a DWO symbol file, that means we were able to successfully
41844185
// load it.
4185-
SymbolFile *dwo_symfile = dwarf_cu->GetDwoSymbolFile();
4186+
SymbolFile *dwo_symfile = dwarf_cu->GetDwoSymbolFile(load_all_debug_info);
41864187
if (dwo_symfile) {
41874188
dwo_data->AddStringItem(
41884189
"resolved_dwo_path",

lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,8 +279,8 @@ class SymbolFileDWARF : public SymbolFileCommon {
279279
void DumpClangAST(Stream &s, llvm::StringRef filter) override;
280280

281281
/// List separate dwo files.
282-
bool GetSeparateDebugInfo(StructuredData::Dictionary &d,
283-
bool errors_only) override;
282+
bool GetSeparateDebugInfo(StructuredData::Dictionary &d, bool errors_only,
283+
bool load_all_debug_info = false) override;
284284

285285
// Gets a pair of loaded and total dwo file counts.
286286
// For split-dwarf files, this reports the counts for successfully loaded DWO

lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1278,7 +1278,8 @@ void SymbolFileDWARFDebugMap::DumpClangAST(Stream &s, llvm::StringRef filter) {
12781278
}
12791279

12801280
bool SymbolFileDWARFDebugMap::GetSeparateDebugInfo(
1281-
lldb_private::StructuredData::Dictionary &d, bool errors_only) {
1281+
lldb_private::StructuredData::Dictionary &d, bool errors_only,
1282+
bool load_all_debug_info) {
12821283
StructuredData::Array separate_debug_info_files;
12831284
const uint32_t cu_count = GetNumCompileUnits();
12841285
for (uint32_t cu_idx = 0; cu_idx < cu_count; ++cu_idx) {

lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ class SymbolFileDWARFDebugMap : public SymbolFileCommon {
132132
void DumpClangAST(Stream &s, llvm::StringRef filter) override;
133133

134134
/// List separate oso files.
135-
bool GetSeparateDebugInfo(StructuredData::Dictionary &d,
136-
bool errors_only) override;
135+
bool GetSeparateDebugInfo(StructuredData::Dictionary &d, bool errors_only,
136+
bool load_all_debug_info = false) override;
137137

138138
// PluginInterface protocol
139139
llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
include Makefile.rules
22

33
a.out:
4-
$(CC) -target x86_64-pc-linux-elf -g -gsplit-dwarf -o $@ $(SRCDIR)/main.c $(SRCDIR)/foo.c
4+
$(CC) $(CFLAGS) -target x86_64-pc-linux-elf -g -gsplit-dwarf -o $@ $(SRCDIR)/main.c $(SRCDIR)/foo.c

lldb/test/API/commands/target/dump-separate-debug-info/dwo/TestDumpDwo.py

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ def get_dwos_from_json_output(self):
2424
result[symfile_entry["symfile"]] = dwo_dict
2525
return result
2626

27-
def build_and_skip_if_error(self):
27+
def build_and_skip_if_error(self, debug_info=None):
2828
try:
29-
self.build()
29+
self.build(debug_info=debug_info)
3030
except BuildError as e:
3131
self.skipTest(f"Skipping test due to build exception: {e}")
3232

@@ -145,6 +145,75 @@ def test_dwos_loaded_symbols_on_demand(self):
145145
self.runCmd("target modules dump separate-debug-info --json")
146146

147147
# Check the output
148+
output = self.get_dwos_from_json_output()
149+
self.assertFalse(output[exe]["a.out-main.dwo"]["loaded"])
150+
self.assertFalse(output[exe]["a.out-foo.dwo"]["loaded"])
151+
# Set a breakpoint in main(). All DWO files should be loaded now
152+
self.runCmd("b main")
153+
self.runCmd("target modules dump separate-debug-info --json")
154+
output = self.get_dwos_from_json_output()
155+
self.assertTrue(output[exe]["a.out-main.dwo"]["loaded"])
156+
self.assertTrue(output[exe]["a.out-foo.dwo"]["loaded"])
157+
158+
def test_dwos_load_json_with_debug_names_default(self):
159+
"""
160+
Test that DWO files are lazily loaded, and target module dump gives the expected output.
161+
"""
162+
# Build with split DWARF, debug_names, and gpubnames
163+
self.build_and_skip_if_error(debug_info=["debug_names"])
164+
exe = self.getBuildArtifact("a.out")
165+
166+
main_dwo = self.getBuildArtifact("a.out-main.dwo")
167+
foo_dwo = self.getBuildArtifact("a.out-foo.dwo")
168+
169+
# Make sure dwo files exist
170+
self.assertTrue(os.path.exists(main_dwo), f'Make sure "{main_dwo}" file exists')
171+
self.assertTrue(os.path.exists(foo_dwo), f'Make sure "{foo_dwo}" file exists')
172+
173+
target = self.dbg.CreateTarget(exe)
174+
self.assertTrue(target, lldbtest.VALID_TARGET)
175+
176+
self.runCmd("target modules dump separate-debug-info --j")
177+
178+
# Check the output
179+
output = self.get_dwos_from_json_output()
180+
self.assertFalse(output[exe]["a.out-main.dwo"]["loaded"])
181+
self.assertFalse(output[exe]["a.out-foo.dwo"]["loaded"])
182+
183+
# Set a breakpoint in main(). a.out-main.dwo should be loaded now
184+
self.runCmd("b main")
185+
self.runCmd("target modules dump separate-debug-info --j")
186+
output = self.get_dwos_from_json_output()
187+
self.assertTrue(output[exe]["a.out-main.dwo"]["loaded"])
188+
self.assertFalse(output[exe]["a.out-foo.dwo"]["loaded"])
189+
190+
# Set a breakpoint in foo(). a.out-foo.dwo should be loaded now
191+
self.runCmd("b foo")
192+
self.runCmd("target modules dump separate-debug-info --j")
193+
output = self.get_dwos_from_json_output()
194+
self.assertTrue(output[exe]["a.out-main.dwo"]["loaded"])
195+
self.assertTrue(output[exe]["a.out-foo.dwo"]["loaded"])
196+
197+
def test_dwos_load_json_with_debug_names_force_load_all(self):
198+
"""
199+
Test that DWO files are lazily loaded, and target module dump gives the expected output.
200+
"""
201+
# Build with split DWARF, debug_names, and gpubnames
202+
self.build_and_skip_if_error(debug_info=["debug_names"])
203+
exe = self.getBuildArtifact("a.out")
204+
205+
main_dwo = self.getBuildArtifact("a.out-main.dwo")
206+
foo_dwo = self.getBuildArtifact("a.out-foo.dwo")
207+
208+
# Make sure dwo files exist
209+
self.assertTrue(os.path.exists(main_dwo), f'Make sure "{main_dwo}" file exists')
210+
self.assertTrue(os.path.exists(foo_dwo), f'Make sure "{foo_dwo}" file exists')
211+
212+
target = self.dbg.CreateTarget(exe)
213+
self.assertTrue(target, lldbtest.VALID_TARGET)
214+
215+
self.runCmd("target modules dump separate-debug-info --j --f")
216+
148217
output = self.get_dwos_from_json_output()
149218
self.assertTrue(output[exe]["a.out-main.dwo"]["loaded"])
150219
self.assertTrue(output[exe]["a.out-foo.dwo"]["loaded"])

0 commit comments

Comments
 (0)