X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=lib%2FSupport%2FCrashRecoveryContext.cpp;h=263114c06f986a3e3594aeec9a805551d6e8c434;hb=71fccadbed49b555d9582198ee58301494109410;hp=c079182bd1c51b8d8e4812b7b501294c80ce9403;hpb=d9082dfd9ab442dd33f552693fcc0f396a514bb6;p=oota-llvm.git diff --git a/lib/Support/CrashRecoveryContext.cpp b/lib/Support/CrashRecoveryContext.cpp index c079182bd1c..263114c06f9 100644 --- a/lib/Support/CrashRecoveryContext.cpp +++ b/lib/Support/CrashRecoveryContext.cpp @@ -9,7 +9,10 @@ #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/ADT/SmallString.h" -#include "llvm/System/ThreadLocal.h" +#include "llvm/Config/config.h" +#include "llvm/Support/Mutex.h" +#include "llvm/Support/ThreadLocal.h" +#include "llvm/Support/ErrorHandling.h" #include #include using namespace llvm; @@ -21,19 +24,25 @@ struct CrashRecoveryContextImpl; static sys::ThreadLocal CurrentContext; struct CrashRecoveryContextImpl { + CrashRecoveryContext *CRC; std::string Backtrace; ::jmp_buf JumpBuffer; volatile unsigned Failed : 1; public: - CrashRecoveryContextImpl() : Failed(false) { + CrashRecoveryContextImpl(CrashRecoveryContext *CRC) : CRC(CRC), + Failed(false) { CurrentContext.set(this); } ~CrashRecoveryContextImpl() { - CurrentContext.set(0); + 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; @@ -46,29 +55,158 @@ public: } +static sys::Mutex gCrashRecoveryContexMutex; static bool gCrashRecoveryEnabled = false; +static sys::ThreadLocal + tlIsRecoveringFromCrash; + +CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() {} + CrashRecoveryContext::~CrashRecoveryContext() { + // Reclaim registered resources. + CrashRecoveryContextCleanup *i = head; + tlIsRecoveringFromCrash.set(head); + while (i) { + CrashRecoveryContextCleanup *tmp = i; + i = tmp->next; + tmp->cleanupFired = true; + tmp->recoverResources(); + delete tmp; + } + tlIsRecoveringFromCrash.erase(); + CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl; delete CRCI; } +bool CrashRecoveryContext::isRecoveringFromCrash() { + return tlIsRecoveringFromCrash.get() != 0; +} + +CrashRecoveryContext *CrashRecoveryContext::GetCurrent() { + if (!gCrashRecoveryEnabled) + return 0; + + const CrashRecoveryContextImpl *CRCI = CurrentContext.get(); + if (!CRCI) + return 0; + + return CRCI->CRC; +} + +void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup) +{ + if (!cleanup) + return; + if (head) + head->prev = cleanup; + cleanup->next = head; + head = cleanup; +} + +void +CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) { + if (!cleanup) + return; + if (cleanup == head) { + head = cleanup->next; + if (head) + head->prev = 0; + } + else { + cleanup->prev->next = cleanup->next; + if (cleanup->next) + cleanup->next->prev = cleanup->prev; + } + delete cleanup; +} + #ifdef LLVM_ON_WIN32 -// FIXME: No real Win32 implementation currently. +#include "Windows/Windows.h" + +// On Windows, we can make use of vectored exception handling to +// catch most crashing situations. Note that this does mean +// we will be alerted of exceptions *before* structured exception +// handling has the opportunity to catch it. But that isn't likely +// to cause problems because nowhere in the project is SEH being +// used. +// +// Vectored exception handling is built on top of SEH, and so it +// works on a per-thread basis. +// +// The vectored exception handler functionality was added in Windows +// XP, so if support for older versions of Windows is required, +// it will have to be added. +// +// If we want to support as far back as Win2k, we could use the +// SetUnhandledExceptionFilter API, but there's a risk of that +// being entirely overwritten (it's not a chain). + +static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) +{ + // Lookup the current thread local recovery object. + const CrashRecoveryContextImpl *CRCI = CurrentContext.get(); + + if (!CRCI) { + // Something has gone horribly wrong, so let's just tell everyone + // to keep searching + CrashRecoveryContext::Disable(); + return EXCEPTION_CONTINUE_SEARCH; + } + + // TODO: We can capture the stack backtrace here and store it on the + // implementation if we so choose. + + // Handle the crash + const_cast(CRCI)->HandleCrash(); + + // Note that we don't actually get here because HandleCrash calls + // longjmp, which means the HandleCrash function never returns. + llvm_unreachable("Handled the crash, should have longjmp'ed out of here"); + return EXCEPTION_CONTINUE_SEARCH; +} + +// Because the Enable and Disable calls are static, it means that +// there may not actually be an Impl available, or even a current +// CrashRecoveryContext at all. So we make use of a thread-local +// exception table. The handles contained in here will either be +// non-NULL, valid VEH handles, or NULL. +static sys::ThreadLocal sCurrentExceptionHandle; void CrashRecoveryContext::Enable() { + sys::ScopedLock L(gCrashRecoveryContexMutex); + if (gCrashRecoveryEnabled) return; gCrashRecoveryEnabled = true; + + // We can set up vectored exception handling now. We will install our + // handler as the front of the list, though there's no assurances that + // it will remain at the front (another call could install itself before + // our handler). This 1) isn't likely, and 2) shouldn't cause problems. + PVOID handle = ::AddVectoredExceptionHandler(1, ExceptionHandler); + sCurrentExceptionHandle.set(handle); } void CrashRecoveryContext::Disable() { + sys::ScopedLock L(gCrashRecoveryContexMutex); + if (!gCrashRecoveryEnabled) return; gCrashRecoveryEnabled = false; + + PVOID currentHandle = const_cast(sCurrentExceptionHandle.get()); + if (currentHandle) { + // Now we can remove the vectored exception handler from the chain + ::RemoveVectoredExceptionHandler(currentHandle); + + // Reset the handle in our thread-local set. + sCurrentExceptionHandle.set(NULL); + } } #else @@ -86,18 +224,9 @@ void CrashRecoveryContext::Disable() { #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 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. @@ -116,6 +245,9 @@ static void CrashRecoverySignalHandler(int Signal) { // 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. @@ -129,6 +261,8 @@ static void CrashRecoverySignalHandler(int Signal) { } void CrashRecoveryContext::Enable() { + sys::ScopedLock L(gCrashRecoveryContexMutex); + if (gCrashRecoveryEnabled) return; @@ -141,12 +275,13 @@ void CrashRecoveryContext::Enable() { sigemptyset(&Handler.sa_mask); for (unsigned i = 0; i != NumSignals; ++i) { - sigaction(SignalInfo[i].Signal, &Handler, - &SignalInfo[i].PrevAction); + sigaction(Signals[i], &Handler, &PrevActions[i]); } } void CrashRecoveryContext::Disable() { + sys::ScopedLock L(gCrashRecoveryContexMutex); + if (!gCrashRecoveryEnabled) return; @@ -154,7 +289,7 @@ void CrashRecoveryContext::Disable() { // Restore the previous signal handlers. for (unsigned i = 0; i != NumSignals; ++i) - sigaction(SignalInfo[i].Signal, &SignalInfo[i].PrevAction, 0); + sigaction(Signals[i], &PrevActions[i], 0); } #endif @@ -163,7 +298,7 @@ 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; + CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this); Impl = CRCI; if (setjmp(CRCI->JumpBuffer) != 0) { @@ -187,3 +322,26 @@ const std::string &CrashRecoveryContext::getBacktrace() const { 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; +}