From abbc960e9631e9ef6d17c8906b987e69e36f9dea Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 10 Jun 2016 19:29:43 +0100 Subject: [PATCH] Linux crashlog: Use sigaction to get more info on fatal signals. --- config.lib | 28 ++++++++++++++++++++ src/os/unix/crashlog_unix.cpp | 50 ++++++++++++++++++++++++++++++++--- src/saveload/afterload.cpp | 42 +++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 4 deletions(-) diff --git a/config.lib b/config.lib index 4ff37a8e20..feee569613 100644 --- a/config.lib +++ b/config.lib @@ -1689,6 +1689,34 @@ EOL CFLAGS="$CFLAGS -g1" fi fi + + log 2 "executing $cc_host $CFLAGS $LDFLAGS $STATIC_FLAGS -o tmp.config.sigaction -x c++ - -ldl" + "$cc_host" $CFLAGS $LDFLAGS $STATIC_FLAGS -o tmp.config.sigaction -x c++ - -ldl 2> /dev/null << EOL + #include + void *addr; + int code; + void handler(int sig, siginfo_t *si, void *context) { + addr = si->si_addr; + code = si->si_code; + } + int main() { + struct sigaction sa; + sa.sa_flags = SA_SIGINFO; + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = handler; + sigaction(SIGSEGV, &sa, 0); + return 0; + } +EOL + ret=$? + rm -f tmp.config.sigaction + log 2 " exit code $ret" + if [ $ret -ne 0 ]; then + log 1 "checking sigaction... no" + else + log 1 "checking sigaction... found" + CFLAGS="$CFLAGS -DWITH_SIGACTION" + fi fi if [ "$os" = "MINGW" ]; then diff --git a/src/os/unix/crashlog_unix.cpp b/src/os/unix/crashlog_unix.cpp index d8f95908b4..5ab8e2a10c 100644 --- a/src/os/unix/crashlog_unix.cpp +++ b/src/os/unix/crashlog_unix.cpp @@ -78,6 +78,9 @@ static void LogStacktraceSigSegvHandler(int sig) class CrashLogUnix : public CrashLog { /** Signal that has been thrown. */ int signum; +#ifdef WITH_SIGACTION + siginfo_t *si; +#endif /* virtual */ char *LogOSVersion(char *buffer, const char *last) const { @@ -101,14 +104,29 @@ class CrashLogUnix : public CrashLog { /* virtual */ char *LogError(char *buffer, const char *last, const char *message) const { - return buffer + seprintf(buffer, last, + buffer += seprintf(buffer, last, "Crash reason:\n" - " Signal: %s (%d)\n" - " Message: %s\n\n", + " Signal: %s (%d)\n", strsignal(this->signum), - this->signum, + this->signum); +#ifdef WITH_SIGACTION + if (this->si) { + buffer += seprintf(buffer, last, + " si_code: %d\n", + this->si->si_code); + if (this->signum != SIGABRT) { + buffer += seprintf(buffer, last, + " fault address: %p\n", + this->si->si_addr); + } + } +#endif + buffer += seprintf(buffer, last, + " Message: %s\n\n", message == NULL ? "" : message ); + + return buffer; } #if defined(SUNOS) @@ -378,10 +396,17 @@ public: * A crash log is always generated by signal. * @param signum the signal that was caused by the crash. */ +#ifdef WITH_SIGACTION + CrashLogUnix(int signum, siginfo_t *si) : + signum(signum), si(si) + { + } +#else CrashLogUnix(int signum) : signum(signum) { } +#endif }; /** The signals we want our crash handler to handle. */ @@ -392,7 +417,11 @@ static const int _signals_to_handle[] = { SIGSEGV, SIGABRT, SIGFPE, SIGBUS, SIGI * @note Not static so it shows up in the backtrace. * @param signum the signal that caused us to crash. */ +#ifdef WITH_SIGACTION +static void CDECL HandleCrash(int signum, siginfo_t *si, void *context) +#else static void CDECL HandleCrash(int signum) +#endif { /* Disable all handling of signals by us, so we don't go into infinite loops. */ for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) { @@ -412,7 +441,11 @@ static void CDECL HandleCrash(int signum) abort(); } +#ifdef WITH_SIGACTION + CrashLogUnix log(signum, si); +#else CrashLogUnix log(signum); +#endif log.MakeCrashLog(); CrashLog::AfterCrashLogCleanup(); @@ -422,6 +455,15 @@ static void CDECL HandleCrash(int signum) /* static */ void CrashLog::InitialiseCrashLog() { for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) { +#ifdef WITH_SIGACTION + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_SIGINFO | SA_RESTART; + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = HandleCrash; + sigaction(*i, &sa, NULL); +#else signal(*i, HandleCrash); +#endif } } diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 9690481154..972a5edd82 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -302,12 +302,20 @@ static void InitializeWindowsAndCaches() BuildOwnerLegend(); } +#ifdef WITH_SIGACTION +static struct sigaction _prev_segfault; +static struct sigaction _prev_abort; +static struct sigaction _prev_fpe; + +static void CDECL HandleSavegameLoadCrash(int signum, siginfo_t *si, void *context); +#else typedef void (CDECL *SignalHandlerPointer)(int); static SignalHandlerPointer _prev_segfault = NULL; static SignalHandlerPointer _prev_abort = NULL; static SignalHandlerPointer _prev_fpe = NULL; static void CDECL HandleSavegameLoadCrash(int signum); +#endif /** * Replaces signal handlers of SIGSEGV and SIGABRT @@ -315,9 +323,20 @@ static void CDECL HandleSavegameLoadCrash(int signum); */ static void SetSignalHandlers() { +#ifdef WITH_SIGACTION + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_SIGINFO | SA_RESTART; + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = HandleSavegameLoadCrash; + sigaction(SIGSEGV, &sa, &_prev_segfault); + sigaction(SIGABRT, &sa, &_prev_abort); + sigaction(SIGFPE, &sa, &_prev_fpe); +#else _prev_segfault = signal(SIGSEGV, HandleSavegameLoadCrash); _prev_abort = signal(SIGABRT, HandleSavegameLoadCrash); _prev_fpe = signal(SIGFPE, HandleSavegameLoadCrash); +#endif } /** @@ -325,9 +344,15 @@ static void SetSignalHandlers() */ static void ResetSignalHandlers() { +#ifdef WITH_SIGACTION + sigaction(SIGSEGV, &_prev_segfault, NULL); + sigaction(SIGABRT, &_prev_abort, NULL); + sigaction(SIGFPE, &_prev_fpe, NULL); +#else signal(SIGSEGV, _prev_segfault); signal(SIGABRT, _prev_abort); signal(SIGFPE, _prev_fpe); +#endif } /** @@ -367,7 +392,11 @@ bool SaveloadCrashWithMissingNewGRFs() * NewGRFs that are required by the savegame. * @param signum received signal */ +#ifdef WITH_SIGACTION +static void CDECL HandleSavegameLoadCrash(int signum, siginfo_t *si, void *context) +#else static void CDECL HandleSavegameLoadCrash(int signum) +#endif { ResetSignalHandlers(); @@ -414,14 +443,27 @@ static void CDECL HandleSavegameLoadCrash(int signum) ShowInfo(buffer); +#ifdef WITH_SIGACTION + struct sigaction call; +#else SignalHandlerPointer call = NULL; +#endif switch (signum) { case SIGSEGV: call = _prev_segfault; break; case SIGABRT: call = _prev_abort; break; case SIGFPE: call = _prev_fpe; break; default: NOT_REACHED(); } +#ifdef WITH_SIGACTION + if (call.sa_flags & SA_SIGINFO) { + if (call.sa_sigaction != NULL) call.sa_sigaction(signum, si, context); + } else { + if (call.sa_handler != NULL) call.sa_handler(signum); + } +#else if (call != NULL) call(signum); +#endif + } /**