Skip to content

Commit 1d1b20a

Browse files
authored
[lldb] Avoid force loading symbol files in statistics collection (#129593)
This commit modifies the `DebuggerStats::ReportStatistics` implementation to avoid loading symbol files for unloaded symbols. We collect stats on debugger shutdown and without this change it can cause the debugger to hang for a long while on shutdown if they symbols were not previously loaded (e.g. `settings set target.preload-symbols false`). The implementation is done by adding an optional parameter to `Module::GetSymtab` to control if the corresponding symbol file will be loaded in the same way that can control it for `Module::GetSymbolFile`.
1 parent 74868cf commit 1d1b20a

File tree

5 files changed

+93
-6
lines changed

5 files changed

+93
-6
lines changed

lldb/include/lldb/Core/Module.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,11 @@ class Module : public std::enable_shared_from_this<Module>,
608608
virtual SymbolFile *GetSymbolFile(bool can_create = true,
609609
Stream *feedback_strm = nullptr);
610610

611-
Symtab *GetSymtab();
611+
/// Get the module's symbol table
612+
///
613+
/// If the symbol table has already been loaded, this function returns it.
614+
/// Otherwise, it will only be loaded when can_create is true.
615+
Symtab *GetSymtab(bool can_create = true);
612616

613617
/// Get a reference to the UUID value contained in this object.
614618
///

lldb/source/Core/Module.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,8 +1022,8 @@ SymbolFile *Module::GetSymbolFile(bool can_create, Stream *feedback_strm) {
10221022
return m_symfile_up ? m_symfile_up->GetSymbolFile() : nullptr;
10231023
}
10241024

1025-
Symtab *Module::GetSymtab() {
1026-
if (SymbolFile *symbols = GetSymbolFile())
1025+
Symtab *Module::GetSymtab(bool can_create) {
1026+
if (SymbolFile *symbols = GetSymbolFile(can_create))
10271027
return symbols->GetSymtab();
10281028
return nullptr;
10291029
}

lldb/source/Target/Statistics.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ llvm::json::Value DebuggerStats::ReportStatistics(
316316
ModuleStats module_stat;
317317
module_stat.symtab_parse_time = module->GetSymtabParseTime().get().count();
318318
module_stat.symtab_index_time = module->GetSymtabIndexTime().get().count();
319-
Symtab *symtab = module->GetSymtab();
319+
Symtab *symtab = module->GetSymtab(/*can_create=*/false);
320320
if (symtab) {
321321
module_stat.symtab_loaded_from_cache = symtab->GetWasLoadedFromCache();
322322
if (module_stat.symtab_loaded_from_cache)
@@ -325,7 +325,7 @@ llvm::json::Value DebuggerStats::ReportStatistics(
325325
if (module_stat.symtab_saved_to_cache)
326326
++symtabs_saved;
327327
}
328-
SymbolFile *sym_file = module->GetSymbolFile();
328+
SymbolFile *sym_file = module->GetSymbolFile(/*can_create=*/false);
329329
if (sym_file) {
330330
if (!summary_only) {
331331
if (sym_file->GetObjectFile() != module->GetObjectFile())
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# This test validates that statistics generation does not force loading
2+
# symbol tables. In order to avoid other sources of symbol loading we
3+
# create the target without loading dependents and do not actually
4+
# run it. Running the target is a problem because there are various
5+
# instrumentation plugins (e.g. ASAN) that are always enabled and force
6+
# symbol loading. If you see this test start to fail we may have added
7+
# a new source of symbol loading unexpectedly.
8+
9+
# Build a simple test executable.
10+
# RUN: %clang_host -g %S/Inputs/main.c -o %t-main.exe
11+
12+
# When we enable symbol preload and dump stats there should be a non-zero
13+
# time for parsing symbol tables for the main module.
14+
# RUN: %lldb -O "settings set plugin.jit-loader.gdb.enable off" \
15+
# RUN: -O "settings set target.preload-symbols true" \
16+
# RUN: -o 'target create --no-dependents "%t-main.exe"' \
17+
# RUN: -o "statistics dump" \
18+
# RUN: -o "q" \
19+
# RUN: | FileCheck %s -check-prefixes=CHECK,PRELOAD_TRUE
20+
21+
# Find the module stats for the main executable and make sure
22+
# we are looking at the symbol parse time for that module.
23+
# CHECK: "modules": [
24+
# CHECK: {
25+
# CHECK: "path": {{.*}}-main.exe
26+
# CHECK-NOT: }
27+
28+
# PRELOAD_TRUE: "symbolTableParseTime":
29+
# PRELOAD_TRUE-SAME: {{[1-9]+}}
30+
31+
# When we disable symbol preload and dump stats the symbol table
32+
# for main should not be parsed and have a time of 0.
33+
# RUN: %lldb -O "settings set plugin.jit-loader.gdb.enable off" \
34+
# RUN: -O "settings set target.preload-symbols false" \
35+
# RUN: -o 'target create --no-dependents "%t-main.exe"' \
36+
# RUN: -o "statistics dump" \
37+
# RUN: -o "q" \
38+
# RUN: | FileCheck %s -check-prefixes=CHECK,PRELOAD_FALSE
39+
40+
# PRELOAD_FALSE: "symbolTableParseTime": 0,

lldb/unittests/Symbol/SymtabTest.cpp

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
#include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
910
#include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h"
1011
#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h"
12+
#include "Plugins/SymbolFile/Symtab/SymbolFileSymtab.h"
1113
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
1214
#include "TestingSupport/SubsystemRAII.h"
1315
#include "TestingSupport/TestUtilities.h"
@@ -31,7 +33,7 @@ using namespace lldb_private::plugin::dwarf;
3133

3234
class SymtabTest : public testing::Test {
3335
SubsystemRAII<FileSystem, HostInfo, ObjectFileMachO, SymbolFileDWARF,
34-
TypeSystemClang>
36+
ObjectFileELF, SymbolFileSymtab, TypeSystemClang>
3537
subsystem;
3638
};
3739

@@ -718,3 +720,44 @@ TEST_F(SymtabTest, TestDecodeCStringMaps) {
718720
Symtab::eVisibilityAny);
719721
ASSERT_NE(symbol, nullptr);
720722
}
723+
724+
TEST_F(SymtabTest, TestSymtabCreatedOnDemand) {
725+
auto ExpectedFile = TestFile::fromYaml(R"(
726+
--- !ELF
727+
FileHeader:
728+
Class: ELFCLASS64
729+
Data: ELFDATA2LSB
730+
Type: ET_EXEC
731+
Machine: EM_X86_64
732+
Entry: 0x0000000000400180
733+
Sections:
734+
- Name: .text
735+
Type: SHT_PROGBITS
736+
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
737+
Address: 0x0000000000400180
738+
AddressAlign: 0x0000000000000010
739+
Content: 554889E58B042500106000890425041060005DC3
740+
Symbols:
741+
- Name: _start
742+
Type: STT_FUNC
743+
Section: .text
744+
Value: 0x0000000000400180
745+
Size: 0x0000000000000014
746+
Binding: STB_GLOBAL
747+
...
748+
)");
749+
ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded());
750+
auto module_sp = std::make_shared<Module>(ExpectedFile->moduleSpec());
751+
752+
// The symbol table should not be loaded by default.
753+
Symtab *module_symtab = module_sp->GetSymtab(/*can_create=*/false);
754+
ASSERT_EQ(module_symtab, nullptr);
755+
756+
// But it should be created on demand.
757+
module_symtab = module_sp->GetSymtab(/*can_create=*/true);
758+
ASSERT_NE(module_symtab, nullptr);
759+
760+
// And we should be able to get it again once it has been created.
761+
Symtab *cached_module_symtab = module_sp->GetSymtab(/*can_create=*/false);
762+
ASSERT_EQ(module_symtab, cached_module_symtab);
763+
}

0 commit comments

Comments
 (0)