[lldb][debuginfod] Fix the DebugInfoD PR that caused issues when working with stripped binaries (#99362)

@walter-erquinigo found the the [PR with testing and a fix for
DebugInfoD](https://github.com/llvm/llvm-project/pull/98344) caused an
issue when working with stripped binaries.

The issue is that when you're working with split-dwarf, there are *3*
possible files: The stripped binary the user is debugging, the
"only-keep-debug" *or* unstripped binary, plus the `.dwp` file. The
debuginfod plugin should provide the unstripped/OKD binary. However, if
the debuginfod plugin fails, the default symbol locator plugin will just
return the stripped binary, which doesn't help. So, to address that, the
SymbolVendorELF code checks to see if the SymbolLocator's
ExecutableObjectFile request returned the same file, and bails if that's
the case. You can see the specific diff as the second commit in the PR.

I'm investigating adding a test: I can't quite get a simple repro, and
I'm unwilling to make any additional changes to Makefile.rules to this
diff, for Pavlovian reasons.
This commit is contained in:
Kevin Frei 2024-08-06 11:06:04 -07:00 committed by GitHub
parent 3983bf6040
commit e77ac42bcc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 549 additions and 21 deletions

View File

@ -33,6 +33,8 @@
#cmakedefine01 LLDB_ENABLE_LZMA
#cmakedefine01 LLVM_ENABLE_CURL
#cmakedefine01 LLDB_ENABLE_CURSES
#cmakedefine01 CURSES_HAVE_NCURSES_CURSES_H

View File

@ -1053,6 +1053,10 @@ def _get_bool_config_skip_if_decorator(key):
return unittest.skipIf(not have, "requires " + key)
def skipIfCurlSupportMissing(func):
return _get_bool_config_skip_if_decorator("curl")(func)
def skipIfCursesSupportMissing(func):
return _get_bool_config_skip_if_decorator("curses")(func)

View File

@ -202,6 +202,12 @@ else
ifeq "$(SPLIT_DEBUG_SYMBOLS)" "YES"
DSYM = $(EXE).debug
endif
ifeq "$(MAKE_DWP)" "YES"
MAKE_DWO := YES
DWP_NAME = $(EXE).dwp
DYLIB_DWP_NAME = $(DYLIB_NAME).dwp
endif
endif
LIMIT_DEBUG_INFO_FLAGS =
@ -347,6 +353,17 @@ ifneq "$(OS)" "Darwin"
OBJCOPY ?= $(call replace_cc_with,objcopy)
ARCHIVER ?= $(call replace_cc_with,ar)
# Look for llvm-dwp or gnu dwp
DWP ?= $(call replace_cc_with,llvm-dwp)
ifeq ($(wildcard $(DWP)),)
DWP = $(call replace_cc_with,dwp)
ifeq ($(wildcard $(DWP)),)
DWP = $(shell command -v llvm-dwp 2> /dev/null)
ifeq ($(wildcard $(DWP)),)
DWP = $(shell command -v dwp 2> /dev/null)
endif
endif
endif
override AR = $(ARCHIVER)
endif
@ -526,6 +543,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
@ -564,10 +585,17 @@ else
endif
else
ifeq "$(SPLIT_DEBUG_SYMBOLS)" "YES"
ifeq "$(SAVE_FULL_DEBUG_BINARY)" "YES"
cp "$(EXE)" "$(EXE).unstripped"
endif
$(OBJCOPY) --only-keep-debug "$(EXE)" "$(DSYM)"
$(OBJCOPY) --strip-debug --add-gnu-debuglink="$(DSYM)" "$(EXE)" "$(EXE)"
endif
ifeq "$(MAKE_DWP)" "YES"
$(DWP) -o "$(DWP_NAME)" $(DWOS)
endif
endif
#----------------------------------------------------------------------
# Make the dylib
@ -609,9 +637,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).unstripped"
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 "$(MAKE_DWP)" "YES"
$(DWP) -o $(DYLIB_DWP_FILE) $(DYLIB_DWOS)
endif
endif
#----------------------------------------------------------------------

View File

@ -775,6 +775,9 @@ SBStructuredData SBDebugger::GetBuildConfiguration() {
AddBoolConfigEntry(
*config_up, "xml", XMLDocument::XMLEnabled(),
"A boolean value that indicates if XML support is enabled in LLDB");
AddBoolConfigEntry(
*config_up, "curl", LLVM_ENABLE_CURL,
"A boolean value that indicates if CURL support is enabled in LLDB");
AddBoolConfigEntry(
*config_up, "curses", LLDB_ENABLE_CURSES,
"A boolean value that indicates if curses support is enabled in LLDB");
@ -1724,20 +1727,20 @@ SBDebugger::LoadTraceFromFile(SBError &error,
void SBDebugger::RequestInterrupt() {
LLDB_INSTRUMENT_VA(this);
if (m_opaque_sp)
m_opaque_sp->RequestInterrupt();
m_opaque_sp->RequestInterrupt();
}
void SBDebugger::CancelInterruptRequest() {
LLDB_INSTRUMENT_VA(this);
if (m_opaque_sp)
m_opaque_sp->CancelInterruptRequest();
m_opaque_sp->CancelInterruptRequest();
}
bool SBDebugger::InterruptRequested() {
LLDB_INSTRUMENT_VA(this);
if (m_opaque_sp)
return m_opaque_sp->InterruptRequested();
return false;

View File

@ -4331,26 +4331,38 @@ 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);
}
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) {

View File

@ -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)

View File

@ -44,6 +44,24 @@ 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);
// 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 function could be called before
// we may have all the symbol files collected and available.
return dwp_obj_file && ObjectFileELF::classof(dwp_obj_file.get()) &&
dwp_obj_file->GetSectionList(false)->FindSectionByType(
eSectionTypeDWARFDebugCuIndex, false);
}
// CreateInstance
//
// Platforms can register a callback to use when creating symbol vendors to
@ -87,8 +105,20 @@ 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 have a DWP file, SymbolLocator
// plugins may be able to give us an unstripped binary or an
// 'only-keep-debug' stripped file.
ModuleSpec unstripped_spec =
PluginManager::LocateExecutableObjectFile(module_spec);
if (!unstripped_spec)
return nullptr;
// The default SymbolLocator plugin returns the original binary if no other
// plugin finds something better.
if (unstripped_spec.GetFileSpec() == module_spec.GetFileSpec())
return nullptr;
dsym_fspec = unstripped_spec.GetFileSpec();
}
DataBufferSP dsym_file_data_sp;
lldb::offset_t dsym_file_data_offset = 0;

