| """ | |
| 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 |