xref: /llvm-project/lldb/test/API/functionalities/inferior-crashing/TestInferiorCrashingStep.py (revision 2238dcc39358353cac21df75c3c3286ab20b8f53)
1"""Test that lldb steps correctly after the inferior has crashed."""
2
3
4import lldb
5from lldbsuite.test import lldbutil
6from lldbsuite.test import lldbplatformutil
7from lldbsuite.test.decorators import *
8from lldbsuite.test.lldbtest import *
9
10
11class CrashingInferiorStepTestCase(TestBase):
12    @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24778")
13    @expectedFailureNetBSD
14    def test_inferior_crashing(self):
15        """Test that lldb reliably catches the inferior crashing (command)."""
16        self.build()
17        self.inferior_crashing()
18
19    def test_inferior_crashing_register(self):
20        """Test that lldb reliably reads registers from the inferior after crashing (command)."""
21        self.build()
22        self.inferior_crashing_registers()
23
24    @add_test_categories(["pyapi"])
25    def test_inferior_crashing_python(self):
26        """Test that lldb reliably catches the inferior crashing (Python API)."""
27        self.build()
28        self.inferior_crashing_python()
29
30    def test_inferior_crashing_expr(self):
31        """Test that the lldb expression interpreter can read from the inferior after crashing (command)."""
32        self.build()
33        self.inferior_crashing_expr()
34
35    def test_inferior_crashing_step(self):
36        """Test that stepping after a crash behaves correctly."""
37        self.build()
38        self.inferior_crashing_step()
39
40    @skipIfTargetAndroid()  # debuggerd interferes with this test on Android
41    @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24778")
42    def test_inferior_crashing_step_after_break(self):
43        """Test that lldb functions correctly after stepping through a crash."""
44        self.build()
45        self.inferior_crashing_step_after_break()
46
47    # Inferior exits after stepping after a segfault. This is working as
48    # intended IMHO.
49    @skipIf(oslist=["freebsd", "linux", "netbsd"])
50    def test_inferior_crashing_expr_step_and_expr(self):
51        """Test that lldb expressions work before and after stepping after a crash."""
52        self.build()
53        self.inferior_crashing_expr_step_expr()
54
55    def set_breakpoint(self, line):
56        lldbutil.run_break_set_by_file_and_line(
57            self, "main.c", line, num_expected_locations=1, loc_exact=True
58        )
59
60    def check_stop_reason(self):
61        # We should have one crashing thread
62        self.assertEqual(
63            len(
64                lldbutil.get_crashed_threads(
65                    self, self.dbg.GetSelectedTarget().GetProcess()
66                )
67            ),
68            1,
69            STOPPED_DUE_TO_EXC_BAD_ACCESS,
70        )
71
72    def get_api_stop_reason(self):
73        return lldb.eStopReasonException
74
75    def setUp(self):
76        # Call super's setUp().
77        TestBase.setUp(self)
78        # Find the line number of the crash.
79        self.line = line_number("main.c", "// Crash here.")
80
81    def inferior_crashing(self):
82        """Inferior crashes upon launching; lldb should catch the event and stop."""
83        exe = self.getBuildArtifact("a.out")
84        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
85
86        self.runCmd("run", RUN_SUCCEEDED)
87        # The exact stop reason depends on the platform
88        if self.platformIsDarwin():
89            stop_reason = "stop reason = EXC_BAD_ACCESS"
90        elif self.getPlatform() == "linux" or self.getPlatform() == "freebsd":
91            stop_reason = "stop reason = signal SIGSEGV"
92        else:
93            stop_reason = "stop reason = invalid address"
94        self.expect(
95            "thread list",
96            STOPPED_DUE_TO_EXC_BAD_ACCESS,
97            substrs=["stopped", stop_reason],
98        )
99
100        # And it should report the correct line number.
101        self.expect(
102            "thread backtrace all", substrs=[stop_reason, "main.c:%d" % self.line]
103        )
104
105    def inferior_crashing_python(self):
106        """Inferior crashes upon launching; lldb should catch the event and stop."""
107        exe = self.getBuildArtifact("a.out")
108
109        target = self.dbg.CreateTarget(exe)
110        self.assertTrue(target, VALID_TARGET)
111
112        # Now launch the process, and do not stop at entry point.
113        # Both argv and envp are null.
114        process = target.LaunchSimple(None, None, self.get_process_working_directory())
115
116        if process.GetState() != lldb.eStateStopped:
117            self.fail(
118                "Process should be in the 'stopped' state, "
119                "instead the actual state is: '%s'"
120                % lldbutil.state_type_to_str(process.GetState())
121            )
122
123        threads = lldbutil.get_crashed_threads(self, process)
124        self.assertEqual(
125            len(threads), 1, "Failed to stop the thread upon bad access exception"
126        )
127
128        if self.TraceOn():
129            lldbutil.print_stacktrace(threads[0])
130
131    def inferior_crashing_registers(self):
132        """Test that lldb can read registers after crashing."""
133        exe = self.getBuildArtifact("a.out")
134        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
135
136        self.runCmd("run", RUN_SUCCEEDED)
137        self.check_stop_reason()
138
139        # lldb should be able to read from registers from the inferior after
140        # crashing.
141        lldbplatformutil.check_first_register_readable(self)
142
143    def inferior_crashing_expr(self):
144        """Test that the lldb expression interpreter can read symbols after crashing."""
145        exe = self.getBuildArtifact("a.out")
146        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
147
148        self.runCmd("run", RUN_SUCCEEDED)
149        self.check_stop_reason()
150
151        # The lldb expression interpreter should be able to read from addresses
152        # of the inferior after a crash.
153        self.expect("expression argc", startstr="(int) $0 = 1")
154
155        self.expect("expression hello_world", substrs=["Hello"])
156
157    def inferior_crashing_step(self):
158        """Test that lldb functions correctly after stepping through a crash."""
159        exe = self.getBuildArtifact("a.out")
160        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
161
162        self.set_breakpoint(self.line)
163        self.runCmd("run", RUN_SUCCEEDED)
164
165        self.expect(
166            "thread list",
167            STOPPED_DUE_TO_BREAKPOINT,
168            substrs=["main.c:%d" % self.line, "stop reason = breakpoint"],
169        )
170
171        self.runCmd("next")
172        self.check_stop_reason()
173
174        # The lldb expression interpreter should be able to read from addresses
175        # of the inferior after a crash.
176        self.expect("expression argv[0]", substrs=["a.out"])
177        self.expect("expression null_ptr", substrs=["= 0x0"])
178
179        # lldb should be able to read from registers from the inferior after
180        # crashing.
181        lldbplatformutil.check_first_register_readable(self)
182
183        # And it should report the correct line number.
184        self.expect("thread backtrace all", substrs=["main.c:%d" % self.line])
185
186    @expectedFailureNetBSD
187    def inferior_crashing_step_after_break(self):
188        """Test that lldb behaves correctly when stepping after a crash."""
189        exe = self.getBuildArtifact("a.out")
190        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
191
192        self.runCmd("run", RUN_SUCCEEDED)
193        self.check_stop_reason()
194
195        expected_state = "exited"  # Provide the exit code.
196        if self.platformIsDarwin():
197            # TODO: Determine why 'next' and 'continue' have no effect after a
198            # crash.
199            expected_state = "stopped"
200
201        self.expect("next", substrs=["Process", expected_state])
202
203        if expected_state == "exited":
204            self.expect("thread list", error=True, substrs=["Process must be launched"])
205        else:
206            self.check_stop_reason()
207
208    def inferior_crashing_expr_step_expr(self):
209        """Test that lldb expressions work before and after stepping after a crash."""
210        exe = self.getBuildArtifact("a.out")
211        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
212
213        self.runCmd("run", RUN_SUCCEEDED)
214        self.check_stop_reason()
215
216        # The lldb expression interpreter should be able to read from addresses
217        # of the inferior after a crash.
218        self.expect("expression argv[0]", substrs=["a.out"])
219
220        self.runCmd("next")
221        self.check_stop_reason()
222
223        # The lldb expression interpreter should be able to read from addresses
224        # of the inferior after a crash.
225        self.expect("expression argv[0]", substrs=["a.out"])
226