View File

@ -0,0 +1,19 @@
C_SOURCES := main.c
# For normal (non DWP) Debuginfod tests, we need:
# * The full binary: a.out.unstripped
# Produced by Makefile.rules with SAVE_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.debug)
# Produced below
SPLIT_DEBUG_SYMBOLS := YES
SAVE_FULL_DEBUG_BINARY := YES
GEN_GNU_BUILD_ID := YES
include Makefile.rules

View File

@ -0,0 +1,186 @@
import os
import shutil
import tempfile
import lldb
from lldbsuite.test.decorators import *
import lldbsuite.test.lldbutil as lldbutil
from lldbsuite.test.lldbtest import *
"""
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
@skipUnlessPlatform(["linux", "freebsd"])
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)
@skipUnlessPlatform(["linux", "freebsd"])
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)
@skipIfCurlSupportMissing
@skipUnlessPlatform(["linux", "freebsd"])
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.unstripped")
self.try_breakpoint(True)
@skipIfCurlSupportMissing
@skipUnlessPlatform(["linux", "freebsd"])
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.unstripped")
self.try_breakpoint(True)
@skipIfCurlSupportMissing
@skipUnlessPlatform(["linux", "freebsd"])
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 = self.getUUID("a.out")
if not uuid:
self.fail("Could not get UUID for a.out")
return
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
)
def getUUID(self, filename):
try:
spec = lldb.SBModuleSpec()
spec.SetFileSpec(lldb.SBFileSpec(self.getBuildArtifact(filename)))
module = lldb.SBModule(spec)
uuid = module.GetUUIDString().replace("-", "").lower()
# Don't want lldb's fake 32 bit CRC's for this one
return uuid if len(uuid) > 8 else None
except:
return None

View File

@ -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); }

View File

@ -0,0 +1,23 @@
C_SOURCES := main.c
# For split-dwarf Debuginfod tests, we need:
# * A .DWP file (a.out.dwp)
# Produced by Makefile.rules with MAKE_DWP set to YES
# * The "full" binary (missing things that live in .dwo's) (a.out.unstripped)
# Produced by Makefile.rules with SAVE_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.debug)
# Produced below
MAKE_DWP := YES
SPLIT_DEBUG_SYMBOLS := YES
SAVE_FULL_DEBUG_BINARY := YES
GEN_GNU_BUILD_ID := YES
include Makefile.rules

View File

@ -0,0 +1,196 @@
"""
Test support for the DebugInfoD network symbol acquisition protocol.
"""
import os
import shutil
import tempfile
import lldb
from lldbsuite.test.decorators import *
import lldbsuite.test.lldbutil as lldbutil
from lldbsuite.test.lldbtest import *
"""
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
@skipUnlessPlatform(["linux_freebsd_but_old_dwp_tools_on_build_bots_are_broken"])
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)
@skipUnlessPlatform(["linux_freebsd_but_old_dwp_tools_on_build_bots_are_broken"])
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)
@skipUnlessPlatform(["linux_freebsd_but_old_dwp_tools_on_build_bots_are_broken"])
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)
@skipIfCurlSupportMissing
@skipUnlessPlatform(["linux_freebsd_but_old_dwp_tools_on_build_bots_are_broken"])
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)
@skipIfCurlSupportMissing
@skipUnlessPlatform(["linux_freebsd_but_old_dwp_tools_on_build_bots_are_broken"])
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.unstripped")
self.try_breakpoint(True)
@skipIfCurlSupportMissing
@skipUnlessPlatform(["linux_freebsd_but_old_dwp_tools_on_build_bots_are_broken"])
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 = self.getUUID("a.out")
if not uuid:
self.fail("Could not get UUID for a.out")
return
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
)
def getUUID(self, filename):
try:
spec = lldb.SBModuleSpec()
spec.SetFileSpec(lldb.SBFileSpec(self.getBuildArtifact(filename)))
module = lldb.SBModule(spec)
uuid = module.GetUUIDString().replace("-", "").lower()
# Don't want lldb's fake 32 bit CRC's for this one
return uuid if len(uuid) > 8 else None
except:
return None

View File

@ -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); }