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