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:
Pavel Labath 2015-05-29 10:13:03 +00:00
parent 43a298cb36
commit c4e25c9648
15 changed files with 251 additions and 134 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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 ()
{

View File

@ -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.

View File

@ -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;
}

View File

@ -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())

View File

@ -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

View File

@ -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)

View File

@ -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."""

View File

@ -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",

View File

@ -0,0 +1,5 @@
LEVEL = ../../../make
C_SOURCES := main.c
include $(LEVEL)/Makefile.rules

View File

@ -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()

View File

@ -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;
}

View File

@ -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

View File

@ -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()