xref: /llvm-project/lldb/test/API/functionalities/inferior-assert/TestInferiorAssert.py (revision 2238dcc39358353cac21df75c3c3286ab20b8f53)
1"""Test that lldb functions correctly after the inferior has asserted."""
2
3import lldb
4from lldbsuite.test import lldbutil
5from lldbsuite.test import lldbplatformutil
6from lldbsuite.test.decorators import *
7from lldbsuite.test.lldbtest import *
8
9
10class AssertingInferiorTestCase(TestBase):
11    @expectedFailureAll(
12        oslist=["windows"],
13        bugnumber="llvm.org/pr21793: need to implement support for detecting assertion / abort on Windows",
14    )
15    @expectedFailureAll(oslist=["linux"], archs=["arm"], bugnumber="llvm.org/pr25338")
16    @expectedFailureAll(bugnumber="llvm.org/pr26592", triple="^mips")
17    def test_inferior_asserting(self):
18        """Test that lldb reliably catches the inferior asserting (command)."""
19        self.build()
20        self.inferior_asserting()
21
22    @expectedFailureAll(
23        oslist=["windows"],
24        bugnumber="llvm.org/pr21793: need to implement support for detecting assertion / abort on Windows",
25    )
26    @expectedFailureAndroid(api_levels=list(range(16 + 1)))  # b.android.com/179836
27    def test_inferior_asserting_register(self):
28        """Test that lldb reliably reads registers from the inferior after asserting (command)."""
29        self.build()
30        self.inferior_asserting_registers()
31
32    @expectedFailureAll(
33        oslist=["windows"],
34        bugnumber="llvm.org/pr21793: need to implement support for detecting assertion / abort on Windows",
35    )
36    @expectedFailureAll(
37        oslist=["linux"],
38        archs=["arm"],
39        triple=no_match(".*-android"),
40        bugnumber="llvm.org/pr25338",
41    )
42    @expectedFailureAll(bugnumber="llvm.org/pr26592", triple="^mips")
43    def test_inferior_asserting_disassemble(self):
44        """Test that lldb reliably disassembles frames after asserting (command)."""
45        self.build()
46        self.inferior_asserting_disassemble()
47
48    @add_test_categories(["pyapi"])
49    @expectedFailureAll(
50        oslist=["windows"],
51        bugnumber="llvm.org/pr21793: need to implement support for detecting assertion / abort on Windows",
52    )
53    def test_inferior_asserting_python(self):
54        """Test that lldb reliably catches the inferior asserting (Python API)."""
55        self.build()
56        self.inferior_asserting_python()
57
58    @expectedFailureAll(
59        oslist=["windows"],
60        bugnumber="llvm.org/pr21793: need to implement support for detecting assertion / abort on Windows",
61    )
62    @expectedFailureAll(
63        oslist=["linux"],
64        archs=["arm"],
65        triple=no_match(".*-android"),
66        bugnumber="llvm.org/pr25338",
67    )
68    @expectedFailureAll(bugnumber="llvm.org/pr26592", triple="^mips")
69    def test_inferior_asserting_expr(self):
70        """Test that the lldb expression interpreter can read from the inferior after asserting (command)."""
71        self.build()
72        self.inferior_asserting_expr()
73
74    @expectedFailureAll(
75        oslist=["windows"],
76        bugnumber="llvm.org/pr21793: need to implement support for detecting assertion / abort on Windows",
77    )
78    @expectedFailureAll(
79        oslist=["linux"],
80        archs=["arm"],
81        triple=no_match(".*-android"),
82        bugnumber="llvm.org/pr25338",
83    )
84    @expectedFailureAll(bugnumber="llvm.org/pr26592", triple="^mips")
85    def test_inferior_asserting_step(self):
86        """Test that lldb functions correctly after stepping through a call to assert()."""
87        self.build()
88        self.inferior_asserting_step()
89
90    def set_breakpoint(self, line):
91        lldbutil.run_break_set_by_file_and_line(
92            self, "main.c", line, num_expected_locations=1, loc_exact=True
93        )
94
95    def check_stop_reason(self):
96        matched = lldbplatformutil.match_android_device(
97            self.getArchitecture(), valid_api_levels=list(range(1, 16 + 1))
98        )
99        if matched:
100            # On android until API-16 the abort() call ended in a sigsegv
101            # instead of in a sigabrt
102            stop_reason = "signal SIGSEGV"
103        else:
104            stop_reason = "signal SIGABRT"
105
106        target = self.dbg.GetTargetAtIndex(0)
107        process = target.GetProcess()
108        thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonSignal)
109
110        pattern = "stop reason = (" + stop_reason + "|hit program assert)"
111
112        # The stop reason of the thread should be an abort signal or exception.
113        self.expect(
114            "thread list",
115            STOPPED_DUE_TO_ASSERT,
116            patterns=[pattern],
117            substrs=["stopped"],
118        )
119
120        return pattern
121
122    def setUp(self):
123        # Call super's setUp().
124        TestBase.setUp(self)
125        # Find the line number of the call to assert.
126        self.line = line_number("main.c", "// Assert here.")
127
128    def inferior_asserting(self):
129        """Inferior asserts upon launching; lldb should catch the event and stop."""
130        exe = self.getBuildArtifact("a.out")
131        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
132
133        self.runCmd("run", RUN_SUCCEEDED)
134        stop_reason = self.check_stop_reason()
135
136        # And it should report a backtrace that includes the assert site.
137        self.expect(
138            "thread backtrace all",
139            patterns=[stop_reason],
140            substrs=["main", "argc", "argv"],
141        )
142
143        # And it should report the correct line number.
144        self.expect(
145            "thread backtrace all",
146            patterns=[stop_reason],
147            substrs=["main.c:%d" % self.line],
148        )
149
150    def inferior_asserting_python(self):
151        """Inferior asserts upon launching; lldb should catch the event and stop."""
152        exe = self.getBuildArtifact("a.out")
153
154        target = self.dbg.CreateTarget(exe)
155        self.assertTrue(target, VALID_TARGET)
156
157        # Now launch the process, and do not stop at entry point.
158        # Both argv and envp are null.
159        process = target.LaunchSimple(None, None, self.get_process_working_directory())
160
161        if process.GetState() != lldb.eStateStopped:
162            self.fail(
163                "Process should be in the 'stopped' state, "
164                "instead the actual state is: '%s'"
165                % lldbutil.state_type_to_str(process.GetState())
166            )
167
168        thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonSignal)
169        if not thread:
170            self.fail("Fail to stop the thread upon assert")
171
172        if self.TraceOn():
173            lldbutil.print_stacktrace(thread)
174
175    def inferior_asserting_registers(self):
176        """Test that lldb can read registers after asserting."""
177        exe = self.getBuildArtifact("a.out")
178        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
179
180        self.runCmd("run", RUN_SUCCEEDED)
181        self.check_stop_reason()
182
183        # change current frame to frame 0, since recognizer might have selected
184        # different frame.
185        self.runCmd("frame select 0", RUN_SUCCEEDED)
186
187        # lldb should be able to read from registers from the inferior after
188        # asserting.
189        lldbplatformutil.check_first_register_readable(self)
190
191    def inferior_asserting_disassemble(self):
192        """Test that lldb can disassemble frames after asserting."""
193        exe = self.getBuildArtifact("a.out")
194
195        # Create a target by the debugger.
196        target = self.dbg.CreateTarget(exe)
197        self.assertTrue(target, VALID_TARGET)
198
199        # Launch the process, and do not stop at the entry point.
200        target.LaunchSimple(None, None, self.get_process_working_directory())
201        self.check_stop_reason()
202
203        process = target.GetProcess()
204        self.assertTrue(process.IsValid(), "current process is valid")
205
206        thread = process.GetThreadAtIndex(0)
207        self.assertTrue(thread.IsValid(), "current thread is valid")
208
209        lastframeID = thread.GetFrameAtIndex(thread.GetNumFrames() - 1).GetFrameID()
210
211        isi386Arch = False
212        if "i386" in self.getArchitecture():
213            isi386Arch = True
214
215        # lldb should be able to disassemble frames from the inferior after
216        # asserting.
217        for frame in thread:
218            self.assertTrue(frame.IsValid(), "current frame is valid")
219
220            self.runCmd("frame select " + str(frame.GetFrameID()), RUN_SUCCEEDED)
221
222            # Don't expect the function name to be in the disassembly as the assert
223            # function might be a no-return function where the PC is past the end
224            # of the function and in the next function. We also can't back the PC up
225            # because we don't know how much to back it up by on targets with opcodes
226            # that have differing sizes
227            pc_backup_offset = 1
228            if frame.GetFrameID() == 0:
229                pc_backup_offset = 0
230            if isi386Arch:
231                if lastframeID == frame.GetFrameID():
232                    pc_backup_offset = 0
233            self.expect(
234                "disassemble -a %s" % (frame.GetPC() - pc_backup_offset),
235                substrs=["<+0>: "],
236            )
237
238    def check_expr_in_main(self, thread):
239        depth = thread.GetNumFrames()
240        for i in range(depth):
241            frame = thread.GetFrameAtIndex(i)
242            self.assertTrue(frame.IsValid(), "current frame is valid")
243            if self.TraceOn():
244                print("Checking if function %s is main" % frame.GetFunctionName())
245
246            if "main" == frame.GetFunctionName():
247                frame_id = frame.GetFrameID()
248                self.runCmd("frame select " + str(frame_id), RUN_SUCCEEDED)
249                self.expect("expression argc", substrs=["(int)", " = 1"])
250                self.expect("expression hello_world", substrs=["Hello"])
251                self.expect("expression argv[0]", substrs=["a.out"])
252                self.expect("expression null_ptr", substrs=["= 0x0"])
253                return True
254        return False
255
256    def inferior_asserting_expr(self):
257        """Test that the lldb expression interpreter can read symbols after asserting."""
258        exe = self.getBuildArtifact("a.out")
259
260        # Create a target by the debugger.
261        target = self.dbg.CreateTarget(exe)
262        self.assertTrue(target, VALID_TARGET)
263
264        # Launch the process, and do not stop at the entry point.
265        target.LaunchSimple(None, None, self.get_process_working_directory())
266        self.check_stop_reason()
267
268        process = target.GetProcess()
269        self.assertTrue(process.IsValid(), "current process is valid")
270
271        thread = process.GetThreadAtIndex(0)
272        self.assertTrue(thread.IsValid(), "current thread is valid")
273
274        # The lldb expression interpreter should be able to read from addresses
275        # of the inferior after a call to assert().
276        self.assertTrue(
277            self.check_expr_in_main(thread), "cannot find 'main' in the backtrace"
278        )
279
280    def inferior_asserting_step(self):
281        """Test that lldb functions correctly after stepping through a call to assert()."""
282        exe = self.getBuildArtifact("a.out")
283
284        # Create a target by the debugger.
285        target = self.dbg.CreateTarget(exe)
286        self.assertTrue(target, VALID_TARGET)
287
288        # Launch the process, and do not stop at the entry point.
289        self.set_breakpoint(self.line)
290        target.LaunchSimple(None, None, self.get_process_working_directory())
291
292        self.expect(
293            "thread list",
294            STOPPED_DUE_TO_BREAKPOINT,
295            substrs=["main.c:%d" % self.line, "stop reason = breakpoint"],
296        )
297
298        self.runCmd("next")
299        stop_reason = self.check_stop_reason()
300
301        # lldb should be able to read from registers from the inferior after
302        # asserting.
303        if "x86_64" in self.getArchitecture():
304            self.expect("register read rbp", substrs=["rbp = 0x"])
305        if "i386" in self.getArchitecture():
306            self.expect("register read ebp", substrs=["ebp = 0x"])
307
308        process = target.GetProcess()
309        self.assertTrue(process.IsValid(), "current process is valid")
310
311        thread = process.GetThreadAtIndex(0)
312        self.assertTrue(thread.IsValid(), "current thread is valid")
313
314        # The lldb expression interpreter should be able to read from addresses
315        # of the inferior after a call to assert().
316        self.assertTrue(
317            self.check_expr_in_main(thread), "cannot find 'main' in the backtrace"
318        )
319
320        # And it should report the correct line number.
321        self.expect(
322            "thread backtrace all",
323            patterns=[stop_reason],
324            substrs=["main.c:%d" % self.line],
325        )
326