CrashRecoveryContext.cpp revision 1a06d5721acb9a2b69217fc8872ed5b14a482104
1//===--- CrashRecoveryContext.cpp - Crash Recovery ------------------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9 10#include "llvm/Support/CrashRecoveryContext.h" 11#include "llvm/ADT/SmallString.h" 12#include "llvm/Config/config.h" 13#include "llvm/Support/Mutex.h" 14#include "llvm/Support/ThreadLocal.h" 15#include <setjmp.h> 16#include <cstdio> 17using namespace llvm; 18 19namespace { 20 21struct CrashRecoveryContextImpl; 22 23static sys::ThreadLocal<const CrashRecoveryContextImpl> CurrentContext; 24 25struct CrashRecoveryContextImpl { 26 CrashRecoveryContext *CRC; 27 std::string Backtrace; 28 ::jmp_buf JumpBuffer; 29 volatile unsigned Failed : 1; 30 31public: 32 CrashRecoveryContextImpl(CrashRecoveryContext *CRC) : CRC(CRC), 33 Failed(false) { 34 CurrentContext.set(this); 35 } 36 ~CrashRecoveryContextImpl() { 37 CurrentContext.erase(); 38 } 39 40 void HandleCrash() { 41 // Eliminate the current context entry, to avoid re-entering in case the 42 // cleanup code crashes. 43 CurrentContext.erase(); 44 45 assert(!Failed && "Crash recovery context already failed!"); 46 Failed = true; 47 48 // FIXME: Stash the backtrace. 49 50 // Jump back to the RunSafely we were called under. 51 longjmp(JumpBuffer, 1); 52 } 53}; 54 55} 56 57static sys::Mutex gCrashRecoveryContexMutex; 58static bool gCrashRecoveryEnabled = false; 59 60CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() {} 61 62CrashRecoveryContext::~CrashRecoveryContext() { 63 // Reclaim registered resources. 64 CrashRecoveryContextCleanup *i = head; 65 while (i) { 66 CrashRecoveryContextCleanup *tmp = i; 67 i = tmp->next; 68 tmp->cleanupFired = true; 69 tmp->recoverResources(); 70 delete tmp; 71 } 72 73 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl; 74 delete CRCI; 75} 76 77CrashRecoveryContext *CrashRecoveryContext::GetCurrent() { 78 if (!gCrashRecoveryEnabled) 79 return 0; 80 81 const CrashRecoveryContextImpl *CRCI = CurrentContext.get(); 82 if (!CRCI) 83 return 0; 84 85 return CRCI->CRC; 86} 87 88void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup) 89{ 90 if (!cleanup) 91 return; 92 if (head) 93 head->prev = cleanup; 94 cleanup->next = head; 95 head = cleanup; 96} 97 98void 99CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) { 100 if (!cleanup) 101 return; 102 if (cleanup == head) { 103 head = cleanup->next; 104 if (head) 105 head->prev = 0; 106 } 107 else { 108 cleanup->prev->next = cleanup->next; 109 if (cleanup->next) 110 cleanup->next->prev = cleanup->prev; 111 } 112 delete cleanup; 113} 114 115#ifdef LLVM_ON_WIN32 116 117// FIXME: No real Win32 implementation currently. 118 119void CrashRecoveryContext::Enable() { 120 sys::ScopedLock L(gCrashRecoveryContexMutex); 121 122 if (gCrashRecoveryEnabled) 123 return; 124 125 gCrashRecoveryEnabled = true; 126} 127 128void CrashRecoveryContext::Disable() { 129 sys::ScopedLock L(gCrashRecoveryContexMutex); 130 131 if (!gCrashRecoveryEnabled) 132 return; 133 134 gCrashRecoveryEnabled = false; 135} 136 137#else 138 139// Generic POSIX implementation. 140// 141// This implementation relies on synchronous signals being delivered to the 142// current thread. We use a thread local object to keep track of the active 143// crash recovery context, and install signal handlers to invoke HandleCrash on 144// the active object. 145// 146// This implementation does not to attempt to chain signal handlers in any 147// reliable fashion -- if we get a signal outside of a crash recovery context we 148// simply disable crash recovery and raise the signal again. 149 150#include <signal.h> 151 152static int Signals[] = { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP }; 153static const unsigned NumSignals = sizeof(Signals) / sizeof(Signals[0]); 154static struct sigaction PrevActions[NumSignals]; 155 156static void CrashRecoverySignalHandler(int Signal) { 157 // Lookup the current thread local recovery object. 158 const CrashRecoveryContextImpl *CRCI = CurrentContext.get(); 159 160 if (!CRCI) { 161 // We didn't find a crash recovery context -- this means either we got a 162 // signal on a thread we didn't expect it on, the application got a signal 163 // outside of a crash recovery context, or something else went horribly 164 // wrong. 165 // 166 // Disable crash recovery and raise the signal again. The assumption here is 167 // that the enclosing application will terminate soon, and we won't want to 168 // attempt crash recovery again. 169 // 170 // This call of Disable isn't thread safe, but it doesn't actually matter. 171 CrashRecoveryContext::Disable(); 172 raise(Signal); 173 174 // The signal will be thrown once the signal mask is restored. 175 return; 176 } 177 178 // Unblock the signal we received. 179 sigset_t SigMask; 180 sigemptyset(&SigMask); 181 sigaddset(&SigMask, Signal); 182 sigprocmask(SIG_UNBLOCK, &SigMask, 0); 183 184 if (CRCI) 185 const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash(); 186} 187 188void CrashRecoveryContext::Enable() { 189 sys::ScopedLock L(gCrashRecoveryContexMutex); 190 191 if (gCrashRecoveryEnabled) 192 return; 193 194 gCrashRecoveryEnabled = true; 195 196 // Setup the signal handler. 197 struct sigaction Handler; 198 Handler.sa_handler = CrashRecoverySignalHandler; 199 Handler.sa_flags = 0; 200 sigemptyset(&Handler.sa_mask); 201 202 for (unsigned i = 0; i != NumSignals; ++i) { 203 sigaction(Signals[i], &Handler, &PrevActions[i]); 204 } 205} 206 207void CrashRecoveryContext::Disable() { 208 sys::ScopedLock L(gCrashRecoveryContexMutex); 209 210 if (!gCrashRecoveryEnabled) 211 return; 212 213 gCrashRecoveryEnabled = false; 214 215 // Restore the previous signal handlers. 216 for (unsigned i = 0; i != NumSignals; ++i) 217 sigaction(Signals[i], &PrevActions[i], 0); 218} 219 220#endif 221 222bool CrashRecoveryContext::RunSafely(void (*Fn)(void*), void *UserData) { 223 // If crash recovery is disabled, do nothing. 224 if (gCrashRecoveryEnabled) { 225 assert(!Impl && "Crash recovery context already initialized!"); 226 CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this); 227 Impl = CRCI; 228 229 if (setjmp(CRCI->JumpBuffer) != 0) { 230 return false; 231 } 232 } 233 234 Fn(UserData); 235 return true; 236} 237 238void CrashRecoveryContext::HandleCrash() { 239 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl; 240 assert(CRCI && "Crash recovery context never initialized!"); 241 CRCI->HandleCrash(); 242} 243 244const std::string &CrashRecoveryContext::getBacktrace() const { 245 CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *) Impl; 246 assert(CRC && "Crash recovery context never initialized!"); 247 assert(CRC->Failed && "No crash was detected!"); 248 return CRC->Backtrace; 249} 250 251// 252 253namespace { 254struct RunSafelyOnThreadInfo { 255 void (*UserFn)(void*); 256 void *UserData; 257 CrashRecoveryContext *CRC; 258 bool Result; 259}; 260} 261 262static void RunSafelyOnThread_Dispatch(void *UserData) { 263 RunSafelyOnThreadInfo *Info = 264 reinterpret_cast<RunSafelyOnThreadInfo*>(UserData); 265 Info->Result = Info->CRC->RunSafely(Info->UserFn, Info->UserData); 266} 267bool CrashRecoveryContext::RunSafelyOnThread(void (*Fn)(void*), void *UserData, 268 unsigned RequestedStackSize) { 269 RunSafelyOnThreadInfo Info = { Fn, UserData, this, false }; 270 llvm_execute_on_thread(RunSafelyOnThread_Dispatch, &Info, RequestedStackSize); 271 return Info.Result; 272} 273