forked from OSchip/llvm-project
Report inferior SIGSEGV as a signal instead of an exception on linux
Summary: Previously, we reported inferior receiving SIGSEGV (or SIGILL, SIGFPE, SIGBUS) as an "exception" to LLDB, presumably to match OSX behaviour. Beside the fact that we were basically lying to the user, this was also causing problems with inferiors which handle SIGSEGV by themselves, since LLDB was unable to reinject this signal back into the inferior. This commit changes LLGS to report SIGSEGV as a signal. This has necessitated some changes in the test-suite, which had previously used eStopReasonException to locate threads that crashed. Now it uses platform-specific logic, which in the case of linux searches for eStopReasonSignaled with signal=SIGSEGV. I have also added the ability to set the description of StopInfoUnixSignal using the description field of the gdb-remote packet. The linux stub uses this to display additional information about the segfault (invalid address, address access protected, etc.). Test Plan: All tests pass on linux and osx. Reviewers: ovyalov, clayborg, emaste Subscribers: emaste, lldb-commits Differential Revision: http://reviews.llvm.org/D10057 llvm-svn: 238549
This commit is contained in:
parent
43a298cb36
commit
c4e25c9648
|
@ -164,7 +164,7 @@ public:
|
|||
CreateStopReasonWithWatchpointID (Thread &thread, lldb::break_id_t watch_id);
|
||||
|
||||
static lldb::StopInfoSP
|
||||
CreateStopReasonWithSignal (Thread &thread, int signo);
|
||||
CreateStopReasonWithSignal (Thread &thread, int signo, const char *description = nullptr);
|
||||
|
||||
static lldb::StopInfoSP
|
||||
CreateStopReasonToTrace (Thread &thread);
|
||||
|
|
|
@ -2177,7 +2177,7 @@ NativeProcessLinux::MonitorSignal(const siginfo_t *info, lldb::pid_t pid, bool e
|
|||
// leave the signal intact if this is the thread that was chosen as the
|
||||
// triggering thread.
|
||||
if (m_pending_notification_up && m_pending_notification_up->triggering_tid == pid)
|
||||
linux_thread_sp->SetStoppedBySignal(SIGSTOP);
|
||||
linux_thread_sp->SetStoppedBySignal(SIGSTOP, info);
|
||||
else
|
||||
linux_thread_sp->SetStoppedBySignal(0);
|
||||
|
||||
|
@ -2217,22 +2217,8 @@ NativeProcessLinux::MonitorSignal(const siginfo_t *info, lldb::pid_t pid, bool e
|
|||
// This thread is stopped.
|
||||
ThreadDidStop (pid, false);
|
||||
|
||||
switch (signo)
|
||||
{
|
||||
case SIGSEGV:
|
||||
case SIGILL:
|
||||
case SIGFPE:
|
||||
case SIGBUS:
|
||||
if (thread_sp)
|
||||
std::static_pointer_cast<NativeThreadLinux> (thread_sp)->SetCrashedWithException (*info);
|
||||
break;
|
||||
default:
|
||||
// This is just a pre-signal-delivery notification of the incoming signal.
|
||||
if (thread_sp)
|
||||
std::static_pointer_cast<NativeThreadLinux> (thread_sp)->SetStoppedBySignal (signo);
|
||||
|
||||
break;
|
||||
}
|
||||
if (thread_sp)
|
||||
std::static_pointer_cast<NativeThreadLinux> (thread_sp)->SetStoppedBySignal(signo, info);
|
||||
|
||||
// Send a stop to the debugger after we get all other threads to stop.
|
||||
StopRunningThreads (pid);
|
||||
|
|
|
@ -125,15 +125,7 @@ NativeThreadLinux::GetStopReason (ThreadStopInfo &stop_info, std::string& descri
|
|||
if (log)
|
||||
LogThreadStopInfo (*log, m_stop_info, "m_stop_info in thread:");
|
||||
stop_info = m_stop_info;
|
||||
switch (m_stop_info.reason)
|
||||
{
|
||||
case StopReason::eStopReasonException:
|
||||
case StopReason::eStopReasonBreakpoint:
|
||||
case StopReason::eStopReasonWatchpoint:
|
||||
description = m_stop_description;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
description = m_stop_description;
|
||||
if (log)
|
||||
LogThreadStopInfo (*log, stop_info, "returned stop_info:");
|
||||
|
||||
|
@ -250,7 +242,7 @@ NativeThreadLinux::SetStepping ()
|
|||
}
|
||||
|
||||
void
|
||||
NativeThreadLinux::SetStoppedBySignal (uint32_t signo)
|
||||
NativeThreadLinux::SetStoppedBySignal(uint32_t signo, const siginfo_t *info)
|
||||
{
|
||||
Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
|
||||
if (log)
|
||||
|
@ -262,6 +254,20 @@ NativeThreadLinux::SetStoppedBySignal (uint32_t signo)
|
|||
|
||||
m_stop_info.reason = StopReason::eStopReasonSignal;
|
||||
m_stop_info.details.signal.signo = signo;
|
||||
|
||||
m_stop_description.clear();
|
||||
switch (signo)
|
||||
{
|
||||
case SIGSEGV:
|
||||
case SIGBUS:
|
||||
case SIGFPE:
|
||||
case SIGILL:
|
||||
if (! info)
|
||||
break;
|
||||
const auto reason = GetCrashReason(*info);
|
||||
m_stop_description = GetCrashReasonString(reason, reinterpret_cast<uintptr_t>(info->si_addr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -355,20 +361,6 @@ NativeThreadLinux::SetStoppedByTrace ()
|
|||
m_stop_info.details.signal.signo = SIGTRAP;
|
||||
}
|
||||
|
||||
void
|
||||
NativeThreadLinux::SetCrashedWithException (const siginfo_t& info)
|
||||
{
|
||||
const StateType new_state = StateType::eStateCrashed;
|
||||
MaybeLogStateChange (new_state);
|
||||
m_state = new_state;
|
||||
|
||||
m_stop_info.reason = StopReason::eStopReasonException;
|
||||
m_stop_info.details.signal.signo = info.si_signo;
|
||||
|
||||
const auto reason = GetCrashReason (info);
|
||||
m_stop_description = GetCrashReasonString (reason, reinterpret_cast<uintptr_t>(info.si_addr));
|
||||
}
|
||||
|
||||
void
|
||||
NativeThreadLinux::SetSuspended ()
|
||||
{
|
||||
|
|
|
@ -60,7 +60,7 @@ namespace process_linux {
|
|||
SetStepping ();
|
||||
|
||||
void
|
||||
SetStoppedBySignal (uint32_t signo);
|
||||
SetStoppedBySignal(uint32_t signo, const siginfo_t *info = nullptr);
|
||||
|
||||
/// Return true if the thread is stopped.
|
||||
/// If stopped by a signal, indicate the signo in the signo argument.
|
||||
|
|
|
@ -134,69 +134,69 @@ GetCrashReasonString (CrashReason reason, lldb::addr_t fault_addr)
|
|||
break;
|
||||
|
||||
case CrashReason::eInvalidAddress:
|
||||
str = "invalid address";
|
||||
str = "signal SIGSEGV: invalid address";
|
||||
AppendFaultAddr (str, fault_addr);
|
||||
break;
|
||||
case CrashReason::ePrivilegedAddress:
|
||||
str = "address access protected";
|
||||
str = "signal SIGSEGV: address access protected";
|
||||
AppendFaultAddr (str, fault_addr);
|
||||
break;
|
||||
case CrashReason::eIllegalOpcode:
|
||||
str = "illegal instruction";
|
||||
str = "signal SIGILL: illegal instruction";
|
||||
break;
|
||||
case CrashReason::eIllegalOperand:
|
||||
str = "illegal instruction operand";
|
||||
str = "signal SIGILL: illegal instruction operand";
|
||||
break;
|
||||
case CrashReason::eIllegalAddressingMode:
|
||||
str = "illegal addressing mode";
|
||||
str = "signal SIGILL: illegal addressing mode";
|
||||
break;
|
||||
case CrashReason::eIllegalTrap:
|
||||
str = "illegal trap";
|
||||
str = "signal SIGILL: illegal trap";
|
||||
break;
|
||||
case CrashReason::ePrivilegedOpcode:
|
||||
str = "privileged instruction";
|
||||
str = "signal SIGILL: privileged instruction";
|
||||
break;
|
||||
case CrashReason::ePrivilegedRegister:
|
||||
str = "privileged register";
|
||||
str = "signal SIGILL: privileged register";
|
||||
break;
|
||||
case CrashReason::eCoprocessorError:
|
||||
str = "coprocessor error";
|
||||
str = "signal SIGILL: coprocessor error";
|
||||
break;
|
||||
case CrashReason::eInternalStackError:
|
||||
str = "internal stack error";
|
||||
str = "signal SIGILL: internal stack error";
|
||||
break;
|
||||
case CrashReason::eIllegalAlignment:
|
||||
str = "illegal alignment";
|
||||
str = "signal SIGBUS: illegal alignment";
|
||||
break;
|
||||
case CrashReason::eIllegalAddress:
|
||||
str = "illegal address";
|
||||
str = "signal SIGBUS: illegal address";
|
||||
break;
|
||||
case CrashReason::eHardwareError:
|
||||
str = "hardware error";
|
||||
str = "signal SIGBUS: hardware error";
|
||||
break;
|
||||
case CrashReason::eIntegerDivideByZero:
|
||||
str = "integer divide by zero";
|
||||
str = "signal SIGFPE: integer divide by zero";
|
||||
break;
|
||||
case CrashReason::eIntegerOverflow:
|
||||
str = "integer overflow";
|
||||
str = "signal SIGFPE: integer overflow";
|
||||
break;
|
||||
case CrashReason::eFloatDivideByZero:
|
||||
str = "floating point divide by zero";
|
||||
str = "signal SIGFPE: floating point divide by zero";
|
||||
break;
|
||||
case CrashReason::eFloatOverflow:
|
||||
str = "floating point overflow";
|
||||
str = "signal SIGFPE: floating point overflow";
|
||||
break;
|
||||
case CrashReason::eFloatUnderflow:
|
||||
str = "floating point underflow";
|
||||
str = "signal SIGFPE: floating point underflow";
|
||||
break;
|
||||
case CrashReason::eFloatInexactResult:
|
||||
str = "inexact floating point result";
|
||||
str = "signal SIGFPE: inexact floating point result";
|
||||
break;
|
||||
case CrashReason::eFloatInvalidOperation:
|
||||
str = "invalid floating point operation";
|
||||
str = "signal SIGFPE: invalid floating point operation";
|
||||
break;
|
||||
case CrashReason::eFloatSubscriptRange:
|
||||
str = "invalid floating point subscript range";
|
||||
str = "signal SIGFPE: invalid floating point subscript range";
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -2080,11 +2080,11 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet)
|
|||
if (thread_sp->GetTemporaryResumeState() == eStateStepping)
|
||||
thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp));
|
||||
else
|
||||
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal(*thread_sp, signo));
|
||||
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal(*thread_sp, signo, description.c_str()));
|
||||
}
|
||||
}
|
||||
if (!handled)
|
||||
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal (*thread_sp, signo));
|
||||
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal (*thread_sp, signo, description.c_str()));
|
||||
}
|
||||
|
||||
if (!description.empty())
|
||||
|
|
|
@ -871,9 +871,10 @@ class StopInfoUnixSignal : public StopInfo
|
|||
{
|
||||
public:
|
||||
|
||||
StopInfoUnixSignal (Thread &thread, int signo) :
|
||||
StopInfoUnixSignal (Thread &thread, int signo, const char *description) :
|
||||
StopInfo (thread, signo)
|
||||
{
|
||||
SetDescription (description);
|
||||
}
|
||||
|
||||
virtual ~StopInfoUnixSignal ()
|
||||
|
@ -1161,9 +1162,9 @@ StopInfo::CreateStopReasonWithWatchpointID (Thread &thread, break_id_t watch_id)
|
|||
}
|
||||
|
||||
StopInfoSP
|
||||
StopInfo::CreateStopReasonWithSignal (Thread &thread, int signo)
|
||||
StopInfo::CreateStopReasonWithSignal (Thread &thread, int signo, const char *description)
|
||||
{
|
||||
return StopInfoSP (new StopInfoUnixSignal (thread, signo));
|
||||
return StopInfoSP (new StopInfoUnixSignal (thread, signo, description));
|
||||
}
|
||||
|
||||
StopInfoSP
|
||||
|
|
|
@ -49,20 +49,14 @@ class ChangedInferiorTestCase(TestBase):
|
|||
|
||||
self.runCmd("run", RUN_FAILED)
|
||||
|
||||
if self.platformIsDarwin():
|
||||
stop_reason = 'stop reason = EXC_BAD_ACCESS'
|
||||
else:
|
||||
stop_reason = 'stop reason = invalid address'
|
||||
|
||||
# The stop reason of the thread should be a bad access exception.
|
||||
self.expect("thread list", STOPPED_DUE_TO_EXC_BAD_ACCESS,
|
||||
substrs = ['stopped',
|
||||
stop_reason])
|
||||
# We should have one crashing thread
|
||||
self.assertEquals(
|
||||
len(lldbutil.get_crashed_threads(self, self.dbg.GetSelectedTarget().GetProcess())),
|
||||
1,
|
||||
STOPPED_DUE_TO_EXC_BAD_ACCESS)
|
||||
|
||||
# And it should report the correct line number.
|
||||
self.expect("thread backtrace all",
|
||||
substrs = [stop_reason,
|
||||
'main.c:%d' % self.line1])
|
||||
self.expect("thread backtrace all", substrs = ['main.c:%d' % self.line1])
|
||||
|
||||
def inferior_not_crashing(self):
|
||||
"""Test lldb reloads the inferior after it was changed during the session."""
|
||||
|
@ -73,13 +67,10 @@ class ChangedInferiorTestCase(TestBase):
|
|||
self.runCmd("run", RUN_FAILED)
|
||||
self.runCmd("process status")
|
||||
|
||||
if self.platformIsDarwin():
|
||||
stop_reason = 'EXC_BAD_ACCESS'
|
||||
else:
|
||||
stop_reason = 'invalid address'
|
||||
|
||||
if stop_reason in self.res.GetOutput():
|
||||
self.fail("Inferior changed, but lldb did not perform a reload")
|
||||
self.assertNotEquals(
|
||||
len(lldbutil.get_crashed_threads(self, self.dbg.GetSelectedTarget().GetProcess())),
|
||||
1,
|
||||
"Inferior changed, but lldb did not perform a reload")
|
||||
|
||||
# Break inside the main.
|
||||
lldbutil.run_break_set_by_file_and_line (self, "main2.c", self.line2, num_expected_locations=1, loc_exact=True)
|
||||
|
|
|
@ -79,7 +79,7 @@ class CrashingInferiorTestCase(TestBase):
|
|||
self.inferior_crashing_expr_step_expr()
|
||||
|
||||
@expectedFailureFreeBSD('llvm.org/pr15989') # Couldn't allocate space for the stack frame
|
||||
@expectedFailureAll("llvm.org/pr23139", oslist=["linux"], compiler="gcc", compiler_version=[">=","4.9"], archs=["i386"])
|
||||
@skipIfLinux # Inferior exits after stepping after a segfault. This is working as intended IMHO.
|
||||
def test_inferior_crashing_expr_step_and_expr_dwarf(self):
|
||||
"""Test that lldb expressions work before and after stepping after a crash."""
|
||||
self.buildDwarf()
|
||||
|
@ -89,17 +89,11 @@ class CrashingInferiorTestCase(TestBase):
|
|||
lldbutil.run_break_set_by_file_and_line (self, "main.c", line, num_expected_locations=1, loc_exact=True)
|
||||
|
||||
def check_stop_reason(self):
|
||||
if self.platformIsDarwin():
|
||||
stop_reason = 'stop reason = EXC_BAD_ACCESS'
|
||||
else:
|
||||
stop_reason = 'stop reason = invalid address'
|
||||
|
||||
# The stop reason of the thread should be a bad access exception.
|
||||
self.expect("thread list", STOPPED_DUE_TO_EXC_BAD_ACCESS,
|
||||
substrs = ['stopped',
|
||||
stop_reason])
|
||||
|
||||
return stop_reason
|
||||
# We should have one crashing thread
|
||||
self.assertEquals(
|
||||
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
|
||||
|
@ -116,7 +110,16 @@ class CrashingInferiorTestCase(TestBase):
|
|||
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
|
||||
|
||||
self.runCmd("run", RUN_FAILED)
|
||||
stop_reason = self.check_stop_reason()
|
||||
# The exact stop reason depends on the platform
|
||||
if self.platformIsDarwin():
|
||||
stop_reason = 'stop reason = EXC_BAD_ACCESS'
|
||||
elif self.getPlatform() == "linux":
|
||||
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",
|
||||
|
@ -139,12 +142,11 @@ class CrashingInferiorTestCase(TestBase):
|
|||
"instead the actual state is: '%s'" %
|
||||
lldbutil.state_type_to_str(process.GetState()))
|
||||
|
||||
thread = lldbutil.get_stopped_thread(process, self.get_api_stop_reason())
|
||||
if not thread:
|
||||
self.fail("Fail to stop the thread upon bad access exception")
|
||||
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(thread)
|
||||
lldbutil.print_stacktrace(threads[0])
|
||||
|
||||
def inferior_crashing_registers(self):
|
||||
"""Test that lldb can read registers after crashing."""
|
||||
|
@ -185,7 +187,7 @@ class CrashingInferiorTestCase(TestBase):
|
|||
'stop reason = breakpoint'])
|
||||
|
||||
self.runCmd("next")
|
||||
stop_reason = self.check_stop_reason()
|
||||
self.check_stop_reason()
|
||||
|
||||
# The lldb expression interpreter should be able to read from addresses of the inferior after a crash.
|
||||
self.expect("p argv[0]",
|
||||
|
@ -198,8 +200,7 @@ class CrashingInferiorTestCase(TestBase):
|
|||
|
||||
# And it should report the correct line number.
|
||||
self.expect("thread backtrace all",
|
||||
substrs = [stop_reason,
|
||||
'main.c:%d' % self.line])
|
||||
substrs = ['main.c:%d' % self.line])
|
||||
|
||||
def inferior_crashing_step_after_break(self):
|
||||
"""Test that lldb behaves correctly when stepping after a crash."""
|
||||
|
@ -209,8 +210,17 @@ class CrashingInferiorTestCase(TestBase):
|
|||
self.runCmd("run", RUN_FAILED)
|
||||
self.check_stop_reason()
|
||||
|
||||
self.runCmd("next")
|
||||
self.check_stop_reason()
|
||||
expected_state = 'exited' # Provide the exit code.
|
||||
if self.platformIsDarwin():
|
||||
expected_state = 'stopped' # TODO: Determine why 'next' and 'continue' have no effect after a crash.
|
||||
|
||||
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."""
|
||||
|
|
|
@ -79,6 +79,7 @@ class CrashingRecursiveInferiorTestCase(TestBase):
|
|||
self.recursive_inferior_crashing_expr_step_expr()
|
||||
|
||||
@expectedFailureFreeBSD('llvm.org/pr15989') # Couldn't allocate space for the stack frame
|
||||
@skipIfLinux # Inferior exits after stepping after a segfault. This is working as intended IMHO.
|
||||
def test_recursive_inferior_crashing_expr_step_and_expr_dwarf(self):
|
||||
"""Test that lldb expressions work before and after stepping after a crash."""
|
||||
self.buildDwarf()
|
||||
|
@ -88,17 +89,11 @@ class CrashingRecursiveInferiorTestCase(TestBase):
|
|||
lldbutil.run_break_set_by_file_and_line (self, "main.c", line, num_expected_locations=1, loc_exact=True)
|
||||
|
||||
def check_stop_reason(self):
|
||||
if self.platformIsDarwin():
|
||||
stop_reason = 'stop reason = EXC_BAD_ACCESS'
|
||||
else:
|
||||
stop_reason = 'stop reason = invalid address'
|
||||
|
||||
# The stop reason of the thread should be a bad access exception.
|
||||
self.expect("thread list", STOPPED_DUE_TO_EXC_BAD_ACCESS,
|
||||
substrs = ['stopped',
|
||||
stop_reason])
|
||||
|
||||
return stop_reason
|
||||
# We should have one crashing thread
|
||||
self.assertEquals(
|
||||
len(lldbutil.get_crashed_threads(self, self.dbg.GetSelectedTarget().GetProcess())),
|
||||
1,
|
||||
STOPPED_DUE_TO_EXC_BAD_ACCESS)
|
||||
|
||||
def setUp(self):
|
||||
# Call super's setUp().
|
||||
|
@ -112,7 +107,17 @@ class CrashingRecursiveInferiorTestCase(TestBase):
|
|||
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
|
||||
|
||||
self.runCmd("run", RUN_FAILED)
|
||||
stop_reason = self.check_stop_reason()
|
||||
|
||||
# The exact stop reason depends on the platform
|
||||
if self.platformIsDarwin():
|
||||
stop_reason = 'stop reason = EXC_BAD_ACCESS'
|
||||
elif self.getPlatform() == "linux":
|
||||
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 a backtrace that includes main and the crash site.
|
||||
self.expect("thread backtrace all",
|
||||
|
@ -139,12 +144,11 @@ class CrashingRecursiveInferiorTestCase(TestBase):
|
|||
"instead the actual state is: '%s'" %
|
||||
lldbutil.state_type_to_str(process.GetState()))
|
||||
|
||||
thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonException)
|
||||
if not thread:
|
||||
self.fail("Fail to stop the thread upon bad access exception")
|
||||
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(thread)
|
||||
lldbutil.print_stacktrace(threads[0])
|
||||
|
||||
def recursive_inferior_crashing_registers(self):
|
||||
"""Test that lldb can read registers after crashing."""
|
||||
|
@ -182,7 +186,7 @@ class CrashingRecursiveInferiorTestCase(TestBase):
|
|||
'stop reason = breakpoint'])
|
||||
|
||||
self.runCmd("next")
|
||||
stop_reason = self.check_stop_reason()
|
||||
self.check_stop_reason()
|
||||
|
||||
# The lldb expression interpreter should be able to read from addresses of the inferior after a crash.
|
||||
self.expect("p i",
|
||||
|
@ -193,8 +197,7 @@ class CrashingRecursiveInferiorTestCase(TestBase):
|
|||
|
||||
# And it should report the correct line number.
|
||||
self.expect("thread backtrace all",
|
||||
substrs = [stop_reason,
|
||||
'main.c:%d' % self.line])
|
||||
substrs = ['main.c:%d' % self.line])
|
||||
|
||||
def recursive_inferior_crashing_step_after_break(self):
|
||||
"""Test that lldb behaves correctly when stepping after a crash."""
|
||||
|
@ -205,7 +208,7 @@ class CrashingRecursiveInferiorTestCase(TestBase):
|
|||
self.check_stop_reason()
|
||||
|
||||
expected_state = 'exited' # Provide the exit code.
|
||||
if self.platformIsDarwin() or self.getPlatform() == "linux":
|
||||
if self.platformIsDarwin():
|
||||
expected_state = 'stopped' # TODO: Determine why 'next' and 'continue' have no effect after a crash.
|
||||
|
||||
self.expect("next",
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
LEVEL = ../../../make
|
||||
|
||||
C_SOURCES := main.c
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
|
@ -0,0 +1,45 @@
|
|||
"""Test that we can debug inferiors that handle SIGSEGV by themselves"""
|
||||
|
||||
import os
|
||||
import unittest2
|
||||
import lldb
|
||||
from lldbtest import *
|
||||
import lldbutil
|
||||
import re
|
||||
|
||||
|
||||
class HandleSegvTestCase(TestBase):
|
||||
|
||||
mydir = TestBase.compute_mydir(__file__)
|
||||
|
||||
@skipIfWindows # signals do not exist on Windows
|
||||
@skipIfDarwin
|
||||
def test_inferior_handle_sigsegv_with_dwarf(self):
|
||||
self.buildDefault()
|
||||
exe = os.path.join(os.getcwd(), "a.out")
|
||||
|
||||
# Create a target by the debugger.
|
||||
target = self.dbg.CreateTarget(exe)
|
||||
self.assertTrue(target, VALID_TARGET)
|
||||
|
||||
# launch
|
||||
process = target.LaunchSimple(None, None, self.get_process_working_directory())
|
||||
self.assertTrue(process, PROCESS_IS_VALID)
|
||||
self.assertEqual(process.GetState(), lldb.eStateStopped)
|
||||
signo = process.GetUnixSignals().GetSignalNumberFromName("SIGSEGV")
|
||||
|
||||
thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonSignal)
|
||||
self.assertTrue(thread.IsValid(), "Thread should be stopped due to a signal")
|
||||
self.assertTrue(thread.GetStopReasonDataCount() >= 1, "There was data in the event.")
|
||||
self.assertEqual(thread.GetStopReasonDataAtIndex(0), signo, "The stop signal was SIGSEGV")
|
||||
|
||||
# Continue until we exit.
|
||||
process.Continue()
|
||||
self.assertEqual(process.GetState(), lldb.eStateExited)
|
||||
self.assertEqual(process.GetExitStatus(), 0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import atexit
|
||||
lldb.SBDebugger.Initialize()
|
||||
atexit.register(lambda: lldb.SBDebugger.Terminate())
|
||||
unittest2.main()
|
|
@ -0,0 +1,58 @@
|
|||
#include <sys/mman.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
enum {
|
||||
kMmapSize = 0x1000,
|
||||
kMagicValue = 47,
|
||||
};
|
||||
|
||||
void *address;
|
||||
volatile sig_atomic_t signaled = 0;
|
||||
|
||||
void handler(int sig)
|
||||
{
|
||||
signaled = 1;
|
||||
if (munmap(address, kMmapSize) != 0)
|
||||
{
|
||||
perror("munmap");
|
||||
_exit(5);
|
||||
}
|
||||
|
||||
void* newaddr = mmap(address, kMmapSize, PROT_READ | PROT_WRITE,
|
||||
MAP_ANON | MAP_FIXED | MAP_PRIVATE, -1, 0);
|
||||
if (newaddr != address)
|
||||
{
|
||||
fprintf(stderr, "Newly mmaped address (%p) does not equal old address (%p).\n",
|
||||
newaddr, address);
|
||||
_exit(6);
|
||||
}
|
||||
*(int*)newaddr = kMagicValue;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
if (signal(SIGSEGV, handler) == SIG_ERR)
|
||||
{
|
||||
perror("signal");
|
||||
return 1;
|
||||
}
|
||||
|
||||
address = mmap(NULL, kMmapSize, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
if (address == MAP_FAILED)
|
||||
{
|
||||
perror("mmap");
|
||||
return 2;
|
||||
}
|
||||
|
||||
// This should first trigger a segfault. Our handler will make the memory readable and write
|
||||
// the magic value into memory.
|
||||
if (*(int*)address != kMagicValue)
|
||||
return 3;
|
||||
|
||||
if (! signaled)
|
||||
return 4;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -390,6 +390,13 @@ class ConcurrentEventsTestCase(TestBase):
|
|||
self.crash_count > 0 or \
|
||||
self.inferior_process.GetState() == lldb.eStateExited
|
||||
|
||||
def count_signaled_threads(self):
|
||||
count = 0
|
||||
for thread in self.inferior_process:
|
||||
if thread.GetStopReason() == lldb.eStopReasonSignal and thread.GetStopReasonDataAtIndex(0) == self.inferior_process.GetUnixSignals().GetSignalNumberFromName('SIGUSR1'):
|
||||
count += 1
|
||||
return count
|
||||
|
||||
def do_thread_actions(self,
|
||||
num_breakpoint_threads = 0,
|
||||
num_signal_threads = 0,
|
||||
|
@ -470,16 +477,16 @@ class ConcurrentEventsTestCase(TestBase):
|
|||
num_threads,
|
||||
"\n\t".join(self.describe_threads())))
|
||||
|
||||
self.signal_count = len(lldbutil.get_stopped_threads(self.inferior_process, lldb.eStopReasonSignal))
|
||||
self.crash_count = len(lldbutil.get_stopped_threads(self.inferior_process, lldb.eStopReasonException))
|
||||
self.signal_count = self.count_signaled_threads()
|
||||
self.crash_count = len(lldbutil.get_crashed_threads(self, self.inferior_process))
|
||||
|
||||
# Run to completion (or crash)
|
||||
while not self.inferior_done():
|
||||
if self.TraceOn():
|
||||
self.runCmd("thread backtrace all")
|
||||
self.runCmd("continue")
|
||||
self.signal_count += len(lldbutil.get_stopped_threads(self.inferior_process, lldb.eStopReasonSignal))
|
||||
self.crash_count += len(lldbutil.get_stopped_threads(self.inferior_process, lldb.eStopReasonException))
|
||||
self.signal_count += self.count_signaled_threads()
|
||||
self.crash_count += len(lldbutil.get_crashed_threads(self, self.inferior_process))
|
||||
|
||||
if num_crash_threads > 0 or num_delay_crash_threads > 0:
|
||||
# Expecting a crash
|
||||
|
|
|
@ -553,6 +553,25 @@ def get_threads_stopped_at_breakpoint (process, bkpt):
|
|||
|
||||
return threads
|
||||
|
||||
def is_thread_crashed (test, thread):
|
||||
"""In the test suite we dereference a null pointer to simulate a crash. The way this is
|
||||
reported depends on the platform."""
|
||||
if test.platformIsDarwin():
|
||||
return thread.GetStopReason() == lldb.eStopReasonException and "EXC_BAD_ACCESS" in thread.GetStopDescription(100)
|
||||
elif test.getPlatform() == "linux":
|
||||
return thread.GetStopReason() == lldb.eStopReasonSignal and thread.GetStopReasonDataAtIndex(0) == thread.GetProcess().GetUnixSignals().GetSignalNumberFromName("SIGSEGV")
|
||||
else:
|
||||
return "invalid address" in thread.GetStopDescription(100)
|
||||
|
||||
def get_crashed_threads (test, process):
|
||||
threads = []
|
||||
if process.GetState() != lldb.eStateStopped:
|
||||
return threads
|
||||
for thread in process:
|
||||
if is_thread_crashed(test, thread):
|
||||
threads.append(thread)
|
||||
return threads
|
||||
|
||||
def continue_to_breakpoint (process, bkpt):
|
||||
""" Continues the process, if it stops, returns the threads stopped at bkpt; otherwise, returns None"""
|
||||
process.Continue()
|
||||
|
|
Loading…
Reference in New Issue