summaryrefslogtreecommitdiffstats
path: root/lldb/test/API/debuginfod/Normal/TestDebuginfod.py
blob: f1be1e705ee0cb2bfc0434b73fee20ed94a24454 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
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 one is for simple / no split-dwarf scenarios.

For no-split-dwarf scenarios, there are 2 variations:
1 - A stripped binary with it's corresponding unstripped binary:
2 - A stripped binary with a corresponding --only-keep-debug symbols file
"""


# It looks like Linux-AArch64 doesn't support build-id's on the LLDB builtbots
class DebugInfodTests(TestBase):
    # No need to try every flavor of debug inf.
    NO_DEBUG_INFO_TESTCASE = True

    @skipIf(oslist=no_match(["linux"]), archs=no_match(["i386", "x86_64"]))
    def test_normal_no_symbols(self):
        """
        Validate behavior with no symbols or symbol locator.
        ('baseline negative' behavior)
        """
        test_root = self.config_test(["a.out"])
        self.try_breakpoint(False)

    @skipIf(oslist=no_match(["linux"]), archs=no_match(["i386", "x86_64"]))
    def test_normal_default(self):
        """
        Validate behavior with symbols, but no symbol locator.
        ('baseline positive' behavior)
        """
        test_root = self.config_test(["a.out", "a.out.debug"])
        self.try_breakpoint(True)

    @skipIf(oslist=no_match(["linux"]), archs=no_match(["i386", "x86_64"]))
    def test_debuginfod_symbols(self):
        """
        Test behavior with the full binary available from Debuginfod as
        'debuginfo' from the plug-in.
        """
        test_root = self.config_test(["a.out"], "a.out.unstripped")
        self.try_breakpoint(True)

    @skipIf(oslist=no_match(["linux"]), archs=no_match(["i386", "x86_64"]))
    def test_debuginfod_executable(self):
        """
        Test behavior with the full binary available from Debuginfod as
        'executable' from the plug-in.
        """
        test_root = self.config_test(["a.out"], None, "a.out.unstripped")
        self.try_breakpoint(True)

    @skipIf(oslist=no_match(["linux"]), archs=no_match(["i386", "x86_64"]))
    def test_debuginfod_okd_symbols(self):
        """
        Test behavior with the 'only-keep-debug' symbols available from Debuginfod.
        """
        test_root = self.config_test(["a.out"], "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()
        test_dir = os.path.join(self.tmp_dir, "test")
        os.makedirs(test_dir)

        self.aout = ""
        # Copy the files used by the test:
        for f in local_files:
            shutil.copy(self.getBuildArtifact(f), test_dir)
            # The first item is the binary to be used for the test
            if self.aout == "":
                self.aout = os.path.join(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"),
                )

        # 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:
            target = self.dbg.CreateTarget(self.getBuildArtifact(filename))
            module = target.GetModuleAtIndex(0)
            uuid = module.GetUUIDString().replace("-", "").lower()
            self.dbg.DeleteTarget(target)
            return uuid if len(uuid) == 40 else None
        except:
            return None