X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=lib%2FSupport%2FCrashRecoveryContext.cpp;h=3f4ef9da48f17c8995523b31fdae8834164ad6c5;hb=62babebeeb57f8110c5b7233cb56dff38b10e840;hp=e558662611f66e17023fd0146746b3aced413b4b;hpb=1a06d5721acb9a2b69217fc8872ed5b14a482104;p=oota-llvm.git diff --git a/lib/Support/CrashRecoveryContext.cpp b/lib/Support/CrashRecoveryContext.cpp index e558662611f..3f4ef9da48f 100644 --- a/lib/Support/CrashRecoveryContext.cpp +++ b/lib/Support/CrashRecoveryContext.cpp @@ -8,39 +8,58 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/CrashRecoveryContext.h" -#include "llvm/ADT/SmallString.h" #include "llvm/Config/config.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Mutex.h" #include "llvm/Support/ThreadLocal.h" #include -#include using namespace llvm; namespace { struct CrashRecoveryContextImpl; -static sys::ThreadLocal CurrentContext; +static ManagedStatic< + sys::ThreadLocal > CurrentContext; struct CrashRecoveryContextImpl { + // When threads are disabled, this links up all active + // CrashRecoveryContextImpls. When threads are enabled there's one thread + // per CrashRecoveryContext and CurrentContext is a thread-local, so only one + // CrashRecoveryContextImpl is active per thread and this is always null. + const CrashRecoveryContextImpl *Next; + CrashRecoveryContext *CRC; std::string Backtrace; ::jmp_buf JumpBuffer; volatile unsigned Failed : 1; + unsigned SwitchedThread : 1; public: CrashRecoveryContextImpl(CrashRecoveryContext *CRC) : CRC(CRC), - Failed(false) { - CurrentContext.set(this); + Failed(false), + SwitchedThread(false) { + Next = CurrentContext->get(); + CurrentContext->set(this); } ~CrashRecoveryContextImpl() { - CurrentContext.erase(); + if (!SwitchedThread) + CurrentContext->set(Next); + } + + /// \brief Called when the separate crash-recovery thread was finished, to + /// indicate that we don't need to clear the thread-local CurrentContext. + void setSwitchedThread() { +#if defined(LLVM_ENABLE_THREADS) && LLVM_ENABLE_THREADS != 0 + SwitchedThread = true; +#endif } void HandleCrash() { // Eliminate the current context entry, to avoid re-entering in case the // cleanup code crashes. - CurrentContext.erase(); + CurrentContext->set(Next); assert(!Failed && "Crash recovery context already failed!"); Failed = true; @@ -54,14 +73,19 @@ public: } -static sys::Mutex gCrashRecoveryContexMutex; +static ManagedStatic gCrashRecoveryContextMutex; static bool gCrashRecoveryEnabled = false; +static ManagedStatic> + tlIsRecoveringFromCrash; + CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() {} CrashRecoveryContext::~CrashRecoveryContext() { // Reclaim registered resources. CrashRecoveryContextCleanup *i = head; + const CrashRecoveryContext *PC = tlIsRecoveringFromCrash->get(); + tlIsRecoveringFromCrash->set(this); while (i) { CrashRecoveryContextCleanup *tmp = i; i = tmp->next; @@ -69,18 +93,23 @@ CrashRecoveryContext::~CrashRecoveryContext() { tmp->recoverResources(); delete tmp; } + tlIsRecoveringFromCrash->set(PC); CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl; delete CRCI; } +bool CrashRecoveryContext::isRecoveringFromCrash() { + return tlIsRecoveringFromCrash->get() != nullptr; +} + CrashRecoveryContext *CrashRecoveryContext::GetCurrent() { if (!gCrashRecoveryEnabled) - return 0; + return nullptr; - const CrashRecoveryContextImpl *CRCI = CurrentContext.get(); + const CrashRecoveryContextImpl *CRCI = CurrentContext->get(); if (!CRCI) - return 0; + return nullptr; return CRCI->CRC; } @@ -102,7 +131,7 @@ CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) { if (cleanup == head) { head = cleanup->next; if (head) - head->prev = 0; + head->prev = nullptr; } else { cleanup->prev->next = cleanup->next; @@ -114,24 +143,88 @@ CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) { #ifdef LLVM_ON_WIN32 -// FIXME: No real Win32 implementation currently. +#include "Windows/WindowsSupport.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"); +} + +// 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); + sys::ScopedLock L(*gCrashRecoveryContextMutex); 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); + sys::ScopedLock L(*gCrashRecoveryContextMutex); 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 @@ -149,13 +242,14 @@ void CrashRecoveryContext::Disable() { #include -static int Signals[] = { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP }; -static const unsigned NumSignals = sizeof(Signals) / sizeof(Signals[0]); +static const int Signals[] = + { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP }; +static const unsigned NumSignals = array_lengthof(Signals); static struct sigaction PrevActions[NumSignals]; static void CrashRecoverySignalHandler(int Signal) { // Lookup the current thread local recovery object. - const CrashRecoveryContextImpl *CRCI = CurrentContext.get(); + const CrashRecoveryContextImpl *CRCI = CurrentContext->get(); if (!CRCI) { // We didn't find a crash recovery context -- this means either we got a @@ -179,14 +273,14 @@ static void CrashRecoverySignalHandler(int Signal) { sigset_t SigMask; sigemptyset(&SigMask); sigaddset(&SigMask, Signal); - sigprocmask(SIG_UNBLOCK, &SigMask, 0); + sigprocmask(SIG_UNBLOCK, &SigMask, nullptr); if (CRCI) const_cast(CRCI)->HandleCrash(); } void CrashRecoveryContext::Enable() { - sys::ScopedLock L(gCrashRecoveryContexMutex); + sys::ScopedLock L(*gCrashRecoveryContextMutex); if (gCrashRecoveryEnabled) return; @@ -205,7 +299,7 @@ void CrashRecoveryContext::Enable() { } void CrashRecoveryContext::Disable() { - sys::ScopedLock L(gCrashRecoveryContexMutex); + sys::ScopedLock L(*gCrashRecoveryContextMutex); if (!gCrashRecoveryEnabled) return; @@ -214,12 +308,12 @@ void CrashRecoveryContext::Disable() { // Restore the previous signal handlers. for (unsigned i = 0; i != NumSignals; ++i) - sigaction(Signals[i], &PrevActions[i], 0); + sigaction(Signals[i], &PrevActions[i], nullptr); } #endif -bool CrashRecoveryContext::RunSafely(void (*Fn)(void*), void *UserData) { +bool CrashRecoveryContext::RunSafely(function_ref Fn) { // If crash recovery is disabled, do nothing. if (gCrashRecoveryEnabled) { assert(!Impl && "Crash recovery context already initialized!"); @@ -231,7 +325,7 @@ bool CrashRecoveryContext::RunSafely(void (*Fn)(void*), void *UserData) { } } - Fn(UserData); + Fn(); return true; } @@ -248,13 +342,26 @@ const std::string &CrashRecoveryContext::getBacktrace() const { return CRC->Backtrace; } -// +// FIXME: Portability. +static void setThreadBackgroundPriority() { +#ifdef __APPLE__ + setpriority(PRIO_DARWIN_THREAD, 0, PRIO_DARWIN_BG); +#endif +} + +static bool hasThreadBackgroundPriority() { +#ifdef __APPLE__ + return getpriority(PRIO_DARWIN_THREAD, 0) == 1; +#else + return false; +#endif +} namespace { struct RunSafelyOnThreadInfo { - void (*UserFn)(void*); - void *UserData; + function_ref Fn; CrashRecoveryContext *CRC; + bool UseBackgroundPriority; bool Result; }; } @@ -262,11 +369,18 @@ struct RunSafelyOnThreadInfo { static void RunSafelyOnThread_Dispatch(void *UserData) { RunSafelyOnThreadInfo *Info = reinterpret_cast(UserData); - Info->Result = Info->CRC->RunSafely(Info->UserFn, Info->UserData); + + if (Info->UseBackgroundPriority) + setThreadBackgroundPriority(); + + Info->Result = Info->CRC->RunSafely(Info->Fn); } -bool CrashRecoveryContext::RunSafelyOnThread(void (*Fn)(void*), void *UserData, +bool CrashRecoveryContext::RunSafelyOnThread(function_ref Fn, unsigned RequestedStackSize) { - RunSafelyOnThreadInfo Info = { Fn, UserData, this, false }; + bool UseBackgroundPriority = hasThreadBackgroundPriority(); + RunSafelyOnThreadInfo Info = { Fn, this, UseBackgroundPriority, false }; llvm_execute_on_thread(RunSafelyOnThread_Dispatch, &Info, RequestedStackSize); + if (CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *)Impl) + CRC->setSwitchedThread(); return Info.Result; }