//===--- CrashRecoveryContext.cpp - Crash Recovery ------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/ADT/SmallString.h" #include "llvm/Config/config.h" #include "llvm/System/Mutex.h" #include "llvm/System/ThreadLocal.h" #include #include using namespace llvm; namespace { struct CrashRecoveryContextImpl; static sys::ThreadLocal CurrentContext; struct CrashRecoveryContextImpl { CrashRecoveryContext *CRC; std::string Backtrace; ::jmp_buf JumpBuffer; volatile unsigned Failed : 1; public: CrashRecoveryContextImpl(CrashRecoveryContext *CRC) : CRC(CRC), Failed(false) { CurrentContext.set(this); } ~CrashRecoveryContextImpl() { CurrentContext.erase(); } void HandleCrash() { // Eliminate the current context entry, to avoid re-entering in case the // cleanup code crashes. CurrentContext.erase(); assert(!Failed && "Crash recovery context already failed!"); Failed = true; // FIXME: Stash the backtrace. // Jump back to the RunSafely we were called under. longjmp(JumpBuffer, 1); } }; } static sys::Mutex gCrashRecoveryContexMutex; static bool gCrashRecoveryEnabled = false; CrashRecoveryContext::~CrashRecoveryContext() { CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl; delete CRCI; } CrashRecoveryContext *CrashRecoveryContext::GetCurrent() { const CrashRecoveryContextImpl *CRCI = CurrentContext.get(); if (!CRCI) return 0; return CRCI->CRC; } #ifdef LLVM_ON_WIN32 // FIXME: No real Win32 implementation currently. void CrashRecoveryContext::Enable() { sys::ScopedLock L(gCrashRecoveryContexMutex); if (gCrashRecoveryEnabled) return; gCrashRecoveryEnabled = true; } void CrashRecoveryContext::Disable() { sys::ScopedLock L(gCrashRecoveryContexMutex); if (!gCrashRecoveryEnabled) return; 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 int Signals[] = { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP }; static const unsigned NumSignals = sizeof(Signals) / sizeof(Signals[0]); static struct sigaction PrevActions[NumSignals]; 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); // The signal will be thrown once the signal mask is restored. return; } // 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() { sys::ScopedLock L(gCrashRecoveryContexMutex); 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(Signals[i], &Handler, &PrevActions[i]); } } void CrashRecoveryContext::Disable() { sys::ScopedLock L(gCrashRecoveryContexMutex); if (!gCrashRecoveryEnabled) return; gCrashRecoveryEnabled = false; // Restore the previous signal handlers. for (unsigned i = 0; i != NumSignals; ++i) sigaction(Signals[i], &PrevActions[i], 0); } #endif bool CrashRecoveryContext::RunSafely(void (*Fn)(void*), void *UserData) { // If crash recovery is disabled, do nothing. if (gCrashRecoveryEnabled) { assert(!Impl && "Crash recovery context already initialized!"); CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this); Impl = CRCI; if (setjmp(CRCI->JumpBuffer) != 0) { return false; } } Fn(UserData); return true; } void CrashRecoveryContext::HandleCrash() { CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl; assert(CRCI && "Crash recovery context never initialized!"); CRCI->HandleCrash(); } const std::string &CrashRecoveryContext::getBacktrace() const { CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *) Impl; assert(CRC && "Crash recovery context never initialized!"); assert(CRC->Failed && "No crash was detected!"); return CRC->Backtrace; } // namespace { struct RunSafelyOnThreadInfo { void (*UserFn)(void*); void *UserData; CrashRecoveryContext *CRC; bool Result; }; } static void RunSafelyOnThread_Dispatch(void *UserData) { RunSafelyOnThreadInfo *Info = reinterpret_cast(UserData); Info->Result = Info->CRC->RunSafely(Info->UserFn, Info->UserData); } bool CrashRecoveryContext::RunSafelyOnThread(void (*Fn)(void*), void *UserData, unsigned RequestedStackSize) { RunSafelyOnThreadInfo Info = { Fn, UserData, this, false }; llvm_execute_on_thread(RunSafelyOnThread_Dispatch, &Info, RequestedStackSize); return Info.Result; }