From d9082dfd9ab442dd33f552693fcc0f396a514bb6 Mon Sep 17 00:00:00 2001 From: Daniel Dunbar Date: Thu, 29 Jul 2010 01:21:47 +0000 Subject: [PATCH] CrashRecoveryContext: Add a simple POSIX implementation. - This works, but won't handle crashes on stack overflow, or signals delivered to a thread other than the one that crashed. The latter is particular annoying on Darwin, because SIGABRT tends to go to the main thread. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@109717 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Support/CrashRecoveryContext.cpp | 103 ++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/lib/Support/CrashRecoveryContext.cpp b/lib/Support/CrashRecoveryContext.cpp index 870221e1711..c079182bd1c 100644 --- a/lib/Support/CrashRecoveryContext.cpp +++ b/lib/Support/CrashRecoveryContext.cpp @@ -9,20 +9,29 @@ #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/ADT/SmallString.h" +#include "llvm/System/ThreadLocal.h" #include +#include using namespace llvm; namespace { struct CrashRecoveryContextImpl; +static sys::ThreadLocal CurrentContext; + struct CrashRecoveryContextImpl { std::string Backtrace; ::jmp_buf JumpBuffer; volatile unsigned Failed : 1; public: - CrashRecoveryContextImpl() : Failed(false) {} + CrashRecoveryContextImpl() : Failed(false) { + CurrentContext.set(this); + } + ~CrashRecoveryContextImpl() { + CurrentContext.set(0); + } void HandleCrash() { assert(!Failed && "Crash recovery context already failed!"); @@ -44,6 +53,10 @@ CrashRecoveryContext::~CrashRecoveryContext() { delete CRCI; } +#ifdef LLVM_ON_WIN32 + +// FIXME: No real Win32 implementation currently. + void CrashRecoveryContext::Enable() { if (gCrashRecoveryEnabled) return; @@ -58,6 +71,94 @@ void CrashRecoveryContext::Disable() { gCrashRecoveryEnabled = false; } +#else + +// Generic POSIX implementation. +// +// This implementation relies on synchronous signals being delivered to the +// current thread. We use a thread local object to keep track of the active +// crash recovery context, and install signal handlers to invoke HandleCrash on +// the active object. +// +// This implementation does not to attempt to chain signal handlers in any +// reliable fashion -- if we get a signal outside of a crash recovery context we +// simply disable crash recovery and raise the signal again. + +#include + +static struct { + int Signal; + struct sigaction PrevAction; +} SignalInfo[] = { + { SIGABRT, {} }, + { SIGBUS, {} }, + { SIGFPE, {} }, + { SIGILL, {} }, + { SIGSEGV, {} }, + { SIGTRAP, {} }, +}; +static const unsigned NumSignals = sizeof(SignalInfo) / sizeof(SignalInfo[0]); + +static void CrashRecoverySignalHandler(int Signal) { + // Lookup the current thread local recovery object. + const CrashRecoveryContextImpl *CRCI = CurrentContext.get(); + + if (!CRCI) { + // We didn't find a crash recovery context -- this means either we got a + // signal on a thread we didn't expect it on, the application got a signal + // outside of a crash recovery context, or something else went horribly + // wrong. + // + // Disable crash recovery and raise the signal again. The assumption here is + // that the enclosing application will terminate soon, and we won't want to + // attempt crash recovery again. + // + // This call of Disable isn't thread safe, but it doesn't actually matter. + CrashRecoveryContext::Disable(); + raise(Signal); + } + + // Unblock the signal we received. + sigset_t SigMask; + sigemptyset(&SigMask); + sigaddset(&SigMask, Signal); + sigprocmask(SIG_UNBLOCK, &SigMask, 0); + + if (CRCI) + const_cast(CRCI)->HandleCrash(); +} + +void CrashRecoveryContext::Enable() { + if (gCrashRecoveryEnabled) + return; + + gCrashRecoveryEnabled = true; + + // Setup the signal handler. + struct sigaction Handler; + Handler.sa_handler = CrashRecoverySignalHandler; + Handler.sa_flags = 0; + sigemptyset(&Handler.sa_mask); + + for (unsigned i = 0; i != NumSignals; ++i) { + sigaction(SignalInfo[i].Signal, &Handler, + &SignalInfo[i].PrevAction); + } +} + +void CrashRecoveryContext::Disable() { + if (!gCrashRecoveryEnabled) + return; + + gCrashRecoveryEnabled = false; + + // Restore the previous signal handlers. + for (unsigned i = 0; i != NumSignals; ++i) + sigaction(SignalInfo[i].Signal, &SignalInfo[i].PrevAction, 0); +} + +#endif + bool CrashRecoveryContext::RunSafely(void (*Fn)(void*), void *UserData) { // If crash recovery is disabled, do nothing. if (gCrashRecoveryEnabled) {