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