1""" 2Test support for the DebugInfoD network symbol acquisition protocol. 3""" 4import os 5import shutil 6import tempfile 7 8import lldb 9from lldbsuite.test.decorators import * 10import lldbsuite.test.lldbutil as lldbutil 11from lldbsuite.test.lldbtest import * 12 13 14""" 15Test support for the DebugInfoD network symbol acquisition protocol. 16This file is for split-dwarf (dwp) scenarios. 17 181 - A split binary target with it's corresponding DWP file 192 - A stripped, split binary target with an unstripped binary and a DWP file 203 - A stripped, split binary target with an --only-keep-debug symbols file and a DWP file 21""" 22 23 24class DebugInfodDWPTests(TestBase): 25 # No need to try every flavor of debug inf. 26 NO_DEBUG_INFO_TESTCASE = True 27 28 @skipUnlessPlatform(["linux_freebsd_but_old_dwp_tools_on_build_bots_are_broken"]) 29 def test_normal_stripped(self): 30 """ 31 Validate behavior with a stripped binary, no symbols or symbol locator. 32 """ 33 self.config_test(["a.out"]) 34 self.try_breakpoint(False) 35 36 @skipUnlessPlatform(["linux_freebsd_but_old_dwp_tools_on_build_bots_are_broken"]) 37 def test_normal_stripped_split_with_dwp(self): 38 """ 39 Validate behavior with symbols, but no symbol locator. 40 """ 41 self.config_test(["a.out", "a.out.debug", "a.out.dwp"]) 42 self.try_breakpoint(True) 43 44 @skipUnlessPlatform(["linux_freebsd_but_old_dwp_tools_on_build_bots_are_broken"]) 45 def test_normal_stripped_only_dwp(self): 46 """ 47 Validate behavior *with* dwp symbols only, but missing other symbols, 48 but no symbol locator. This shouldn't work: without the other symbols 49 DWO's appear mostly useless. 50 """ 51 self.config_test(["a.out", "a.out.dwp"]) 52 self.try_breakpoint(False) 53 54 @skipIfCurlSupportMissing 55 @skipUnlessPlatform(["linux_freebsd_but_old_dwp_tools_on_build_bots_are_broken"]) 56 def test_debuginfod_dwp_from_service(self): 57 """ 58 Test behavior with the unstripped binary, and DWP from the service. 59 """ 60 self.config_test(["a.out.debug"], "a.out.dwp") 61 self.try_breakpoint(True) 62 63 @skipIfCurlSupportMissing 64 @skipUnlessPlatform(["linux_freebsd_but_old_dwp_tools_on_build_bots_are_broken"]) 65 def test_debuginfod_both_symfiles_from_service(self): 66 """ 67 Test behavior with a stripped binary, with the unstripped binary and 68 dwp symbols from Debuginfod. 69 """ 70 self.config_test(["a.out"], "a.out.dwp", "a.out.unstripped") 71 self.try_breakpoint(True) 72 73 @skipIfCurlSupportMissing 74 @skipUnlessPlatform(["linux_freebsd_but_old_dwp_tools_on_build_bots_are_broken"]) 75 def test_debuginfod_both_okd_symfiles_from_service(self): 76 """ 77 Test behavior with both the only-keep-debug symbols and the dwp symbols 78 from Debuginfod. 79 """ 80 self.config_test(["a.out"], "a.out.dwp", "a.out.debug") 81 self.try_breakpoint(True) 82 83 def try_breakpoint(self, should_have_loc): 84 """ 85 This function creates a target from self.aout, sets a function-name 86 breakpoint, and checks to see if we have a file/line location, 87 as a way to validate that the symbols have been loaded. 88 should_have_loc specifies if we're testing that symbols have or 89 haven't been loaded. 90 """ 91 target = self.dbg.CreateTarget(self.aout) 92 self.assertTrue(target and target.IsValid(), "Target is valid") 93 94 bp = target.BreakpointCreateByName("func") 95 self.assertTrue(bp and bp.IsValid(), "Breakpoint is valid") 96 self.assertEqual(bp.GetNumLocations(), 1) 97 98 loc = bp.GetLocationAtIndex(0) 99 self.assertTrue(loc and loc.IsValid(), "Location is valid") 100 addr = loc.GetAddress() 101 self.assertTrue(addr and addr.IsValid(), "Loc address is valid") 102 line_entry = addr.GetLineEntry() 103 self.assertEqual( 104 should_have_loc, 105 line_entry != None and line_entry.IsValid(), 106 "Loc line entry is valid", 107 ) 108 if should_have_loc: 109 self.assertEqual(line_entry.GetLine(), 4) 110 self.assertEqual( 111 line_entry.GetFileSpec().GetFilename(), 112 self.main_source_file.GetFilename(), 113 ) 114 self.dbg.DeleteTarget(target) 115 shutil.rmtree(self.tmp_dir) 116 117 def config_test(self, local_files, debuginfo=None, executable=None): 118 """ 119 Set up a test with local_files[] copied to a different location 120 so that we control which files are, or are not, found in the file system. 121 Also, create a stand-alone file-system 'hosted' debuginfod server with the 122 provided debuginfo and executable files (if they exist) 123 124 Make the filesystem look like: 125 126 /tmp/<tmpdir>/test/[local_files] 127 128 /tmp/<tmpdir>/cache (for lldb to use as a temp cache) 129 130 /tmp/<tmpdir>/buildid/<uuid>/executable -> <executable> 131 /tmp/<tmpdir>/buildid/<uuid>/debuginfo -> <debuginfo> 132 Returns the /tmp/<tmpdir> path 133 """ 134 135 self.build() 136 137 uuid = self.getUUID("a.out") 138 if not uuid: 139 self.fail("Could not get UUID for a.out") 140 return 141 self.main_source_file = lldb.SBFileSpec("main.c") 142 self.tmp_dir = tempfile.mkdtemp() 143 self.test_dir = os.path.join(self.tmp_dir, "test") 144 os.makedirs(self.test_dir) 145 146 self.aout = "" 147 # Copy the files used by the test: 148 for f in local_files: 149 shutil.copy(self.getBuildArtifact(f), self.test_dir) 150 if self.aout == "": 151 self.aout = os.path.join(self.test_dir, f) 152 153 use_debuginfod = debuginfo != None or executable != None 154 155 # Populated the 'file://... mocked' Debuginfod server: 156 if use_debuginfod: 157 os.makedirs(os.path.join(self.tmp_dir, "cache")) 158 uuid_dir = os.path.join(self.tmp_dir, "buildid", uuid) 159 os.makedirs(uuid_dir) 160 if debuginfo: 161 shutil.copy( 162 self.getBuildArtifact(debuginfo), 163 os.path.join(uuid_dir, "debuginfo"), 164 ) 165 if executable: 166 shutil.copy( 167 self.getBuildArtifact(executable), 168 os.path.join(uuid_dir, "executable"), 169 ) 170 os.remove(self.getBuildArtifact("main.dwo")) 171 # Configure LLDB for the test: 172 self.runCmd( 173 "settings set symbols.enable-external-lookup %s" 174 % str(use_debuginfod).lower() 175 ) 176 self.runCmd("settings clear plugin.symbol-locator.debuginfod.server-urls") 177 if use_debuginfod: 178 self.runCmd( 179 "settings set plugin.symbol-locator.debuginfod.cache-path %s/cache" 180 % self.tmp_dir 181 ) 182 self.runCmd( 183 "settings insert-before plugin.symbol-locator.debuginfod.server-urls 0 file://%s" 184 % self.tmp_dir 185 ) 186 187 def getUUID(self, filename): 188 try: 189 spec = lldb.SBModuleSpec() 190 spec.SetFileSpec(lldb.SBFileSpec(self.getBuildArtifact(filename))) 191 module = lldb.SBModule(spec) 192 uuid = module.GetUUIDString().replace("-", "").lower() 193 # Don't want lldb's fake 32 bit CRC's for this one 194 return uuid if len(uuid) > 8 else None 195 except: 196 return None 197