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 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
|
"""Test that lldb steps correctly after the inferior has crashed."""
import lldb
from lldbsuite.test import lldbutil
from lldbsuite.test import lldbplatformutil
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
class CrashingInferiorStepTestCase(TestBase):
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24778")
@expectedFailureNetBSD
def test_inferior_crashing(self):
"""Test that lldb reliably catches the inferior crashing (command)."""
self.build()
self.inferior_crashing()
def test_inferior_crashing_register(self):
"""Test that lldb reliably reads registers from the inferior after crashing (command)."""
self.build()
self.inferior_crashing_registers()
@add_test_categories(["pyapi"])
def test_inferior_crashing_python(self):
"""Test that lldb reliably catches the inferior crashing (Python API)."""
self.build()
self.inferior_crashing_python()
def test_inferior_crashing_expr(self):
"""Test that the lldb expression interpreter can read from the inferior after crashing (command)."""
self.build()
self.inferior_crashing_expr()
def test_inferior_crashing_step(self):
"""Test that stepping after a crash behaves correctly."""
self.build()
self.inferior_crashing_step()
@skipIfTargetAndroid() # debuggerd interferes with this test on Android
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24778")
def test_inferior_crashing_step_after_break(self):
"""Test that lldb functions correctly after stepping through a crash."""
self.build()
self.inferior_crashing_step_after_break()
# Inferior exits after stepping after a segfault. This is working as
# intended IMHO.
@skipIf(oslist=["freebsd", "linux", "netbsd"])
def test_inferior_crashing_expr_step_and_expr(self):
"""Test that lldb expressions work before and after stepping after a crash."""
self.build()
self.inferior_crashing_expr_step_expr()
def set_breakpoint(self, line):
lldbutil.run_break_set_by_file_and_line(
self, "main.c", line, num_expected_locations=1, loc_exact=True
)
def check_stop_reason(self):
# We should have one crashing thread
self.assertEqual(
len(
lldbutil.get_crashed_threads(
self, self.dbg.GetSelectedTarget().GetProcess()
)
),
1,
STOPPED_DUE_TO_EXC_BAD_ACCESS,
)
def get_api_stop_reason(self):
return lldb.eStopReasonException
def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
# Find the line number of the crash.
self.line = line_number("main.c", "// Crash here.")
def inferior_crashing(self):
"""Inferior crashes upon launching; lldb should catch the event and stop."""
exe = self.getBuildArtifact("a.out")
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
self.runCmd("run", RUN_SUCCEEDED)
# The exact stop reason depends on the platform
if self.platformIsDarwin():
stop_reason = "stop reason = EXC_BAD_ACCESS"
elif self.getPlatform() == "linux" or self.getPlatform() == "freebsd":
stop_reason = "stop reason = signal SIGSEGV"
else:
stop_reason = "stop reason = invalid address"
self.expect(
"thread list",
STOPPED_DUE_TO_EXC_BAD_ACCESS,
substrs=["stopped", stop_reason],
)
# And it should report the correct line number.
self.expect(
"thread backtrace all", substrs=[stop_reason, "main.c:%d" % self.line]
)
def inferior_crashing_python(self):
"""Inferior crashes upon launching; lldb should catch the event and stop."""
exe = self.getBuildArtifact("a.out")
target = self.dbg.CreateTarget(exe)
self.assertTrue(target, VALID_TARGET)
# Now launch the process, and do not stop at entry point.
# Both argv and envp are null.
process = target.LaunchSimple(None, None, self.get_process_working_directory())
if process.GetState() != lldb.eStateStopped:
self.fail(
"Process should be in the 'stopped' state, "
"instead the actual state is: '%s'"
% lldbutil.state_type_to_str(process.GetState())
)
threads = lldbutil.get_crashed_threads(self, process)
self.assertEqual(
len(threads), 1, "Failed to stop the thread upon bad access exception"
)
if self.TraceOn():
lldbutil.print_stacktrace(threads[0])
def inferior_crashing_registers(self):
"""Test that lldb can read registers after crashing."""
exe = self.getBuildArtifact("a.out")
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
self.runCmd("run", RUN_SUCCEEDED)
self.check_stop_reason()
# lldb should be able to read from registers from the inferior after
# crashing.
lldbplatformutil.check_first_register_readable(self)
def inferior_crashing_expr(self):
"""Test that the lldb expression interpreter can read symbols after crashing."""
exe = self.getBuildArtifact("a.out")
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
self.runCmd("run", RUN_SUCCEEDED)
self.check_stop_reason()
# The lldb expression interpreter should be able to read from addresses
# of the inferior after a crash.
self.expect("expression argc", startstr="(int) $0 = 1")
self.expect("expression hello_world", substrs=["Hello"])
def inferior_crashing_step(self):
"""Test that lldb functions correctly after stepping through a crash."""
exe = self.getBuildArtifact("a.out")
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
self.set_breakpoint(self.line)
self.runCmd("run", RUN_SUCCEEDED)
self.expect(
"thread list",
STOPPED_DUE_TO_BREAKPOINT,
substrs=["main.c:%d" % self.line, "stop reason = breakpoint"],
)
self.runCmd("next")
self.check_stop_reason()
# The lldb expression interpreter should be able to read from addresses
# of the inferior after a crash.
self.expect("expression argv[0]", substrs=["a.out"])
self.expect("expression null_ptr", substrs=["= 0x0"])
# lldb should be able to read from registers from the inferior after
# crashing.
lldbplatformutil.check_first_register_readable(self)
# And it should report the correct line number.
self.expect("thread backtrace all", substrs=["main.c:%d" % self.line])
@expectedFailureNetBSD
def inferior_crashing_step_after_break(self):
"""Test that lldb behaves correctly when stepping after a crash."""
exe = self.getBuildArtifact("a.out")
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
self.runCmd("run", RUN_SUCCEEDED)
self.check_stop_reason()
expected_state = "exited" # Provide the exit code.
if self.platformIsDarwin():
# TODO: Determine why 'next' and 'continue' have no effect after a
# crash.
expected_state = "stopped"
self.expect("next", substrs=["Process", expected_state])
if expected_state == "exited":
self.expect("thread list", error=True, substrs=["Process must be launched"])
else:
self.check_stop_reason()
def inferior_crashing_expr_step_expr(self):
"""Test that lldb expressions work before and after stepping after a crash."""
exe = self.getBuildArtifact("a.out")
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
self.runCmd("run", RUN_SUCCEEDED)
self.check_stop_reason()
# The lldb expression interpreter should be able to read from addresses
# of the inferior after a crash.
self.expect("expression argv[0]", substrs=["a.out"])
self.runCmd("next")
self.check_stop_reason()
# The lldb expression interpreter should be able to read from addresses
# of the inferior after a crash.
self.expect("expression argv[0]", substrs=["a.out"])
|