mirror of
https://github.com/ARMSX2/ARMSX3.git
synced 2026-08-24 16:58:52 -07:00
Give the SIGSEGV handler a stack to report from
Arkham City dies of SIGSEGV about 140ms after the game writes PS3Progress_Frame_1,
on both the Qualcomm driver and Turnip, with the database on or off, Multithreaded
RSX on or off, and the RSX profiler on or off. A table of ten runs showed no setting
correlates with it.
The reason it took so long to even establish that it WAS a segfault: nothing records
it. No tombstone (/data/tombstones is root-only, so its emptiness proves nothing), no
logcat crash-buffer entry, and no line from our own handler. The single witness
anywhere is Zygote:
I Zygote : Process <pid> exited due to signal 11 (Segmentation fault)
That combination is what a stack overflow looks like here. The handler is installed
with SA_SIGINFO alone, so it runs on the faulting thread's own stack; if that stack is
what overflowed there is nowhere to run, it faults again immediately, and the kernel
applies the default action having written nothing.
So: an alternate signal stack per thread in thread_base::initialize, and SA_ONSTACK on
the handler. Thread-local rather than shared, because two threads can fault at once and
a shared stack would corrupt whichever report lost the race.
This does not fix the fault. It makes the fault reportable, which is the thing that has
been missing all along: the next occurrence should log where it came from instead of
vanishing. Android only.
This commit is contained in:
@@ -2690,7 +2690,21 @@ void sigpipe_signaling_handler(int)
|
||||
const bool s_exception_handler_set = []() -> bool
|
||||
{
|
||||
struct ::sigaction sa;
|
||||
#ifdef __ANDROID__
|
||||
// Run the handler on the alternate stack installed per thread in
|
||||
// thread_base::initialize. Without this the handler runs on the faulting thread's own
|
||||
// stack, so a stack overflow has nowhere to report itself from: the handler faults
|
||||
// again immediately and the kernel applies the default action, killing the process
|
||||
// having written nothing.
|
||||
//
|
||||
// Arkham City does exactly that. The only record anywhere of the crash was a single
|
||||
// Zygote line, "exited due to signal 11 (Segmentation fault)", with no tombstone, no
|
||||
// crash-buffer entry and no line from this handler, which cost hours of diagnosing it
|
||||
// as an external kill.
|
||||
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
#else
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
#endif
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_sigaction = signal_handler;
|
||||
|
||||
@@ -2794,6 +2808,33 @@ void thread_base::start()
|
||||
|
||||
void thread_base::initialize(void (*error_cb)())
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
// Somewhere for the SIGSEGV handler to run, per thread. See SA_ONSTACK above.
|
||||
//
|
||||
// Deliberately a thread_local rather than a shared buffer: the handler can fire on any
|
||||
// thread, two threads can fault at once, and a shared stack would corrupt whichever
|
||||
// report lost the race. It is released with the thread, after which no handler can run
|
||||
// on it.
|
||||
//
|
||||
// 128KB because the handler formats and logs rather than just setting a flag. That is
|
||||
// real memory across the emulator's thread count, and it buys turning a silent death
|
||||
// into a reported one.
|
||||
static thread_local std::array<u8, 128 * 1024> s_signal_stack;
|
||||
|
||||
stack_t alt{};
|
||||
alt.ss_sp = s_signal_stack.data();
|
||||
alt.ss_size = s_signal_stack.size();
|
||||
alt.ss_flags = 0;
|
||||
|
||||
if (::sigaltstack(&alt, nullptr) == -1)
|
||||
{
|
||||
// Not fatal: the handler simply falls back to the faulting stack, which is the
|
||||
// behaviour everywhere else. Worth knowing about, because it means a stack
|
||||
// overflow will go unreported again.
|
||||
sig_log.error("sigaltstack failed (%d); stack overflows will not be reported", errno);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#ifdef __APPLE__
|
||||
while (!m_thread)
|
||||
|
||||
Reference in New Issue
Block a user