Skip to content

Commit a8425d2

Browse files
kevinfreiKevin Frei
and
Kevin Frei
authored
DebugInfoD issues, take 2 (#86812)
The previous diff (and it's subsequent fix) were reverted as the tests didn't work properly on the AArch64 & ARM LLDB buildbots. I made a couple more minor changes to tests (from @clayborg's feedback) and disabled them for non Linux-x86(_64) builds, as I don't have the ability do anything about an ARM64 Linux failure. If I had to guess, I'd say the toolchain on the buildbots isn't respecting the `-Wl,--build-id` flag. Maybe, one day, when I have a Linux AArch64 system I'll dig in to it. From the reverted PR: I've migrated the tests in my #79181 from shell to API (at @JDevlieghere's suggestion) and addressed a couple issues that were exposed during testing. The tests first test the "normal" situation (no DebugInfoD involvement, just normal debug files sitting around), then the "no debug info" situation (to make sure the test is seeing failure properly), then it tests to validate that when DebugInfoD returns the symbols, things work properly. This is duplicated for DWP/split-dwarf scenarios. --------- Co-authored-by: Kevin Frei <[email protected]>
1 parent e29228e commit a8425d2

File tree

10 files changed

+506
-17
lines changed

10 files changed

+506
-17
lines changed

lldb/packages/Python/lldbsuite/test/make/Makefile.rules

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ LLDB_BASE_DIR := $(THIS_FILE_DIR)/../../../../../
5151
#
5252
# GNUWin32 uname gives "windows32" or "server version windows32" while
5353
# some versions of MSYS uname return "MSYS_NT*", but most environments
54-
# standardize on "Windows_NT", so we'll make it consistent here.
54+
# standardize on "Windows_NT", so we'll make it consistent here.
5555
# When running tests from Visual Studio, the environment variable isn't
5656
# inherited all the way down to the process spawned for make.
5757
#----------------------------------------------------------------------
@@ -210,6 +210,12 @@ else
210210
ifeq "$(SPLIT_DEBUG_SYMBOLS)" "YES"
211211
DSYM = $(EXE).debug
212212
endif
213+
214+
ifeq "$(MAKE_DWP)" "YES"
215+
MAKE_DWO := YES
216+
DWP_NAME = $(EXE).dwp
217+
DYLIB_DWP_NAME = $(DYLIB_NAME).dwp
218+
endif
213219
endif
214220

215221
LIMIT_DEBUG_INFO_FLAGS =
@@ -357,6 +363,7 @@ ifneq "$(OS)" "Darwin"
357363

358364
OBJCOPY ?= $(call replace_cc_with,objcopy)
359365
ARCHIVER ?= $(call replace_cc_with,ar)
366+
DWP ?= $(call replace_cc_with,dwp)
360367
override AR = $(ARCHIVER)
361368
endif
362369

@@ -527,6 +534,10 @@ ifneq "$(CXX)" ""
527534
endif
528535
endif
529536

537+
ifeq "$(GEN_GNU_BUILD_ID)" "YES"
538+
LDFLAGS += -Wl,--build-id
539+
endif
540+
530541
#----------------------------------------------------------------------
531542
# DYLIB_ONLY variable can be used to skip the building of a.out.
532543
# See the sections below regarding dSYM file as well as the building of
@@ -565,10 +576,17 @@ else
565576
endif
566577
else
567578
ifeq "$(SPLIT_DEBUG_SYMBOLS)" "YES"
579+
ifeq "$(SAVE_FULL_DEBUG_BINARY)" "YES"
580+
cp "$(EXE)" "$(EXE).unstripped"
581+
endif
568582
$(OBJCOPY) --only-keep-debug "$(EXE)" "$(DSYM)"
569583
$(OBJCOPY) --strip-debug --add-gnu-debuglink="$(DSYM)" "$(EXE)" "$(EXE)"
570584
endif
585+
ifeq "$(MAKE_DWP)" "YES"
586+
$(DWP) -o "$(DWP_NAME)" $(DWOS)
571587
endif
588+
endif
589+
572590

573591
#----------------------------------------------------------------------
574592
# Make the dylib
@@ -610,9 +628,15 @@ endif
610628
else
611629
$(LD) $(DYLIB_OBJECTS) $(LDFLAGS) -shared -o "$(DYLIB_FILENAME)"
612630
ifeq "$(SPLIT_DEBUG_SYMBOLS)" "YES"
631+
ifeq "$(SAVE_FULL_DEBUG_BINARY)" "YES"
632+
cp "$(DYLIB_FILENAME)" "$(DYLIB_FILENAME).unstripped"
633+
endif
613634
$(OBJCOPY) --only-keep-debug "$(DYLIB_FILENAME)" "$(DYLIB_FILENAME).debug"
614635
$(OBJCOPY) --strip-debug --add-gnu-debuglink="$(DYLIB_FILENAME).debug" "$(DYLIB_FILENAME)" "$(DYLIB_FILENAME)"
615636
endif
637+
ifeq "$(MAKE_DWP)" "YES"
638+
$(DWP) -o $(DYLIB_DWP_FILE) $(DYLIB_DWOS)
639+
endif
616640
endif
617641

618642
#----------------------------------------------------------------------

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

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4378,26 +4378,38 @@ const std::shared_ptr<SymbolFileDWARFDwo> &SymbolFileDWARF::GetDwpSymbolFile() {
43784378
FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths();
43794379
ModuleSpec module_spec;
43804380
module_spec.GetFileSpec() = m_objfile_sp->GetFileSpec();
4381+
FileSpec dwp_filespec;
43814382
for (const auto &symfile : symfiles.files()) {
43824383
module_spec.GetSymbolFileSpec() =
43834384
FileSpec(symfile.GetPath() + ".dwp", symfile.GetPathStyle());
43844385
LLDB_LOG(log, "Searching for DWP using: \"{0}\"",
43854386
module_spec.GetSymbolFileSpec());
4386-
FileSpec dwp_filespec =
4387+
dwp_filespec =
43874388
PluginManager::LocateExecutableSymbolFile(module_spec, search_paths);
43884389
if (FileSystem::Instance().Exists(dwp_filespec)) {
4389-
LLDB_LOG(log, "Found DWP file: \"{0}\"", dwp_filespec);
4390-
DataBufferSP dwp_file_data_sp;
4391-
lldb::offset_t dwp_file_data_offset = 0;
4392-
ObjectFileSP dwp_obj_file = ObjectFile::FindPlugin(
4393-
GetObjectFile()->GetModule(), &dwp_filespec, 0,
4394-
FileSystem::Instance().GetByteSize(dwp_filespec), dwp_file_data_sp,
4395-
dwp_file_data_offset);
4396-
if (dwp_obj_file) {
4397-
m_dwp_symfile = std::make_shared<SymbolFileDWARFDwo>(
4398-
*this, dwp_obj_file, DIERef::k_file_index_mask);
4399-
break;
4400-
}
4390+
break;
4391+
}
4392+
}
4393+
if (!FileSystem::Instance().Exists(dwp_filespec)) {
4394+
LLDB_LOG(log, "No DWP file found locally");
4395+
// Fill in the UUID for the module we're trying to match for, so we can
4396+
// find the correct DWP file, as the Debuginfod plugin uses *only* this
4397+
// data to correctly match the DWP file with the binary.
4398+
module_spec.GetUUID() = m_objfile_sp->GetUUID();
4399+
dwp_filespec =
4400+
PluginManager::LocateExecutableSymbolFile(module_spec, search_paths);
4401+
}
4402+
if (FileSystem::Instance().Exists(dwp_filespec)) {
4403+
LLDB_LOG(log, "Found DWP file: \"{0}\"", dwp_filespec);
4404+
DataBufferSP dwp_file_data_sp;
4405+
lldb::offset_t dwp_file_data_offset = 0;
4406+
ObjectFileSP dwp_obj_file = ObjectFile::FindPlugin(
4407+
GetObjectFile()->GetModule(), &dwp_filespec, 0,
4408+
FileSystem::Instance().GetByteSize(dwp_filespec), dwp_file_data_sp,
4409+
dwp_file_data_offset);
4410+
if (dwp_obj_file) {
4411+
m_dwp_symfile = std::make_shared<SymbolFileDWARFDwo>(
4412+
*this, dwp_obj_file, DIERef::k_file_index_mask);
44014413
}
44024414
}
44034415
if (!m_dwp_symfile) {

lldb/source/Plugins/SymbolLocator/CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
# Order matters here: the first symbol locator prevents further searching.
2+
# For DWARF binaries that are both stripped and split, the Default plugin
3+
# will return the stripped binary when asked for the ObjectFile, which then
4+
# prevents an unstripped binary from being requested from the Debuginfod
5+
# provider.
6+
add_subdirectory(Debuginfod)
17
add_subdirectory(Default)
28
if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
39
add_subdirectory(DebugSymbols)
410
endif()
5-
add_subdirectory(Debuginfod)

lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,24 @@ llvm::StringRef SymbolVendorELF::GetPluginDescriptionStatic() {
4444
"executables.";
4545
}
4646

47+
// If this is needed elsewhere, it can be exported/moved.
48+
static bool IsDwpSymbolFile(const lldb::ModuleSP &module_sp,
49+
const FileSpec &file_spec) {
50+
DataBufferSP dwp_file_data_sp;
51+
lldb::offset_t dwp_file_data_offset = 0;
52+
// Try to create an ObjectFile from the file_spec.
53+
ObjectFileSP dwp_obj_file = ObjectFile::FindPlugin(
54+
module_sp, &file_spec, 0, FileSystem::Instance().GetByteSize(file_spec),
55+
dwp_file_data_sp, dwp_file_data_offset);
56+
// The presence of a debug_cu_index section is the key identifying feature of
57+
// a DWP file. Make sure we don't fill in the section list on dwp_obj_file
58+
// (by calling GetSectionList(false)) as this function could be called before
59+
// we may have all the symbol files collected and available.
60+
return dwp_obj_file && ObjectFileELF::classof(dwp_obj_file.get()) &&
61+
dwp_obj_file->GetSectionList(false)->FindSectionByType(
62+
eSectionTypeDWARFDebugCuIndex, false);
63+
}
64+
4765
// CreateInstance
4866
//
4967
// Platforms can register a callback to use when creating symbol vendors to
@@ -87,8 +105,15 @@ SymbolVendorELF::CreateInstance(const lldb::ModuleSP &module_sp,
87105
FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths();
88106
FileSpec dsym_fspec =
89107
PluginManager::LocateExecutableSymbolFile(module_spec, search_paths);
90-
if (!dsym_fspec)
91-
return nullptr;
108+
if (!dsym_fspec || IsDwpSymbolFile(module_sp, dsym_fspec)) {
109+
// If we have a stripped binary or if we got a DWP file, we should prefer
110+
// symbols in the executable acquired through a plugin.
111+
ModuleSpec unstripped_spec =
112+
PluginManager::LocateExecutableObjectFile(module_spec);
113+
if (!unstripped_spec)
114+
return nullptr;
115+
dsym_fspec = unstripped_spec.GetFileSpec();
116+
}
92117

93118
DataBufferSP dsym_file_data_sp;
94119
lldb::offset_t dsym_file_data_offset = 0;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
C_SOURCES := main.c
2+
3+
# For normal (non DWP) Debuginfod tests, we need:
4+
5+
# * The full binary: a.out.unstripped
6+
# Produced by Makefile.rules with SAVE_FULL_DEBUG_BINARY set to YES and
7+
# SPLIT_DEBUG_SYMBOLS set to YES
8+
9+
# * The stripped binary (a.out)
10+
# Produced by Makefile.rules with SPLIT_DEBUG_SYMBOLS set to YES
11+
12+
# * The 'only-keep-debug' binary (a.out.debug)
13+
# Produced below
14+
15+
SPLIT_DEBUG_SYMBOLS := YES
16+
SAVE_FULL_DEBUG_BINARY := YES
17+
GEN_GNU_BUILD_ID := YES
18+
19+
include Makefile.rules
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import os
2+
import shutil
3+
import tempfile
4+
5+
import lldb
6+
from lldbsuite.test.decorators import *
7+
import lldbsuite.test.lldbutil as lldbutil
8+
from lldbsuite.test.lldbtest import *
9+
10+
11+
"""
12+
Test support for the DebugInfoD network symbol acquisition protocol.
13+
This one is for simple / no split-dwarf scenarios.
14+
15+
For no-split-dwarf scenarios, there are 2 variations:
16+
1 - A stripped binary with it's corresponding unstripped binary:
17+
2 - A stripped binary with a corresponding --only-keep-debug symbols file
18+
"""
19+
20+
21+
# It looks like Linux-AArch64 doesn't support build-id's on the LLDB builtbots
22+
@skipIf(oslist=no_match(["linux"]), archs=no_match(["i386", "x86_64"]))
23+
class DebugInfodTests(TestBase):
24+
# No need to try every flavor of debug inf.
25+
NO_DEBUG_INFO_TESTCASE = True
26+
27+
def test_normal_no_symbols(self):
28+
"""
29+
Validate behavior with no symbols or symbol locator.
30+
('baseline negative' behavior)
31+
"""
32+
test_root = self.config_test(["a.out"])
33+
self.try_breakpoint(False)
34+
35+
def test_normal_default(self):
36+
"""
37+
Validate behavior with symbols, but no symbol locator.
38+
('baseline positive' behavior)
39+
"""
40+
test_root = self.config_test(["a.out", "a.out.debug"])
41+
self.try_breakpoint(True)
42+
43+
def test_debuginfod_symbols(self):
44+
"""
45+
Test behavior with the full binary available from Debuginfod as
46+
'debuginfo' from the plug-in.
47+
"""
48+
test_root = self.config_test(["a.out"], "a.out.unstripped")
49+
self.try_breakpoint(True)
50+
51+
def test_debuginfod_executable(self):
52+
"""
53+
Test behavior with the full binary available from Debuginfod as
54+
'executable' from the plug-in.
55+
"""
56+
test_root = self.config_test(["a.out"], None, "a.out.unstripped")
57+
self.try_breakpoint(True)
58+
59+
def test_debuginfod_okd_symbols(self):
60+
"""
61+
Test behavior with the 'only-keep-debug' symbols available from Debuginfod.
62+
"""
63+
test_root = self.config_test(["a.out"], "a.out.debug")
64+
self.try_breakpoint(True)
65+
66+
def try_breakpoint(self, should_have_loc):
67+
"""
68+
This function creates a target from self.aout, sets a function-name
69+
breakpoint, and checks to see if we have a file/line location,
70+
as a way to validate that the symbols have been loaded.
71+
should_have_loc specifies if we're testing that symbols have or
72+
haven't been loaded.
73+
"""
74+
target = self.dbg.CreateTarget(self.aout)
75+
self.assertTrue(target and target.IsValid(), "Target is valid")
76+
77+
bp = target.BreakpointCreateByName("func")
78+
self.assertTrue(bp and bp.IsValid(), "Breakpoint is valid")
79+
self.assertEqual(bp.GetNumLocations(), 1)
80+
81+
loc = bp.GetLocationAtIndex(0)
82+
self.assertTrue(loc and loc.IsValid(), "Location is valid")
83+
addr = loc.GetAddress()
84+
self.assertTrue(addr and addr.IsValid(), "Loc address is valid")
85+
line_entry = addr.GetLineEntry()
86+
self.assertEqual(
87+
should_have_loc,
88+
line_entry != None and line_entry.IsValid(),
89+
"Loc line entry is valid",
90+
)
91+
if should_have_loc:
92+
self.assertEqual(line_entry.GetLine(), 4)
93+
self.assertEqual(
94+
line_entry.GetFileSpec().GetFilename(),
95+
self.main_source_file.GetFilename(),
96+
)
97+
self.dbg.DeleteTarget(target)
98+
shutil.rmtree(self.tmp_dir)
99+
100+
def config_test(self, local_files, debuginfo=None, executable=None):
101+
"""
102+
Set up a test with local_files[] copied to a different location
103+
so that we control which files are, or are not, found in the file system.
104+
Also, create a stand-alone file-system 'hosted' debuginfod server with the
105+
provided debuginfo and executable files (if they exist)
106+
107+
Make the filesystem look like:
108+
109+
/tmp/<tmpdir>/test/[local_files]
110+
111+
/tmp/<tmpdir>/cache (for lldb to use as a temp cache)
112+
113+
/tmp/<tmpdir>/buildid/<uuid>/executable -> <executable>
114+
/tmp/<tmpdir>/buildid/<uuid>/debuginfo -> <debuginfo>
115+
Returns the /tmp/<tmpdir> path
116+
"""
117+
118+
self.build()
119+
120+
uuid = self.getUUID("a.out")
121+
if not uuid:
122+
self.fail("Could not get UUID for a.out")
123+
return
124+
self.main_source_file = lldb.SBFileSpec("main.c")
125+
self.tmp_dir = tempfile.mkdtemp()
126+
test_dir = os.path.join(self.tmp_dir, "test")
127+
os.makedirs(test_dir)
128+
129+
self.aout = ""
130+
# Copy the files used by the test:
131+
for f in local_files:
132+
shutil.copy(self.getBuildArtifact(f), test_dir)
133+
# The first item is the binary to be used for the test
134+
if self.aout == "":
135+
self.aout = os.path.join(test_dir, f)
136+
137+
use_debuginfod = debuginfo != None or executable != None
138+
139+
# Populated the 'file://... mocked' Debuginfod server:
140+
if use_debuginfod:
141+
os.makedirs(os.path.join(self.tmp_dir, "cache"))
142+
uuid_dir = os.path.join(self.tmp_dir, "buildid", uuid)
143+
os.makedirs(uuid_dir)
144+
if debuginfo:
145+
shutil.copy(
146+
self.getBuildArtifact(debuginfo),
147+
os.path.join(uuid_dir, "debuginfo"),
148+
)
149+
if executable:
150+
shutil.copy(
151+
self.getBuildArtifact(executable),
152+
os.path.join(uuid_dir, "executable"),
153+
)
154+
155+
# Configure LLDB for the test:
156+
self.runCmd(
157+
"settings set symbols.enable-external-lookup %s"
158+
% str(use_debuginfod).lower()
159+
)
160+
self.runCmd("settings clear plugin.symbol-locator.debuginfod.server-urls")
161+
if use_debuginfod:
162+
self.runCmd(
163+
"settings set plugin.symbol-locator.debuginfod.cache-path %s/cache"
164+
% self.tmp_dir
165+
)
166+
self.runCmd(
167+
"settings insert-before plugin.symbol-locator.debuginfod.server-urls 0 file://%s"
168+
% self.tmp_dir
169+
)
170+
171+
def getUUID(self, filename):
172+
try:
173+
target = self.dbg.CreateTarget(self.getBuildArtifact(filename))
174+
module = target.GetModuleAtIndex(0)
175+
uuid = module.GetUUIDString().replace("-", "").lower()
176+
self.dbg.DeleteTarget(target)
177+
return uuid if len(uuid) == 40 else None
178+
except:
179+
return None
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// This is a dump little pair of test files
2+
3+
int func(int argc, const char *argv[]) {
4+
return (argc + 1) * (argv[argc][0] + 2);
5+
}
6+
7+
int main(int argc, const char *argv[]) { return func(0, argv); }

0 commit comments

Comments
 (0)