[Lldb-commits] [lldb] DebugInfoD tests + fixing issues exposed by tests (PR #85693)
Kevin Frei via lldb-commits
lldb-commits at lists.llvm.org
Tue Mar 19 08:25:31 PDT 2024
https://github.com/kevinfrei updated https://github.com/llvm/llvm-project/pull/85693
>From 9713607cd4839ad355c7fd2e786ae7eb5a96f637 Mon Sep 17 00:00:00 2001
From: Kevin Frei <freik at meta.com>
Date: Fri, 15 Mar 2024 08:54:04 -0700
Subject: [PATCH 1/5] Tests (w/fixes) for initial DebugInfoD LLDB integration
Summary:
While adding tests for DebugInfoD integration, there were a couple issues that came up:
DWP from /debuginfo for a stripped binary needs to return symbols from /executable
Test Plan: Added some API tests
Reviewers: gclayton, hyubo
Subscribers:
Tasks: T179375937
Tags: debuginfod
Differential Revision: https://phabricator.intern.facebook.com/D54953389
---
.../Python/lldbsuite/test/make/Makefile.rules | 33 +++-
.../SymbolFile/DWARF/SymbolFileDWARF.cpp | 40 +++--
.../Plugins/SymbolLocator/CMakeLists.txt | 7 +-
.../SymbolVendor/ELF/SymbolVendorELF.cpp | 32 +++-
lldb/test/API/debuginfod/Normal/Makefile | 25 +++
.../API/debuginfod/Normal/TestDebuginfod.py | 159 +++++++++++++++++
lldb/test/API/debuginfod/Normal/main.c | 7 +
lldb/test/API/debuginfod/SplitDWARF/Makefile | 29 +++
.../SplitDWARF/TestDebuginfodDWP.py | 167 ++++++++++++++++++
lldb/test/API/debuginfod/SplitDWARF/main.c | 7 +
10 files changed, 489 insertions(+), 17 deletions(-)
create mode 100644 lldb/test/API/debuginfod/Normal/Makefile
create mode 100644 lldb/test/API/debuginfod/Normal/TestDebuginfod.py
create mode 100644 lldb/test/API/debuginfod/Normal/main.c
create mode 100644 lldb/test/API/debuginfod/SplitDWARF/Makefile
create mode 100644 lldb/test/API/debuginfod/SplitDWARF/TestDebuginfodDWP.py
create mode 100644 lldb/test/API/debuginfod/SplitDWARF/main.c
diff --git a/lldb/packages/Python/lldbsuite/test/make/Makefile.rules b/lldb/packages/Python/lldbsuite/test/make/Makefile.rules
index bfd249ccd43f2e..b33c087357a79b 100644
--- a/lldb/packages/Python/lldbsuite/test/make/Makefile.rules
+++ b/lldb/packages/Python/lldbsuite/test/make/Makefile.rules
@@ -51,7 +51,7 @@ LLDB_BASE_DIR := $(THIS_FILE_DIR)/../../../../../
#
# GNUWin32 uname gives "windows32" or "server version windows32" while
# some versions of MSYS uname return "MSYS_NT*", but most environments
-# standardize on "Windows_NT", so we'll make it consistent here.
+# standardize on "Windows_NT", so we'll make it consistent here.
# When running tests from Visual Studio, the environment variable isn't
# inherited all the way down to the process spawned for make.
#----------------------------------------------------------------------
@@ -210,6 +210,12 @@ else
ifeq "$(SPLIT_DEBUG_SYMBOLS)" "YES"
DSYM = $(EXE).debug
endif
+
+ ifeq "$(MERGE_DWOS)" "YES"
+ MAKE_DWO := YES
+ DWP_NAME = $(EXE).dwp
+ DYLIB_DWP_NAME = $(DYLIB_NAME).dwp
+ endif
endif
LIMIT_DEBUG_INFO_FLAGS =
@@ -357,6 +363,7 @@ ifneq "$(OS)" "Darwin"
OBJCOPY ?= $(call replace_cc_with,objcopy)
ARCHIVER ?= $(call replace_cc_with,ar)
+ DWP ?= $(call replace_cc_with,dwp)
override AR = $(ARCHIVER)
endif
@@ -527,6 +534,10 @@ ifneq "$(CXX)" ""
endif
endif
+ifeq "$(GEN_GNU_BUILD_ID)" "YES"
+ LDFLAGS += -Wl,--build-id
+endif
+
#----------------------------------------------------------------------
# DYLIB_ONLY variable can be used to skip the building of a.out.
# See the sections below regarding dSYM file as well as the building of
@@ -565,11 +576,25 @@ else
endif
else
ifeq "$(SPLIT_DEBUG_SYMBOLS)" "YES"
+ifeq "$(SAVE_FULL_DEBUG_BINARY)" "YES"
+ cp "$(EXE)" "$(EXE).full"
+endif
$(OBJCOPY) --only-keep-debug "$(EXE)" "$(DSYM)"
$(OBJCOPY) --strip-debug --add-gnu-debuglink="$(DSYM)" "$(EXE)" "$(EXE)"
endif
+ifeq "$(MERGE_DWOS)" "YES"
+ $(DWP) -o "$(DWP_NAME)" $(DWOS)
+endif
endif
+
+#----------------------------------------------------------------------
+# Support emitting the context of the GNU build-id into a file
+# This needs to be used in conjunction with GEN_GNU_BUILD_ID := YES
+#----------------------------------------------------------------------
+$(EXE).uuid : $(EXE)
+ $(OBJCOPY) --dump-section=.note.gnu.build-id=$@ $<
+
#----------------------------------------------------------------------
# Make the dylib
#----------------------------------------------------------------------
@@ -610,9 +635,15 @@ endif
else
$(LD) $(DYLIB_OBJECTS) $(LDFLAGS) -shared -o "$(DYLIB_FILENAME)"
ifeq "$(SPLIT_DEBUG_SYMBOLS)" "YES"
+ ifeq "$(SAVE_FULL_DEBUG_BINARY)" "YES"
+ cp "$(DYLIB_FILENAME)" "$(DYLIB_FILENAME).full"
+ endif
$(OBJCOPY) --only-keep-debug "$(DYLIB_FILENAME)" "$(DYLIB_FILENAME).debug"
$(OBJCOPY) --strip-debug --add-gnu-debuglink="$(DYLIB_FILENAME).debug" "$(DYLIB_FILENAME)" "$(DYLIB_FILENAME)"
endif
+ifeq "$(MERGE_DWOS)" "YES"
+ $(DWP) -o $(DYLIB_DWP_FILE) $(DYLIB_DWOS)
+endif
endif
#----------------------------------------------------------------------
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 5f67658f86ea96..0b7e86743f9f9f 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -4377,26 +4377,40 @@ const std::shared_ptr<SymbolFileDWARFDwo> &SymbolFileDWARF::GetDwpSymbolFile() {
FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths();
ModuleSpec module_spec;
module_spec.GetFileSpec() = m_objfile_sp->GetFileSpec();
+ FileSpec dwp_filespec;
for (const auto &symfile : symfiles.files()) {
module_spec.GetSymbolFileSpec() =
FileSpec(symfile.GetPath() + ".dwp", symfile.GetPathStyle());
LLDB_LOG(log, "Searching for DWP using: \"{0}\"",
module_spec.GetSymbolFileSpec());
- FileSpec dwp_filespec =
+ dwp_filespec =
PluginManager::LocateExecutableSymbolFile(module_spec, search_paths);
if (FileSystem::Instance().Exists(dwp_filespec)) {
- LLDB_LOG(log, "Found DWP file: \"{0}\"", dwp_filespec);
- DataBufferSP dwp_file_data_sp;
- lldb::offset_t dwp_file_data_offset = 0;
- ObjectFileSP dwp_obj_file = ObjectFile::FindPlugin(
- GetObjectFile()->GetModule(), &dwp_filespec, 0,
- FileSystem::Instance().GetByteSize(dwp_filespec), dwp_file_data_sp,
- dwp_file_data_offset);
- if (dwp_obj_file) {
- m_dwp_symfile = std::make_shared<SymbolFileDWARFDwo>(
- *this, dwp_obj_file, DIERef::k_file_index_mask);
- break;
- }
+ break;
+ }
+ }
+ if (!FileSystem::Instance().Exists(dwp_filespec)) {
+ LLDB_LOG(log, "No DWP file found locally");
+ // Fill in the UUID for the module we're trying to match for, so we can
+ // find the correct DWP file, as the Debuginfod plugin uses *only* this
+ // data to correctly match the DWP file with the binary.
+ module_spec.GetUUID() = m_objfile_sp->GetUUID();
+ dwp_filespec =
+ PluginManager::LocateExecutableSymbolFile(module_spec, search_paths);
+ // Set it back so it's not outliving the m_objfile_sp shared pointer.
+ module_spec.GetUUID() = {};
+ }
+ if (FileSystem::Instance().Exists(dwp_filespec)) {
+ LLDB_LOG(log, "Found DWP file: \"{0}\"", dwp_filespec);
+ DataBufferSP dwp_file_data_sp;
+ lldb::offset_t dwp_file_data_offset = 0;
+ ObjectFileSP dwp_obj_file = ObjectFile::FindPlugin(
+ GetObjectFile()->GetModule(), &dwp_filespec, 0,
+ FileSystem::Instance().GetByteSize(dwp_filespec), dwp_file_data_sp,
+ dwp_file_data_offset);
+ if (dwp_obj_file) {
+ m_dwp_symfile = std::make_shared<SymbolFileDWARFDwo>(
+ *this, dwp_obj_file, DIERef::k_file_index_mask);
}
}
if (!m_dwp_symfile) {
diff --git a/lldb/source/Plugins/SymbolLocator/CMakeLists.txt b/lldb/source/Plugins/SymbolLocator/CMakeLists.txt
index ca969626f4ffc4..3367022639ab85 100644
--- a/lldb/source/Plugins/SymbolLocator/CMakeLists.txt
+++ b/lldb/source/Plugins/SymbolLocator/CMakeLists.txt
@@ -1,5 +1,10 @@
+# Order matters here: the first symbol locator prevents further searching.
+# For DWARF binaries that are both stripped and split, the Default plugin
+# will return the stripped binary when asked for the ObjectFile, which then
+# prevents an unstripped binary from being requested from the Debuginfod
+# provider.
+add_subdirectory(Debuginfod)
add_subdirectory(Default)
if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
add_subdirectory(DebugSymbols)
endif()
-add_subdirectory(Debuginfod)
diff --git a/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp b/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp
index b5fe35d71032a8..cd9259a8c21630 100644
--- a/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp
+++ b/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp
@@ -44,6 +44,27 @@ llvm::StringRef SymbolVendorELF::GetPluginDescriptionStatic() {
"executables.";
}
+// If this is needed elsewhere, it can be exported/moved.
+static bool IsDwpSymbolFile(const lldb::ModuleSP &module_sp,
+ const FileSpec &file_spec) {
+ DataBufferSP dwp_file_data_sp;
+ lldb::offset_t dwp_file_data_offset = 0;
+ // Try to create an ObjectFile from the file_spec.
+ ObjectFileSP dwp_obj_file = ObjectFile::FindPlugin(
+ module_sp, &file_spec, 0, FileSystem::Instance().GetByteSize(file_spec),
+ dwp_file_data_sp, dwp_file_data_offset);
+ if (!ObjectFileELF::classof(dwp_obj_file.get()))
+ return false;
+ // The presence of a debug_cu_index section is the key identifying feature of
+ // a DWP file. Make sure we don't fill in the section list on dwp_obj_file
+ // (by calling GetSectionList(false)) as this is invoked before we may have
+ // all the symbol files collected and available.
+ if (!dwp_obj_file || !dwp_obj_file->GetSectionList(false)->FindSectionByType(
+ eSectionTypeDWARFDebugCuIndex, false))
+ return false;
+ return true;
+}
+
// CreateInstance
//
// Platforms can register a callback to use when creating symbol vendors to
@@ -87,8 +108,15 @@ SymbolVendorELF::CreateInstance(const lldb::ModuleSP &module_sp,
FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths();
FileSpec dsym_fspec =
PluginManager::LocateExecutableSymbolFile(module_spec, search_paths);
- if (!dsym_fspec)
- return nullptr;
+ if (!dsym_fspec || IsDwpSymbolFile(module_sp, dsym_fspec)) {
+ // If we have a stripped binary or if we got a DWP file, we should prefer
+ // symbols in the executable acquired through a plugin.
+ ModuleSpec unstripped_spec =
+ PluginManager::LocateExecutableObjectFile(module_spec);
+ if (!unstripped_spec)
+ return nullptr;
+ dsym_fspec = unstripped_spec.GetFileSpec();
+ }
DataBufferSP dsym_file_data_sp;
lldb::offset_t dsym_file_data_offset = 0;
diff --git a/lldb/test/API/debuginfod/Normal/Makefile b/lldb/test/API/debuginfod/Normal/Makefile
new file mode 100644
index 00000000000000..bd2fa623df473d
--- /dev/null
+++ b/lldb/test/API/debuginfod/Normal/Makefile
@@ -0,0 +1,25 @@
+C_SOURCES := main.c
+
+# For normal (non DWP) Debuginfod tests, we need:
+
+# * The "full" binary: a.out.debug
+# Produced by Makefile.rules with KEEP_FULL_DEBUG_BINARY set to YES and
+# SPLIT_DEBUG_SYMBOLS set to YES
+
+# * The stripped binary (a.out)
+# Produced by Makefile.rules with SPLIT_DEBUG_SYMBOLS set to YES
+
+# * The 'only-keep-debug' binary (a.out.dbg)
+# Produced below
+
+# * The .uuid file (for a little easier testing code)
+# Produced below
+
+# Don't strip the debug info from a.out:
+SPLIT_DEBUG_SYMBOLS := YES
+SAVE_FULL_DEBUG_BINARY := YES
+GEN_GNU_BUILD_ID := YES
+
+all: a.out.uuid a.out
+
+include Makefile.rules
diff --git a/lldb/test/API/debuginfod/Normal/TestDebuginfod.py b/lldb/test/API/debuginfod/Normal/TestDebuginfod.py
new file mode 100644
index 00000000000000..f2d3dcebeb0623
--- /dev/null
+++ b/lldb/test/API/debuginfod/Normal/TestDebuginfod.py
@@ -0,0 +1,159 @@
+import os
+import shutil
+import tempfile
+import struct
+
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.lldbtest import *
+
+
+def getUUID(aoutuuid):
+ """
+ Pull the 20 byte UUID out of the .note.gnu.build-id section that was dumped
+ to a file already, as part of the build.
+ """
+ with open(aoutuuid, "rb") as f:
+ data = f.read(36)
+ if len(data) != 36:
+ return None
+ header = struct.unpack_from("<4I", data)
+ if len(header) != 4:
+ return None
+ # 4 element 'prefix', 20 bytes of uuid, 3 byte long string: 'GNU':
+ if header[0] != 4 or header[1] != 20 or header[2] != 3 or header[3] != 0x554e47:
+ return None
+ return data[16:].hex()
+
+
+"""
+Test support for the DebugInfoD network symbol acquisition protocol.
+This one is for simple / no split-dwarf scenarios.
+
+For no-split-dwarf scenarios, there are 2 variations:
+1 - A stripped binary with it's corresponding unstripped binary:
+2 - A stripped binary with a corresponding --only-keep-debug symbols file
+"""
+class DebugInfodTests(TestBase):
+ # No need to try every flavor of debug inf.
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_normal_no_symbols(self):
+ """
+ Validate behavior with no symbols or symbol locator.
+ ('baseline negative' behavior)
+ """
+ test_root = self.config_test(["a.out"])
+ self.try_breakpoint(False)
+
+ def test_normal_default(self):
+ """
+ Validate behavior with symbols, but no symbol locator.
+ ('baseline positive' behavior)
+ """
+ test_root = self.config_test(["a.out", "a.out.debug"])
+ self.try_breakpoint(True)
+
+ def test_debuginfod_symbols(self):
+ """
+ Test behavior with the full binary available from Debuginfod as
+ 'debuginfo' from the plug-in.
+ """
+ test_root = self.config_test(["a.out"], "a.out.full")
+ self.try_breakpoint(True)
+
+ def test_debuginfod_executable(self):
+ """
+ Test behavior with the full binary available from Debuginfod as
+ 'executable' from the plug-in.
+ """
+ test_root = self.config_test(["a.out"], None, "a.out.full")
+ self.try_breakpoint(True)
+
+ def test_debuginfod_okd_symbols(self):
+ """
+ Test behavior with the 'only-keep-debug' symbols available from Debuginfod.
+ """
+ test_root = self.config_test(["a.out"], "a.out.debug")
+ self.try_breakpoint(True)
+
+ def try_breakpoint(self, should_have_loc):
+ """
+ This function creates a target from self.aout, sets a function-name
+ breakpoint, and checks to see if we have a file/line location,
+ as a way to validate that the symbols have been loaded.
+ should_have_loc specifies if we're testing that symbols have or
+ haven't been loaded.
+ """
+ target = self.dbg.CreateTarget(self.aout)
+ self.assertTrue(target and target.IsValid(), "Target is valid")
+
+ bp = target.BreakpointCreateByName("func")
+ self.assertTrue(bp and bp.IsValid(), "Breakpoint is valid")
+ self.assertEqual(bp.GetNumLocations(), 1)
+
+ loc = bp.GetLocationAtIndex(0)
+ self.assertTrue(loc and loc.IsValid(), "Location is valid")
+ addr = loc.GetAddress()
+ self.assertTrue(addr and addr.IsValid(), "Loc address is valid")
+ line_entry = addr.GetLineEntry()
+ self.assertEqual(should_have_loc, line_entry != None and line_entry.IsValid(), "Loc line entry is valid")
+ if should_have_loc:
+ self.assertEqual(line_entry.GetLine(), 4)
+ self.assertEqual(line_entry.GetFileSpec().GetFilename(), self.main_source_file.GetFilename())
+ self.dbg.DeleteTarget(target)
+ shutil.rmtree(self.tmp_dir)
+
+ def config_test(self, local_files, debuginfo = None, executable = None):
+ """
+ Set up a test with local_files[] copied to a different location
+ so that we control which files are, or are not, found in the file system.
+ Also, create a stand-alone file-system 'hosted' debuginfod server with the
+ provided debuginfo and executable files (if they exist)
+
+ Make the filesystem look like:
+
+ /tmp/<tmpdir>/test/[local_files]
+
+ /tmp/<tmpdir>/cache (for lldb to use as a temp cache)
+
+ /tmp/<tmpdir>/buildid/<uuid>/executable -> <executable>
+ /tmp/<tmpdir>/buildid/<uuid>/debuginfo -> <debuginfo>
+ Returns the /tmp/<tmpdir> path
+ """
+
+ self.build()
+
+ uuid = getUUID(self.getBuildArtifact("a.out.uuid"))
+
+ self.main_source_file = lldb.SBFileSpec("main.c")
+ self.tmp_dir = tempfile.mkdtemp()
+ test_dir = os.path.join(self.tmp_dir, "test")
+ os.makedirs(test_dir)
+
+ self.aout = ""
+ # Copy the files used by the test:
+ for f in local_files:
+ shutil.copy(self.getBuildArtifact(f), test_dir)
+ # The first item is the binary to be used for the test
+ if (self.aout == ""):
+ self.aout = os.path.join(test_dir, f)
+
+ use_debuginfod = debuginfo != None or executable != None
+
+ # Populated the 'file://... mocked' Debuginfod server:
+ if use_debuginfod:
+ os.makedirs(os.path.join(self.tmp_dir, "cache"))
+ uuid_dir = os.path.join(self.tmp_dir, "buildid", uuid)
+ os.makedirs(uuid_dir)
+ if debuginfo:
+ shutil.copy(self.getBuildArtifact(debuginfo), os.path.join(uuid_dir, "debuginfo"))
+ if executable:
+ shutil.copy(self.getBuildArtifact(executable), os.path.join(uuid_dir, "executable"))
+
+ # Configure LLDB for the test:
+ self.runCmd("settings set symbols.enable-external-lookup %s" % str(use_debuginfod).lower())
+ self.runCmd("settings clear plugin.symbol-locator.debuginfod.server-urls")
+ if use_debuginfod:
+ self.runCmd("settings set plugin.symbol-locator.debuginfod.cache-path %s/cache" % self.tmp_dir)
+ self.runCmd("settings insert-before plugin.symbol-locator.debuginfod.server-urls 0 file://%s" % self.tmp_dir)
diff --git a/lldb/test/API/debuginfod/Normal/main.c b/lldb/test/API/debuginfod/Normal/main.c
new file mode 100644
index 00000000000000..4c7184609b4536
--- /dev/null
+++ b/lldb/test/API/debuginfod/Normal/main.c
@@ -0,0 +1,7 @@
+// This is a dump little pair of test files
+
+int func(int argc, const char *argv[]) {
+ return (argc + 1) * (argv[argc][0] + 2);
+}
+
+int main(int argc, const char *argv[]) { return func(0, argv); }
diff --git a/lldb/test/API/debuginfod/SplitDWARF/Makefile b/lldb/test/API/debuginfod/SplitDWARF/Makefile
new file mode 100644
index 00000000000000..89e00378fa2aa9
--- /dev/null
+++ b/lldb/test/API/debuginfod/SplitDWARF/Makefile
@@ -0,0 +1,29 @@
+C_SOURCES := main.c
+
+# For split-dwarf Debuginfod tests, we need:
+
+# * A .DWP file (a.out.dwp)
+# Produced by Makefile.rules with MAKE_DWO and MERGE_DWOS both set to YES
+
+# * The "full" binary: it's missing things that live in .dwo's (a.out.debug)
+# Produced by Makefile.rules with KEEP_FULL_DEBUG_BINARY set to YES and
+# SPLIT_DEBUG_SYMBOLS set to YES
+
+# * The stripped binary (a.out)
+# Produced by Makefile.rules
+
+# * The 'only-keep-debug' binary (a.out.dbg)
+# Produced below
+
+# * The .uuid file (for a little easier testing code)
+# Produced here in the rule below
+
+MAKE_DWO := YES
+MERGE_DWOS := YES
+SPLIT_DEBUG_SYMBOLS := YES
+SAVE_FULL_DEBUG_BINARY := YES
+GEN_GNU_BUILD_ID := YES
+
+all: a.out.uuid a.out
+
+include Makefile.rules
diff --git a/lldb/test/API/debuginfod/SplitDWARF/TestDebuginfodDWP.py b/lldb/test/API/debuginfod/SplitDWARF/TestDebuginfodDWP.py
new file mode 100644
index 00000000000000..fe0d1f686b1f44
--- /dev/null
+++ b/lldb/test/API/debuginfod/SplitDWARF/TestDebuginfodDWP.py
@@ -0,0 +1,167 @@
+"""
+Test support for the DebugInfoD network symbol acquisition protocol.
+"""
+import os
+import shutil
+import tempfile
+import struct
+
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.lldbtest import *
+
+
+def getUUID(aoutuuid):
+ """
+ Pull the 20 byte UUID out of the .note.gnu.build-id section that was dumped
+ to a file already, as part of the build.
+ """
+ with open(aoutuuid, "rb") as f:
+ data = f.read(36)
+ if len(data) != 36:
+ return None
+ header = struct.unpack_from("<4I", data)
+ if len(header) != 4:
+ return None
+ # 4 element 'prefix', 20 bytes of uuid, 3 byte long string: 'GNU':
+ if header[0] != 4 or header[1] != 20 or header[2] != 3 or header[3] != 0x554e47:
+ return None
+ return data[16:].hex()
+
+"""
+Test support for the DebugInfoD network symbol acquisition protocol.
+This file is for split-dwarf (dwp) scenarios.
+
+1 - A split binary target with it's corresponding DWP file
+2 - A stripped, split binary target with an unstripped binary and a DWP file
+3 - A stripped, split binary target with an --only-keep-debug symbols file and a DWP file
+"""
+class DebugInfodDWPTests(TestBase):
+ # No need to try every flavor of debug inf.
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_normal_stripped(self):
+ """
+ Validate behavior with a stripped binary, no symbols or symbol locator.
+ """
+ self.config_test(["a.out"])
+ self.try_breakpoint(False)
+
+ def test_normal_stripped_split_with_dwp(self):
+ """
+ Validate behavior with symbols, but no symbol locator.
+ """
+ self.config_test(["a.out", "a.out.debug", "a.out.dwp"])
+ self.try_breakpoint(True)
+
+ def test_normal_stripped_only_dwp(self):
+ """
+ Validate behavior *with* dwp symbols only, but missing other symbols,
+ but no symbol locator. This shouldn't work: without the other symbols
+ DWO's appear mostly useless.
+ """
+ self.config_test(["a.out", "a.out.dwp"])
+ self.try_breakpoint(False)
+
+ def test_debuginfod_dwp_from_service(self):
+ """
+ Test behavior with the unstripped binary, and DWP from the service.
+ """
+ self.config_test(["a.out.debug"], "a.out.dwp")
+ self.try_breakpoint(True)
+
+ def test_debuginfod_both_symfiles_from_service(self):
+ """
+ Test behavior with a stripped binary, with the unstripped binary and
+ dwp symbols from Debuginfod.
+ """
+ self.config_test(["a.out"], "a.out.dwp", "a.out.full")
+ self.try_breakpoint(True)
+
+ def test_debuginfod_both_okd_symfiles_from_service(self):
+ """
+ Test behavior with both the only-keep-debug symbols and the dwp symbols
+ from Debuginfod.
+ """
+ self.config_test(["a.out"], "a.out.dwp", "a.out.debug")
+ self.try_breakpoint(True)
+
+ def try_breakpoint(self, should_have_loc):
+ """
+ This function creates a target from self.aout, sets a function-name
+ breakpoint, and checks to see if we have a file/line location,
+ as a way to validate that the symbols have been loaded.
+ should_have_loc specifies if we're testing that symbols have or
+ haven't been loaded.
+ """
+ target = self.dbg.CreateTarget(self.aout)
+ self.assertTrue(target and target.IsValid(), "Target is valid")
+
+ bp = target.BreakpointCreateByName("func")
+ self.assertTrue(bp and bp.IsValid(), "Breakpoint is valid")
+ self.assertEqual(bp.GetNumLocations(), 1)
+
+ loc = bp.GetLocationAtIndex(0)
+ self.assertTrue(loc and loc.IsValid(), "Location is valid")
+ addr = loc.GetAddress()
+ self.assertTrue(addr and addr.IsValid(), "Loc address is valid")
+ line_entry = addr.GetLineEntry()
+ self.assertEqual(should_have_loc, line_entry != None and line_entry.IsValid(), "Loc line entry is valid")
+ if should_have_loc:
+ self.assertEqual(line_entry.GetLine(), 4)
+ self.assertEqual(line_entry.GetFileSpec().GetFilename(), self.main_source_file.GetFilename())
+ self.dbg.DeleteTarget(target)
+ shutil.rmtree(self.tmp_dir)
+
+ def config_test(self, local_files, debuginfo = None, executable = None):
+ """
+ Set up a test with local_files[] copied to a different location
+ so that we control which files are, or are not, found in the file system.
+ Also, create a stand-alone file-system 'hosted' debuginfod server with the
+ provided debuginfo and executable files (if they exist)
+
+ Make the filesystem look like:
+
+ /tmp/<tmpdir>/test/[local_files]
+
+ /tmp/<tmpdir>/cache (for lldb to use as a temp cache)
+
+ /tmp/<tmpdir>/buildid/<uuid>/executable -> <executable>
+ /tmp/<tmpdir>/buildid/<uuid>/debuginfo -> <debuginfo>
+ Returns the /tmp/<tmpdir> path
+ """
+
+ self.build()
+
+ uuid = getUUID(self.getBuildArtifact("a.out.uuid"))
+
+ self.main_source_file = lldb.SBFileSpec("main.c")
+ self.tmp_dir = tempfile.mkdtemp()
+ self.test_dir = os.path.join(self.tmp_dir, "test")
+ os.makedirs(self.test_dir)
+
+ self.aout = ""
+ # Copy the files used by the test:
+ for f in local_files:
+ shutil.copy(self.getBuildArtifact(f), self.test_dir)
+ if (self.aout == ""):
+ self.aout = os.path.join(self.test_dir, f)
+
+ use_debuginfod = debuginfo != None or executable != None
+
+ # Populated the 'file://... mocked' Debuginfod server:
+ if use_debuginfod:
+ os.makedirs(os.path.join(self.tmp_dir, "cache"))
+ uuid_dir = os.path.join(self.tmp_dir, "buildid", uuid)
+ os.makedirs(uuid_dir)
+ if debuginfo:
+ shutil.copy(self.getBuildArtifact(debuginfo), os.path.join(uuid_dir, "debuginfo"))
+ if executable:
+ shutil.copy(self.getBuildArtifact(executable), os.path.join(uuid_dir, "executable"))
+ os.remove(self.getBuildArtifact("main.dwo"))
+ # Configure LLDB for the test:
+ self.runCmd("settings set symbols.enable-external-lookup %s" % str(use_debuginfod).lower())
+ self.runCmd("settings clear plugin.symbol-locator.debuginfod.server-urls")
+ if use_debuginfod:
+ self.runCmd("settings set plugin.symbol-locator.debuginfod.cache-path %s/cache" % self.tmp_dir)
+ self.runCmd("settings insert-before plugin.symbol-locator.debuginfod.server-urls 0 file://%s" % self.tmp_dir)
diff --git a/lldb/test/API/debuginfod/SplitDWARF/main.c b/lldb/test/API/debuginfod/SplitDWARF/main.c
new file mode 100644
index 00000000000000..4c7184609b4536
--- /dev/null
+++ b/lldb/test/API/debuginfod/SplitDWARF/main.c
@@ -0,0 +1,7 @@
+// This is a dump little pair of test files
+
+int func(int argc, const char *argv[]) {
+ return (argc + 1) * (argv[argc][0] + 2);
+}
+
+int main(int argc, const char *argv[]) { return func(0, argv); }
>From 2998d958d242210601678e40606720520259ecd7 Mon Sep 17 00:00:00 2001
From: Kevin Frei <freik at meta.com>
Date: Mon, 18 Mar 2024 11:56:42 -0700
Subject: [PATCH 2/5] Skip the tests for non-*nix platforms
---
lldb/test/API/debuginfod/Normal/TestDebuginfod.py | 2 ++
lldb/test/API/debuginfod/SplitDWARF/TestDebuginfodDWP.py | 2 ++
2 files changed, 4 insertions(+)
diff --git a/lldb/test/API/debuginfod/Normal/TestDebuginfod.py b/lldb/test/API/debuginfod/Normal/TestDebuginfod.py
index f2d3dcebeb0623..78fa8aaeea2e59 100644
--- a/lldb/test/API/debuginfod/Normal/TestDebuginfod.py
+++ b/lldb/test/API/debuginfod/Normal/TestDebuginfod.py
@@ -4,6 +4,7 @@
import struct
import lldb
+from lldbsuite.test.decorators import *
import lldbsuite.test.lldbutil as lldbutil
from lldbsuite.test.lldbtest import *
@@ -34,6 +35,7 @@ def getUUID(aoutuuid):
1 - A stripped binary with it's corresponding unstripped binary:
2 - A stripped binary with a corresponding --only-keep-debug symbols file
"""
+ at skipUnlessPlatform(["linux", "freebsd"])
class DebugInfodTests(TestBase):
# No need to try every flavor of debug inf.
NO_DEBUG_INFO_TESTCASE = True
diff --git a/lldb/test/API/debuginfod/SplitDWARF/TestDebuginfodDWP.py b/lldb/test/API/debuginfod/SplitDWARF/TestDebuginfodDWP.py
index fe0d1f686b1f44..638b4aa7207fb9 100644
--- a/lldb/test/API/debuginfod/SplitDWARF/TestDebuginfodDWP.py
+++ b/lldb/test/API/debuginfod/SplitDWARF/TestDebuginfodDWP.py
@@ -7,6 +7,7 @@
import struct
import lldb
+from lldbsuite.test.decorators import *
import lldbsuite.test.lldbutil as lldbutil
from lldbsuite.test.lldbtest import *
@@ -36,6 +37,7 @@ def getUUID(aoutuuid):
2 - A stripped, split binary target with an unstripped binary and a DWP file
3 - A stripped, split binary target with an --only-keep-debug symbols file and a DWP file
"""
+ at skipUnlessPlatform(["linux", "freebsd"])
class DebugInfodDWPTests(TestBase):
# No need to try every flavor of debug inf.
NO_DEBUG_INFO_TESTCASE = True
>From b00e998a6f5d23ae51a11db859f9053fd782f85b Mon Sep 17 00:00:00 2001
From: Kevin Frei <freik at meta.com>
Date: Mon, 18 Mar 2024 13:28:52 -0700
Subject: [PATCH 3/5] Removed an inadvertent auto-formatter change
---
lldb/packages/Python/lldbsuite/test/make/Makefile.rules | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lldb/packages/Python/lldbsuite/test/make/Makefile.rules b/lldb/packages/Python/lldbsuite/test/make/Makefile.rules
index b33c087357a79b..370db60ab7f7d6 100644
--- a/lldb/packages/Python/lldbsuite/test/make/Makefile.rules
+++ b/lldb/packages/Python/lldbsuite/test/make/Makefile.rules
@@ -51,7 +51,7 @@ LLDB_BASE_DIR := $(THIS_FILE_DIR)/../../../../../
#
# GNUWin32 uname gives "windows32" or "server version windows32" while
# some versions of MSYS uname return "MSYS_NT*", but most environments
-# standardize on "Windows_NT", so we'll make it consistent here.
+# standardize on "Windows_NT", so we'll make it consistent here.
# When running tests from Visual Studio, the environment variable isn't
# inherited all the way down to the process spawned for make.
#----------------------------------------------------------------------
@@ -589,7 +589,7 @@ endif
#----------------------------------------------------------------------
-# Support emitting the context of the GNU build-id into a file
+# Support emitting the content of the GNU build-id into a file
# This needs to be used in conjunction with GEN_GNU_BUILD_ID := YES
#----------------------------------------------------------------------
$(EXE).uuid : $(EXE)
>From b97ed48c8891da05b749e13b23414c6db9e07915 Mon Sep 17 00:00:00 2001
From: Kevin Frei <freik at meta.com>
Date: Mon, 18 Mar 2024 14:37:33 -0700
Subject: [PATCH 4/5] Fixed formatting for the Python tests
---
.../API/debuginfod/Normal/TestDebuginfod.py | 44 +++++++++++++-----
.../SplitDWARF/TestDebuginfodDWP.py | 45 ++++++++++++++-----
2 files changed, 69 insertions(+), 20 deletions(-)
diff --git a/lldb/test/API/debuginfod/Normal/TestDebuginfod.py b/lldb/test/API/debuginfod/Normal/TestDebuginfod.py
index 78fa8aaeea2e59..eb5efe83c17aa6 100644
--- a/lldb/test/API/debuginfod/Normal/TestDebuginfod.py
+++ b/lldb/test/API/debuginfod/Normal/TestDebuginfod.py
@@ -22,7 +22,7 @@ def getUUID(aoutuuid):
if len(header) != 4:
return None
# 4 element 'prefix', 20 bytes of uuid, 3 byte long string: 'GNU':
- if header[0] != 4 or header[1] != 20 or header[2] != 3 or header[3] != 0x554e47:
+ if header[0] != 4 or header[1] != 20 or header[2] != 3 or header[3] != 0x554E47:
return None
return data[16:].hex()
@@ -35,6 +35,8 @@ def getUUID(aoutuuid):
1 - A stripped binary with it's corresponding unstripped binary:
2 - A stripped binary with a corresponding --only-keep-debug symbols file
"""
+
+
@skipUnlessPlatform(["linux", "freebsd"])
class DebugInfodTests(TestBase):
# No need to try every flavor of debug inf.
@@ -99,14 +101,21 @@ def try_breakpoint(self, should_have_loc):
addr = loc.GetAddress()
self.assertTrue(addr and addr.IsValid(), "Loc address is valid")
line_entry = addr.GetLineEntry()
- self.assertEqual(should_have_loc, line_entry != None and line_entry.IsValid(), "Loc line entry is valid")
+ self.assertEqual(
+ should_have_loc,
+ line_entry != None and line_entry.IsValid(),
+ "Loc line entry is valid",
+ )
if should_have_loc:
self.assertEqual(line_entry.GetLine(), 4)
- self.assertEqual(line_entry.GetFileSpec().GetFilename(), self.main_source_file.GetFilename())
+ self.assertEqual(
+ line_entry.GetFileSpec().GetFilename(),
+ self.main_source_file.GetFilename(),
+ )
self.dbg.DeleteTarget(target)
shutil.rmtree(self.tmp_dir)
- def config_test(self, local_files, debuginfo = None, executable = None):
+ def config_test(self, local_files, debuginfo=None, executable=None):
"""
Set up a test with local_files[] copied to a different location
so that we control which files are, or are not, found in the file system.
@@ -138,7 +147,7 @@ def config_test(self, local_files, debuginfo = None, executable = None):
for f in local_files:
shutil.copy(self.getBuildArtifact(f), test_dir)
# The first item is the binary to be used for the test
- if (self.aout == ""):
+ if self.aout == "":
self.aout = os.path.join(test_dir, f)
use_debuginfod = debuginfo != None or executable != None
@@ -149,13 +158,28 @@ def config_test(self, local_files, debuginfo = None, executable = None):
uuid_dir = os.path.join(self.tmp_dir, "buildid", uuid)
os.makedirs(uuid_dir)
if debuginfo:
- shutil.copy(self.getBuildArtifact(debuginfo), os.path.join(uuid_dir, "debuginfo"))
+ shutil.copy(
+ self.getBuildArtifact(debuginfo),
+ os.path.join(uuid_dir, "debuginfo"),
+ )
if executable:
- shutil.copy(self.getBuildArtifact(executable), os.path.join(uuid_dir, "executable"))
+ shutil.copy(
+ self.getBuildArtifact(executable),
+ os.path.join(uuid_dir, "executable"),
+ )
# Configure LLDB for the test:
- self.runCmd("settings set symbols.enable-external-lookup %s" % str(use_debuginfod).lower())
+ self.runCmd(
+ "settings set symbols.enable-external-lookup %s"
+ % str(use_debuginfod).lower()
+ )
self.runCmd("settings clear plugin.symbol-locator.debuginfod.server-urls")
if use_debuginfod:
- self.runCmd("settings set plugin.symbol-locator.debuginfod.cache-path %s/cache" % self.tmp_dir)
- self.runCmd("settings insert-before plugin.symbol-locator.debuginfod.server-urls 0 file://%s" % self.tmp_dir)
+ self.runCmd(
+ "settings set plugin.symbol-locator.debuginfod.cache-path %s/cache"
+ % self.tmp_dir
+ )
+ self.runCmd(
+ "settings insert-before plugin.symbol-locator.debuginfod.server-urls 0 file://%s"
+ % self.tmp_dir
+ )
diff --git a/lldb/test/API/debuginfod/SplitDWARF/TestDebuginfodDWP.py b/lldb/test/API/debuginfod/SplitDWARF/TestDebuginfodDWP.py
index 638b4aa7207fb9..09f91b6f1c6c32 100644
--- a/lldb/test/API/debuginfod/SplitDWARF/TestDebuginfodDWP.py
+++ b/lldb/test/API/debuginfod/SplitDWARF/TestDebuginfodDWP.py
@@ -25,10 +25,11 @@ def getUUID(aoutuuid):
if len(header) != 4:
return None
# 4 element 'prefix', 20 bytes of uuid, 3 byte long string: 'GNU':
- if header[0] != 4 or header[1] != 20 or header[2] != 3 or header[3] != 0x554e47:
+ if header[0] != 4 or header[1] != 20 or header[2] != 3 or header[3] != 0x554E47:
return None
return data[16:].hex()
+
"""
Test support for the DebugInfoD network symbol acquisition protocol.
This file is for split-dwarf (dwp) scenarios.
@@ -37,6 +38,8 @@ def getUUID(aoutuuid):
2 - A stripped, split binary target with an unstripped binary and a DWP file
3 - A stripped, split binary target with an --only-keep-debug symbols file and a DWP file
"""
+
+
@skipUnlessPlatform(["linux", "freebsd"])
class DebugInfodDWPTests(TestBase):
# No need to try every flavor of debug inf.
@@ -108,14 +111,21 @@ def try_breakpoint(self, should_have_loc):
addr = loc.GetAddress()
self.assertTrue(addr and addr.IsValid(), "Loc address is valid")
line_entry = addr.GetLineEntry()
- self.assertEqual(should_have_loc, line_entry != None and line_entry.IsValid(), "Loc line entry is valid")
+ self.assertEqual(
+ should_have_loc,
+ line_entry != None and line_entry.IsValid(),
+ "Loc line entry is valid",
+ )
if should_have_loc:
self.assertEqual(line_entry.GetLine(), 4)
- self.assertEqual(line_entry.GetFileSpec().GetFilename(), self.main_source_file.GetFilename())
+ self.assertEqual(
+ line_entry.GetFileSpec().GetFilename(),
+ self.main_source_file.GetFilename(),
+ )
self.dbg.DeleteTarget(target)
shutil.rmtree(self.tmp_dir)
- def config_test(self, local_files, debuginfo = None, executable = None):
+ def config_test(self, local_files, debuginfo=None, executable=None):
"""
Set up a test with local_files[] copied to a different location
so that we control which files are, or are not, found in the file system.
@@ -146,7 +156,7 @@ def config_test(self, local_files, debuginfo = None, executable = None):
# Copy the files used by the test:
for f in local_files:
shutil.copy(self.getBuildArtifact(f), self.test_dir)
- if (self.aout == ""):
+ if self.aout == "":
self.aout = os.path.join(self.test_dir, f)
use_debuginfod = debuginfo != None or executable != None
@@ -157,13 +167,28 @@ def config_test(self, local_files, debuginfo = None, executable = None):
uuid_dir = os.path.join(self.tmp_dir, "buildid", uuid)
os.makedirs(uuid_dir)
if debuginfo:
- shutil.copy(self.getBuildArtifact(debuginfo), os.path.join(uuid_dir, "debuginfo"))
+ shutil.copy(
+ self.getBuildArtifact(debuginfo),
+ os.path.join(uuid_dir, "debuginfo"),
+ )
if executable:
- shutil.copy(self.getBuildArtifact(executable), os.path.join(uuid_dir, "executable"))
+ shutil.copy(
+ self.getBuildArtifact(executable),
+ os.path.join(uuid_dir, "executable"),
+ )
os.remove(self.getBuildArtifact("main.dwo"))
# Configure LLDB for the test:
- self.runCmd("settings set symbols.enable-external-lookup %s" % str(use_debuginfod).lower())
+ self.runCmd(
+ "settings set symbols.enable-external-lookup %s"
+ % str(use_debuginfod).lower()
+ )
self.runCmd("settings clear plugin.symbol-locator.debuginfod.server-urls")
if use_debuginfod:
- self.runCmd("settings set plugin.symbol-locator.debuginfod.cache-path %s/cache" % self.tmp_dir)
- self.runCmd("settings insert-before plugin.symbol-locator.debuginfod.server-urls 0 file://%s" % self.tmp_dir)
+ self.runCmd(
+ "settings set plugin.symbol-locator.debuginfod.cache-path %s/cache"
+ % self.tmp_dir
+ )
+ self.runCmd(
+ "settings insert-before plugin.symbol-locator.debuginfod.server-urls 0 file://%s"
+ % self.tmp_dir
+ )
>From 031a23477c43edc7e7d78c235ebaea332f3a35d9 Mon Sep 17 00:00:00 2001
From: Kevin Frei <freik at meta.com>
Date: Tue, 19 Mar 2024 08:24:49 -0700
Subject: [PATCH 5/5] Address PR feedback from JDevlieghere
---
lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp | 2 --
lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp | 6 ++----
2 files changed, 2 insertions(+), 6 deletions(-)
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 0b7e86743f9f9f..08ce7b82b0c16a 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -4397,8 +4397,6 @@ const std::shared_ptr<SymbolFileDWARFDwo> &SymbolFileDWARF::GetDwpSymbolFile() {
module_spec.GetUUID() = m_objfile_sp->GetUUID();
dwp_filespec =
PluginManager::LocateExecutableSymbolFile(module_spec, search_paths);
- // Set it back so it's not outliving the m_objfile_sp shared pointer.
- module_spec.GetUUID() = {};
}
if (FileSystem::Instance().Exists(dwp_filespec)) {
LLDB_LOG(log, "Found DWP file: \"{0}\"", dwp_filespec);
diff --git a/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp b/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp
index cd9259a8c21630..91b8b4a979e0c7 100644
--- a/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp
+++ b/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp
@@ -59,10 +59,8 @@ static bool IsDwpSymbolFile(const lldb::ModuleSP &module_sp,
// a DWP file. Make sure we don't fill in the section list on dwp_obj_file
// (by calling GetSectionList(false)) as this is invoked before we may have
// all the symbol files collected and available.
- if (!dwp_obj_file || !dwp_obj_file->GetSectionList(false)->FindSectionByType(
- eSectionTypeDWARFDebugCuIndex, false))
- return false;
- return true;
+ return dwp_obj_file && dwp_obj_file->GetSectionList(false)->FindSectionByType(
+ eSectionTypeDWARFDebugCuIndex, false);
}
// CreateInstance
More information about the lldb-commits
mailing list