Skip to content

Commit 967434a

Browse files
authored
[lldb] Remerge llvm#136236 (Avoid force loading symbols in statistics collection (llvm#136795)
Fix a [test failure](llvm#136236 (comment)) in llvm#136236, apply a minor renaming of statistics, and remerge. See details below. # Changes in llvm#136236 Currently, `DebuggerStats::ReportStatistics()` calls `Module::GetSymtab(/*can_create=*/false)`, but then the latter calls `SymbolFile::GetSymtab()`. This will load symbols if haven't yet. See stacktrace below. The problem is that `DebuggerStats::ReportStatistics` should be read-only. This is especially important because it reports stats for symtab parsing/indexing time, which could be affected by the reporting itself if it's not read-only. This patch fixes this problem by adding an optional parameter `SymbolFile::GetSymtab(bool can_create = true)` and receiving the `false` value passed down from `Module::GetSymtab(/*can_create=*/false)` when the call is initiated from `DebuggerStats::ReportStatistics()`. --- Notes about the following stacktrace: 1. This can be reproduced. Create a helloworld program on **macOS** with dSYM, add `settings set target.preload-symbols false` to `~/.lldbinit`, do `lldb a.out`, then `statistics dump`. 2. `ObjectFile::GetSymtab` has `llvm::call_once`. So the fact that it called into `ObjectFileMachO::ParseSymtab` means that the symbol table is actually being parsed. ``` (lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = step over frame #0: 0x0000000124c4d5a0 LLDB`ObjectFileMachO::ParseSymtab(this=0x0000000111504e40, symtab=0x0000600000a05e00) at ObjectFileMachO.cpp:2259:44 * frame #1: 0x0000000124fc50a0 LLDB`lldb_private::ObjectFile::GetSymtab()::$_0::operator()(this=0x000000016d35c858) const at ObjectFile.cpp:761:9 frame #5: 0x0000000124fc4e68 LLDB`void std::__1::__call_once_proxy[abi:v160006]<std::__1::tuple<lldb_private::ObjectFile::GetSymtab()::$_0&&>>(__vp=0x000000016d35c7f0) at mutex:652:5 frame #6: 0x0000000198afb99c libc++.1.dylib`std::__1::__call_once(unsigned long volatile&, void*, void (*)(void*)) + 196 frame #7: 0x0000000124fc4dd0 LLDB`void std::__1::call_once[abi:v160006]<lldb_private::ObjectFile::GetSymtab()::$_0>(__flag=0x0000600003920080, __func=0x000000016d35c858) at mutex:670:9 frame #8: 0x0000000124fc3cb0 LLDB`void llvm::call_once<lldb_private::ObjectFile::GetSymtab()::$_0>(flag=0x0000600003920080, F=0x000000016d35c858) at Threading.h:88:5 frame #9: 0x0000000124fc2bc4 LLDB`lldb_private::ObjectFile::GetSymtab(this=0x0000000111504e40) at ObjectFile.cpp:755:5 frame #10: 0x0000000124fe0a28 LLDB`lldb_private::SymbolFileCommon::GetSymtab(this=0x0000000104865200) at SymbolFile.cpp:158:39 frame #11: 0x0000000124d8fedc LLDB`lldb_private::Module::GetSymtab(this=0x00000001113041a8, can_create=false) at Module.cpp:1027:21 frame #12: 0x0000000125125bdc LLDB`lldb_private::DebuggerStats::ReportStatistics(debugger=0x000000014284d400, target=0x0000000115808200, options=0x000000014195d6d1) at Statistics.cpp:329:30 frame #13: 0x0000000125672978 LLDB`CommandObjectStatsDump::DoExecute(this=0x000000014195d540, command=0x000000016d35d820, result=0x000000016d35e150) at CommandObjectStats.cpp:144:18 frame #14: 0x0000000124f29b40 LLDB`lldb_private::CommandObjectParsed::Execute(this=0x000000014195d540, args_string="", result=0x000000016d35e150) at CommandObject.cpp:832:9 frame #15: 0x0000000124efbd70 LLDB`lldb_private::CommandInterpreter::HandleCommand(this=0x0000000141b22f30, command_line="statistics dump", lazy_add_to_history=eLazyBoolCalculate, result=0x000000016d35e150, force_repeat_command=false) at CommandInterpreter.cpp:2134:14 frame #16: 0x0000000124f007f4 LLDB`lldb_private::CommandInterpreter::IOHandlerInputComplete(this=0x0000000141b22f30, io_handler=0x00000001419b2aa8, line="statistics dump") at CommandInterpreter.cpp:3251:3 frame #17: 0x0000000124d7b5ec LLDB`lldb_private::IOHandlerEditline::Run(this=0x00000001419b2aa8) at IOHandler.cpp:588:22 frame #18: 0x0000000124d1e8fc LLDB`lldb_private::Debugger::RunIOHandlers(this=0x000000014284d400) at Debugger.cpp:1225:16 frame #19: 0x0000000124f01f74 LLDB`lldb_private::CommandInterpreter::RunCommandInterpreter(this=0x0000000141b22f30, options=0x000000016d35e63c) at CommandInterpreter.cpp:3543:16 frame #20: 0x0000000122840294 LLDB`lldb::SBDebugger::RunCommandInterpreter(this=0x000000016d35ebd8, auto_handle_events=true, spawn_thread=false) at SBDebugger.cpp:1212:42 frame #21: 0x0000000102aa6d28 lldb`Driver::MainLoop(this=0x000000016d35ebb8) at Driver.cpp:621:18 frame #22: 0x0000000102aa75b0 lldb`main(argc=1, argv=0x000000016d35f548) at Driver.cpp:829:26 frame #23: 0x0000000198858274 dyld`start + 2840 ``` # Changes in this PR top of the above Fix a [test failure](llvm#136236 (comment)) in `TestStats.py`. The original version of the added test checks that all modules have symbol count zero when `target.preload-symbols == false`. The test failed on macOS. Due to various reasons, on macOS, symbols can be loaded for dylibs even with that setting, but not for the main module. For now, the fix of the test is to limit the assertion to only the main module. The test now passes on macOS. In the future, when we have a way to control a specific list of plug-ins to be loaded, there may be a configuration that this test can use to assert that all modules have symbol count zero. Apply a minor renaming of statistics, per the [suggestion](llvm#136226 (comment)) in llvm#136226 after merge.
1 parent 31b38d6 commit 967434a

File tree

11 files changed

+119
-23
lines changed

11 files changed

+119
-23
lines changed

lldb/include/lldb/Symbol/ObjectFile.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ class ObjectFile : public std::enable_shared_from_this<ObjectFile>,
319319
///
320320
/// \return
321321
/// The symbol table for this object file.
322-
Symtab *GetSymtab();
322+
Symtab *GetSymtab(bool can_create = true);
323323

324324
/// Parse the symbol table into the provides symbol table object.
325325
///

lldb/include/lldb/Symbol/SymbolFile.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ class SymbolFile : public PluginInterface {
144144
virtual uint32_t GetNumCompileUnits() = 0;
145145
virtual lldb::CompUnitSP GetCompileUnitAtIndex(uint32_t idx) = 0;
146146

147-
virtual Symtab *GetSymtab() = 0;
147+
virtual Symtab *GetSymtab(bool can_create = true) = 0;
148148

149149
virtual lldb::LanguageType ParseLanguage(CompileUnit &comp_unit) = 0;
150150
/// Return the Xcode SDK comp_unit was compiled against.
@@ -533,7 +533,7 @@ class SymbolFileCommon : public SymbolFile {
533533
return m_abilities;
534534
}
535535

536-
Symtab *GetSymtab() override;
536+
Symtab *GetSymtab(bool can_create = true) override;
537537

538538
ObjectFile *GetObjectFile() override { return m_objfile_sp.get(); }
539539
const ObjectFile *GetObjectFile() const override {

lldb/include/lldb/Symbol/SymbolFileOnDemand.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,9 @@ class SymbolFileOnDemand : public lldb_private::SymbolFile {
186186

187187
uint32_t GetAbilities() override;
188188

189-
Symtab *GetSymtab() override { return m_sym_file_impl->GetSymtab(); }
189+
Symtab *GetSymtab(bool can_create = true) override {
190+
return m_sym_file_impl->GetSymtab(can_create);
191+
}
190192

191193
ObjectFile *GetObjectFile() override {
192194
return m_sym_file_impl->GetObjectFile();

lldb/include/lldb/Target/Statistics.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ struct ModuleStats {
120120
llvm::StringMap<llvm::json::Value> type_system_stats;
121121
double symtab_parse_time = 0.0;
122122
double symtab_index_time = 0.0;
123-
uint32_t num_symbols_loaded = 0;
123+
uint32_t symtab_symbol_count = 0;
124124
double debug_parse_time = 0.0;
125125
double debug_index_time = 0.0;
126126
uint64_t debug_info_size = 0;

lldb/source/Core/Module.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -997,7 +997,7 @@ SymbolFile *Module::GetSymbolFile(bool can_create, Stream *feedback_strm) {
997997

998998
Symtab *Module::GetSymtab(bool can_create) {
999999
if (SymbolFile *symbols = GetSymbolFile(can_create))
1000-
return symbols->GetSymtab();
1000+
return symbols->GetSymtab(can_create);
10011001
return nullptr;
10021002
}
10031003

lldb/source/Symbol/ObjectFile.cpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -734,10 +734,9 @@ void llvm::format_provider<ObjectFile::Strata>::format(
734734
}
735735
}
736736

737-
738-
Symtab *ObjectFile::GetSymtab() {
737+
Symtab *ObjectFile::GetSymtab(bool can_create) {
739738
ModuleSP module_sp(GetModule());
740-
if (module_sp) {
739+
if (module_sp && can_create) {
741740
// We can't take the module lock in ObjectFile::GetSymtab() or we can
742741
// deadlock in DWARF indexing when any file asks for the symbol table from
743742
// an object file. This currently happens in the preloading of symbols in

lldb/source/Symbol/SymbolFile.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,10 @@ void SymbolFile::AssertModuleLock() {
152152

153153
SymbolFile::RegisterInfoResolver::~RegisterInfoResolver() = default;
154154

155-
Symtab *SymbolFileCommon::GetSymtab() {
155+
Symtab *SymbolFileCommon::GetSymtab(bool can_create) {
156156
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
157157
// Fetch the symtab from the main object file.
158-
auto *symtab = GetMainObjectFile()->GetSymtab();
158+
auto *symtab = GetMainObjectFile()->GetSymtab(can_create);
159159
if (m_symtab != symtab) {
160160
m_symtab = symtab;
161161

lldb/source/Target/Statistics.cpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ json::Value ModuleStats::ToJSON() const {
7171
module.try_emplace("debugInfoHadIncompleteTypes",
7272
debug_info_had_incomplete_types);
7373
module.try_emplace("symbolTableStripped", symtab_stripped);
74-
module.try_emplace("symbolsLoaded", num_symbols_loaded);
74+
module.try_emplace("symbolTableSymbolCount", symtab_symbol_count);
7575
if (!symfile_path.empty())
7676
module.try_emplace("symbolFilePath", symfile_path);
7777

@@ -311,7 +311,7 @@ llvm::json::Value DebuggerStats::ReportStatistics(
311311
uint32_t num_modules_with_variable_errors = 0;
312312
uint32_t num_modules_with_incomplete_types = 0;
313313
uint32_t num_stripped_modules = 0;
314-
uint32_t num_symbols_loaded = 0;
314+
uint32_t symtab_symbol_count = 0;
315315
for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
316316
Module *module = target != nullptr
317317
? target->GetImages().GetModuleAtIndex(image_idx).get()
@@ -321,8 +321,8 @@ llvm::json::Value DebuggerStats::ReportStatistics(
321321
module_stat.symtab_index_time = module->GetSymtabIndexTime().get().count();
322322
Symtab *symtab = module->GetSymtab(/*can_create=*/false);
323323
if (symtab) {
324-
module_stat.num_symbols_loaded = symtab->GetNumSymbols();
325-
num_symbols_loaded += module_stat.num_symbols_loaded;
324+
module_stat.symtab_symbol_count = symtab->GetNumSymbols();
325+
symtab_symbol_count += module_stat.symtab_symbol_count;
326326
++symtabs_loaded;
327327
module_stat.symtab_loaded_from_cache = symtab->GetWasLoadedFromCache();
328328
if (module_stat.symtab_loaded_from_cache)
@@ -414,7 +414,7 @@ llvm::json::Value DebuggerStats::ReportStatistics(
414414
num_modules_with_incomplete_types},
415415
{"totalDebugInfoEnabled", num_debug_info_enabled_modules},
416416
{"totalSymbolTableStripped", num_stripped_modules},
417-
{"totalSymbolsLoaded", num_symbols_loaded},
417+
{"totalSymbolTableSymbolCount", symtab_symbol_count},
418418
};
419419

420420
if (include_targets) {

lldb/test/API/commands/statistics/basic/TestStats.py

+25-4
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ def test_default_no_run(self):
170170
"totalSymbolTableIndexTime",
171171
"totalSymbolTablesLoadedFromCache",
172172
"totalSymbolTablesSavedToCache",
173-
"totalSymbolsLoaded",
173+
"totalSymbolTableSymbolCount",
174174
"totalSymbolTablesLoaded",
175175
"totalDebugInfoByteSize",
176176
"totalDebugInfoIndexTime",
@@ -180,7 +180,7 @@ def test_default_no_run(self):
180180
]
181181
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
182182
if self.getPlatform() != "windows":
183-
self.assertGreater(debug_stats["totalSymbolsLoaded"], 0)
183+
self.assertGreater(debug_stats["totalSymbolTableSymbolCount"], 0)
184184
self.assertGreater(debug_stats["totalSymbolTablesLoaded"], 0)
185185

186186
# Verify target stats keys.
@@ -203,13 +203,34 @@ def test_default_no_run(self):
203203
# Verify module stats keys.
204204
for module_stats in debug_stats["modules"]:
205205
module_stat_keys_exist = [
206-
"symbolsLoaded",
206+
"symbolTableSymbolCount",
207207
]
208208
self.verify_keys(
209209
module_stats, '"module_stats"', module_stat_keys_exist, None
210210
)
211211
if self.getPlatform() != "windows":
212-
self.assertGreater(module_stats["symbolsLoaded"], 0)
212+
self.assertGreater(module_stats["symbolTableSymbolCount"], 0)
213+
214+
def test_default_no_run_no_preload_symbols(self):
215+
"""Test "statistics dump" without running the target and without
216+
preloading symbols.
217+
218+
Checks that symbol count are zero.
219+
"""
220+
# Make sure symbols will not be preloaded.
221+
self.runCmd("settings set target.preload-symbols false")
222+
223+
# Build and load the target
224+
self.build()
225+
self.createTestTarget()
226+
227+
# Get statistics
228+
debug_stats = self.get_stats()
229+
230+
# No symbols should be loaded in the main module.
231+
main_module_stats = debug_stats["modules"][0]
232+
if self.getPlatform() != "windows":
233+
self.assertEqual(main_module_stats["symbolTableSymbolCount"], 0)
213234

214235
def test_default_with_run(self):
215236
"""Test "statistics dump" when running the target to a breakpoint.

lldb/unittests/Symbol/LineTableTest.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class FakeSymbolFile : public SymbolFile {
5858
uint32_t CalculateAbilities() override { return UINT32_MAX; }
5959
uint32_t GetNumCompileUnits() override { return 1; }
6060
CompUnitSP GetCompileUnitAtIndex(uint32_t) override { return m_cu_sp; }
61-
Symtab *GetSymtab() override { return nullptr; }
61+
Symtab *GetSymtab(bool can_create = true) override { return nullptr; }
6262
LanguageType ParseLanguage(CompileUnit &) override { return eLanguageTypeC; }
6363
size_t ParseFunctions(CompileUnit &) override { return 0; }
6464
bool ParseLineTable(CompileUnit &) override { return true; }

lldb/unittests/Symbol/SymtabTest.cpp

+76-2
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ TEST_F(SymtabTest, TestDecodeCStringMaps) {
721721
ASSERT_NE(symbol, nullptr);
722722
}
723723

724-
TEST_F(SymtabTest, TestSymtabCreatedOnDemand) {
724+
TEST_F(SymtabTest, TestSymbolFileCreatedOnDemand) {
725725
auto ExpectedFile = TestFile::fromYaml(R"(
726726
--- !ELF
727727
FileHeader:
@@ -749,7 +749,7 @@ TEST_F(SymtabTest, TestSymtabCreatedOnDemand) {
749749
ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded());
750750
auto module_sp = std::make_shared<Module>(ExpectedFile->moduleSpec());
751751

752-
// The symbol table should not be loaded by default.
752+
// The symbol file should not be created by default.
753753
Symtab *module_symtab = module_sp->GetSymtab(/*can_create=*/false);
754754
ASSERT_EQ(module_symtab, nullptr);
755755

@@ -761,3 +761,77 @@ TEST_F(SymtabTest, TestSymtabCreatedOnDemand) {
761761
Symtab *cached_module_symtab = module_sp->GetSymtab(/*can_create=*/false);
762762
ASSERT_EQ(module_symtab, cached_module_symtab);
763763
}
764+
765+
TEST_F(SymtabTest, TestSymbolTableCreatedOnDemand) {
766+
const char *yamldata = R"(
767+
--- !ELF
768+
FileHeader:
769+
Class: ELFCLASS64
770+
Data: ELFDATA2LSB
771+
Type: ET_EXEC
772+
Machine: EM_386
773+
DWARF:
774+
debug_abbrev:
775+
- Table:
776+
- Code: 0x00000001
777+
Tag: DW_TAG_compile_unit
778+
Children: DW_CHILDREN_no
779+
Attributes:
780+
- Attribute: DW_AT_addr_base
781+
Form: DW_FORM_sec_offset
782+
debug_info:
783+
- Version: 5
784+
AddrSize: 4
785+
UnitType: DW_UT_compile
786+
Entries:
787+
- AbbrCode: 0x00000001
788+
Values:
789+
- Value: 0x8 # Offset of the first Address past the header
790+
- AbbrCode: 0x0
791+
debug_addr:
792+
- Version: 5
793+
AddressSize: 4
794+
Entries:
795+
- Address: 0x1234
796+
- Address: 0x5678
797+
debug_line:
798+
- Length: 42
799+
Version: 2
800+
PrologueLength: 36
801+
MinInstLength: 1
802+
DefaultIsStmt: 1
803+
LineBase: 251
804+
LineRange: 14
805+
OpcodeBase: 13
806+
StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ]
807+
IncludeDirs:
808+
- '/tmp'
809+
Files:
810+
- Name: main.cpp
811+
DirIdx: 1
812+
ModTime: 0
813+
Length: 0
814+
)";
815+
llvm::Expected<TestFile> file = TestFile::fromYaml(yamldata);
816+
EXPECT_THAT_EXPECTED(file, llvm::Succeeded());
817+
auto module_sp = std::make_shared<Module>(file->moduleSpec());
818+
819+
SymbolFile *symbol_file = module_sp->GetSymbolFile();
820+
// At this point, the symbol table is not created. This is because the above
821+
// yaml data contains the necessary sections in order for
822+
// SymbolFileDWARF::CalculateAbilities() to identify all abilities, saving it
823+
// from calling SymbolFileDWARFDebugMap::CalculateAbilities(), which
824+
// eventually loads the symbol table, which we don't want.
825+
826+
// The symbol table should not be created if asked not to.
827+
Symtab *symtab = symbol_file->GetSymtab(/*can_create=*/false);
828+
ASSERT_EQ(symtab, nullptr);
829+
830+
// But it should be created on demand.
831+
symtab = symbol_file->GetSymtab(/*can_create=*/true);
832+
ASSERT_NE(symtab, nullptr);
833+
834+
// And we should be able to get it again once it has been created.
835+
Symtab *cached_symtab = symbol_file->GetSymtab(/*can_create=*/false);
836+
ASSERT_EQ(symtab, cached_symtab);
837+
}

0 commit comments

Comments
 (0)