xref: /llvm-project/lldb/test/API/debuginfod/Normal/TestDebuginfod.py (revision e77ac42bccb8c26bbf4b74d8e92eb09e7fa1b218)
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