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