1"""
2Test basics of Minidump debugging.
3"""
4
5from six import iteritems
6
7
8import lldb
9import os
10from lldbsuite.test.decorators import *
11from lldbsuite.test.lldbtest import *
12from lldbsuite.test import lldbutil
13
14
15class MiniDumpUUIDTestCase(TestBase):
16
17    mydir = TestBase.compute_mydir(__file__)
18
19    NO_DEBUG_INFO_TESTCASE = True
20
21    def verify_module(self, module, verify_path, verify_uuid):
22        # Compare the filename and the directory separately. We are avoiding
23        # SBFileSpec.fullpath because it causes a slash/backslash confusion
24        # on Windows.
25        self.assertEqual(
26            os.path.basename(verify_path), module.GetFileSpec().basename)
27        self.assertEqual(
28            os.path.dirname(verify_path), module.GetFileSpec().dirname or "")
29        self.assertEqual(verify_uuid, module.GetUUIDString())
30
31    def get_minidump_modules(self, yaml_file):
32        minidump_path = self.getBuildArtifact(os.path.basename(yaml_file) + ".dmp")
33        self.yaml2obj(yaml_file, minidump_path)
34        self.target = self.dbg.CreateTarget(None)
35        self.process = self.target.LoadCore(minidump_path)
36        return self.target.modules
37
38    def test_zero_uuid_modules(self):
39        """
40            Test multiple modules having a MINIDUMP_MODULE.CvRecord that is valid,
41            but contains a PDB70 value whose age is zero and whose UUID values are
42            all zero. Prior to a fix all such modules would be duplicated to the
43            first one since the UUIDs claimed to be valid and all zeroes. Now we
44            ensure that the UUID is not valid for each module and that we have
45            each of the modules in the target after loading the core
46        """
47        modules = self.get_minidump_modules("linux-arm-zero-uuids.yaml")
48        self.assertEqual(2, len(modules))
49        self.verify_module(modules[0], "/file/does/not/exist/a", None)
50        self.verify_module(modules[1], "/file/does/not/exist/b", None)
51
52    def test_uuid_modules_no_age(self):
53        """
54            Test multiple modules having a MINIDUMP_MODULE.CvRecord that is valid,
55            and contains a PDB70 value whose age is zero and whose UUID values are
56            valid. Ensure we decode the UUID and don't include the age field in the UUID.
57        """
58        modules = self.get_minidump_modules("linux-arm-uuids-no-age.yaml")
59        modules = self.target.modules
60        self.assertEqual(2, len(modules))
61        self.verify_module(modules[0], "/tmp/a", "01020304-0506-0708-090A-0B0C0D0E0F10")
62        self.verify_module(modules[1], "/tmp/b", "0A141E28-323C-4650-5A64-6E78828C96A0")
63
64    def test_uuid_modules_no_age_apple(self):
65        """
66            Test multiple modules having a MINIDUMP_MODULE.CvRecord that is valid,
67            and contains a PDB70 value whose age is zero and whose UUID values are
68            valid. Ensure we decode the UUID and don't include the age field in the UUID.
69            Also ensure that the first uint32_t is byte swapped, along with the next
70            two uint16_t values. Breakpad incorrectly byte swaps these values when it
71            saves Darwin minidump files.
72        """
73        modules = self.get_minidump_modules("macos-arm-uuids-no-age.yaml")
74        modules = self.target.modules
75        self.assertEqual(2, len(modules))
76        self.verify_module(modules[0], "/tmp/a", "04030201-0605-0807-090A-0B0C0D0E0F10")
77        self.verify_module(modules[1], "/tmp/b", "281E140A-3C32-5046-5A64-6E78828C96A0")
78
79    def test_uuid_modules_with_age(self):
80        """
81            Test multiple modules having a MINIDUMP_MODULE.CvRecord that is valid,
82            and contains a PDB70 value whose age is valid and whose UUID values are
83            valid. Ensure we decode the UUID and include the age field in the UUID.
84        """
85        modules = self.get_minidump_modules("linux-arm-uuids-with-age.yaml")
86        self.assertEqual(2, len(modules))
87        self.verify_module(modules[0], "/tmp/a", "01020304-0506-0708-090A-0B0C0D0E0F10-10101010")
88        self.verify_module(modules[1], "/tmp/b", "0A141E28-323C-4650-5A64-6E78828C96A0-20202020")
89
90    def test_uuid_modules_elf_build_id_16(self):
91        """
92            Test multiple modules having a MINIDUMP_MODULE.CvRecord that is valid,
93            and contains a ELF build ID whose value is valid and is 16 bytes long.
94        """
95        modules = self.get_minidump_modules("linux-arm-uuids-elf-build-id-16.yaml")
96        self.assertEqual(2, len(modules))
97        self.verify_module(modules[0], "/tmp/a", "01020304-0506-0708-090A-0B0C0D0E0F10")
98        self.verify_module(modules[1], "/tmp/b", "0A141E28-323C-4650-5A64-6E78828C96A0")
99
100    def test_uuid_modules_elf_build_id_20(self):
101        """
102            Test multiple modules having a MINIDUMP_MODULE.CvRecord that is valid,
103            and contains a ELF build ID whose value is valid and is 20 bytes long.
104        """
105        modules = self.get_minidump_modules("linux-arm-uuids-elf-build-id-20.yaml")
106        self.assertEqual(2, len(modules))
107        self.verify_module(modules[0], "/tmp/a", "01020304-0506-0708-090A-0B0C0D0E0F10-11121314")
108        self.verify_module(modules[1], "/tmp/b", "0A141E28-323C-4650-5A64-6E78828C96A0-AAB4BEC8")
109
110    def test_uuid_modules_elf_build_id_zero(self):
111        """
112            Test multiple modules having a MINIDUMP_MODULE.CvRecord that is valid,
113            and contains a ELF build ID whose value is all zero.
114        """
115        modules = self.get_minidump_modules("linux-arm-uuids-elf-build-id-zero.yaml")
116        self.assertEqual(2, len(modules))
117        self.verify_module(modules[0], "/not/exist/a", None)
118        self.verify_module(modules[1], "/not/exist/b", None)
119
120    def test_uuid_modules_elf_build_id_same(self):
121        """
122            Test multiple modules having a MINIDUMP_MODULE.CvRecord that is
123            valid, and contains a ELF build ID whose value is the same. There
124            is an assert in the PlaceholderObjectFile that was firing when we
125            encountered this which was crashing the process that was checking
126            if PlaceholderObjectFile.m_base was the same as the address this
127            fake module was being loaded at. We need to ensure we don't crash
128            in such cases and that we add both modules even though they have
129            the same UUID.
130        """
131        modules = self.get_minidump_modules("linux-arm-same-uuids.yaml")
132        self.assertEqual(2, len(modules))
133        self.verify_module(modules[0], "/file/does/not/exist/a",
134                           '11223344-1122-3344-1122-334411223344-11223344')
135        self.verify_module(modules[1], "/file/does/not/exist/b",
136                           '11223344-1122-3344-1122-334411223344-11223344')
137
138    def test_partial_uuid_match(self):
139        """
140            Breakpad has been known to create minidump files using CvRecord in each
141            module whose signature is set to PDB70 where the UUID only contains the
142            first 16 bytes of a 20 byte ELF build ID. Code was added to
143            ProcessMinidump.cpp to deal with this and allows partial UUID matching.
144
145            This test verifies that if we have a minidump with a 16 byte UUID, that
146            we are able to associate a symbol file with a 20 byte UUID only if the
147            first 16 bytes match. In this case we will see the path from the file
148            we found in the test directory and the 20 byte UUID from the actual
149            file, not the 16 byte shortened UUID from the minidump.
150        """
151        so_path = self.getBuildArtifact("libuuidmatch.so")
152        self.yaml2obj("libuuidmatch.yaml", so_path)
153        cmd = 'settings set target.exec-search-paths "%s"' % (os.path.dirname(so_path))
154        self.dbg.HandleCommand(cmd)
155        modules = self.get_minidump_modules("linux-arm-partial-uuids-match.yaml")
156        self.assertEqual(1, len(modules))
157        self.verify_module(modules[0], so_path,
158                           "7295E17C-6668-9E05-CBB5-DEE5003865D5-5267C116")
159
160    def test_partial_uuid_mismatch(self):
161        """
162            Breakpad has been known to create minidump files using CvRecord in each
163            module whose signature is set to PDB70 where the UUID only contains the
164            first 16 bytes of a 20 byte ELF build ID. Code was added to
165            ProcessMinidump.cpp to deal with this and allows partial UUID matching.
166
167            This test verifies that if we have a minidump with a 16 byte UUID, that
168            we are not able to associate a symbol file with a 20 byte UUID only if
169            any of the first 16 bytes do not match. In this case we will see the UUID
170            from the minidump file and the path from the minidump file.
171        """
172        so_path = self.getBuildArtifact("libuuidmismatch.so")
173        self.yaml2obj("libuuidmismatch.yaml", so_path)
174        cmd = 'settings set target.exec-search-paths "%s"' % (os.path.dirname(so_path))
175        self.dbg.HandleCommand(cmd)
176        modules = self.get_minidump_modules("linux-arm-partial-uuids-mismatch.yaml")
177        self.assertEqual(1, len(modules))
178        self.verify_module(modules[0],
179                           "/invalid/path/on/current/system/libuuidmismatch.so",
180                           "7295E17C-6668-9E05-CBB5-DEE5003865D5")
181
182    def test_relative_module_name(self):
183        old_cwd = os.getcwd()
184        self.addTearDownHook(lambda: os.chdir(old_cwd))
185        os.chdir(self.getBuildDir())
186        name = "file-with-a-name-unlikely-to-exist-in-the-current-directory.so"
187        open(name, "a").close()
188        modules = self.get_minidump_modules(
189                self.getSourcePath("relative_module_name.yaml"))
190        self.assertEqual(1, len(modules))
191        self.verify_module(modules[0], name, None)
192
193    def test_add_module_build_id_16(self):
194        """
195            Test that adding module with 16 byte UUID returns the existing
196            module or fails.
197        """
198        modules = self.get_minidump_modules("linux-arm-uuids-elf-build-id-16.yaml")
199        self.assertEqual(2, len(modules))
200
201        # Add the existing modules.
202        self.assertEqual(modules[0], self.target.AddModule(
203            "/some/local/a", "", "01020304-0506-0708-090A-0B0C0D0E0F10"))
204        self.assertEqual(modules[1], self.target.AddModule(
205            "/some/local/b", "", "0A141E28-323C-4650-5A64-6E78828C96A0"))
206
207        # Adding modules with non-existing UUID should fail.
208        self.assertFalse(
209            self.target.AddModule(
210                "a", "", "12345678-1234-1234-1234-123456789ABC").IsValid())
211        self.assertFalse(
212            self.target.AddModule(
213                "a", "", "01020304-0506-0708-090A-0B0C0D0E0F10-12345678").IsValid())
214
215    def test_add_module_build_id_20(self):
216        """
217            Test that adding module with 20 byte UUID returns the existing
218            module or fails.
219        """
220        modules = self.get_minidump_modules("linux-arm-uuids-elf-build-id-20.yaml")
221
222        # Add the existing modules.
223        self.assertEqual(modules[0], self.target.AddModule(
224            "/some/local/a", "", "01020304-0506-0708-090A-0B0C0D0E0F10-11121314"))
225        self.assertEqual(modules[1], self.target.AddModule(
226            "/some/local/b", "", "0A141E28-323C-4650-5A64-6E78828C96A0-AAB4BEC8"))
227
228        # Adding modules with non-existing UUID should fail.
229        self.assertFalse(
230            self.target.AddModule(
231                "a", "", "01020304-0506-0708-090A-0B0C0D0E0F10").IsValid())
232        self.assertFalse(
233            self.target.AddModule(
234                "a", "", "01020304-0506-0708-090A-0B0C0D0E0F10-12345678").IsValid())
235
236    def test_add_module_build_id_4(self):
237        """
238            Test that adding module with 4 byte UUID returns the existing
239            module or fails.
240        """
241        modules = self.get_minidump_modules("linux-arm-uuids-elf-build-id-4.yaml")
242
243        # Add the existing modules.
244        self.assertEqual(modules[0], self.target.AddModule(
245            "/some/local/a.so", "", "01020304"))
246        self.assertEqual(modules[1], self.target.AddModule(
247            "/some/local/b.so", "", "0A141E28"))
248
249        # Adding modules with non-existing UUID should fail.
250        self.assertFalse(
251            self.target.AddModule(
252                "a", "", "01020304-0506-0708-090A-0B0C0D0E0F10").IsValid())
253        self.assertFalse(self.target.AddModule("a", "", "01020305").IsValid())
254
255    @skipIfReproducer # Modules are not orphaned and it finds the module with the same UUID from test_partial_uuid_match.
256    def test_remove_placeholder_add_real_module(self):
257        """
258            Test that removing a placeholder module and adding back the real
259            module succeeds.
260        """
261        so_path = self.getBuildArtifact("libuuidmatch.so")
262        self.yaml2obj("libuuidmatch.yaml", so_path)
263        modules = self.get_minidump_modules("linux-arm-uuids-match.yaml")
264
265        uuid = "7295E17C-6668-9E05-CBB5-DEE5003865D5-5267C116";
266        self.assertEqual(1, len(modules))
267        self.verify_module(modules[0], "/target/path/libuuidmatch.so",uuid)
268
269        self.target.RemoveModule(modules[0])
270        new_module = self.target.AddModule(so_path, "", uuid)
271
272        self.verify_module(new_module, so_path, uuid)
273        self.assertEqual(new_module, self.target.modules[0])
274        self.assertEqual(1, len(self.target.modules))
275