Home | History | Annotate | Download | only in Support
      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/ErrorHandling.h"
     14 #include "llvm/Support/Mutex.h"
     15 #include "llvm/Support/ThreadLocal.h"
     16 #include <cstdio>
     17 #include <setjmp.h>
     18 using namespace llvm;
     19 
     20 namespace {
     21 
     22 struct CrashRecoveryContextImpl;
     23 
     24 static sys::ThreadLocal<const CrashRecoveryContextImpl> CurrentContext;
     25 
     26 struct CrashRecoveryContextImpl {
     27   CrashRecoveryContext *CRC;
     28   std::string Backtrace;
     29   ::jmp_buf JumpBuffer;
     30   volatile unsigned Failed : 1;
     31 
     32 public:
     33   CrashRecoveryContextImpl(CrashRecoveryContext *CRC) : CRC(CRC),
     34                                                         Failed(false) {
     35     CurrentContext.set(this);
     36   }
     37   ~CrashRecoveryContextImpl() {
     38     CurrentContext.erase();
     39   }
     40 
     41   void HandleCrash() {
     42     // Eliminate the current context entry, to avoid re-entering in case the
     43     // cleanup code crashes.
     44     CurrentContext.erase();
     45 
     46     assert(!Failed && "Crash recovery context already failed!");
     47     Failed = true;
     48 
     49     // FIXME: Stash the backtrace.
     50 
     51     // Jump back to the RunSafely we were called under.
     52     longjmp(JumpBuffer, 1);
     53   }
     54 };
     55 
     56 }
     57 
     58 static sys::Mutex gCrashRecoveryContexMutex;
     59 static bool gCrashRecoveryEnabled = false;
     60 
     61 static sys::ThreadLocal<const CrashRecoveryContextCleanup>
     62        tlIsRecoveringFromCrash;
     63 
     64 CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() {}
     65 
     66 CrashRecoveryContext::~CrashRecoveryContext() {
     67   // Reclaim registered resources.
     68   CrashRecoveryContextCleanup *i = head;
     69   tlIsRecoveringFromCrash.set(head);
     70   while (i) {
     71     CrashRecoveryContextCleanup *tmp = i;
     72     i = tmp->next;
     73     tmp->cleanupFired = true;
     74     tmp->recoverResources();
     75     delete tmp;
     76   }
     77   tlIsRecoveringFromCrash.erase();
     78 
     79   CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
     80   delete CRCI;
     81 }
     82 
     83 bool CrashRecoveryContext::isRecoveringFromCrash() {
     84   return tlIsRecoveringFromCrash.get() != 0;
     85 }
     86 
     87 CrashRecoveryContext *CrashRecoveryContext::GetCurrent() {
     88   if (!gCrashRecoveryEnabled)
     89     return 0;
     90 
     91   const CrashRecoveryContextImpl *CRCI = CurrentContext.get();
     92   if (!CRCI)
     93     return 0;
     94 
     95   return CRCI->CRC;
     96 }
     97 
     98 void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup)
     99 {
    100   if (!cleanup)
    101     return;
    102   if (head)
    103     head->prev = cleanup;
    104   cleanup->next = head;
    105   head = cleanup;
    106 }
    107 
    108 void
    109 CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) {
    110   if (!cleanup)
    111     return;
    112   if (cleanup == head) {
    113     head = cleanup->next;
    114     if (head)
    115       head->prev = 0;
    116   }
    117   else {
    118     cleanup->prev->next = cleanup->next;
    119     if (cleanup->next)
    120       cleanup->next->prev = cleanup->prev;
    121   }
    122   delete cleanup;
    123 }
    124 
    125 #ifdef LLVM_ON_WIN32
    126 
    127 #include "Windows/Windows.h"
    128 
    129 // On Windows, we can make use of vectored exception handling to
    130 // catch most crashing situations.  Note that this does mean
    131 // we will be alerted of exceptions *before* structured exception
    132 // handling has the opportunity to catch it.  But that isn't likely
    133 // to cause problems because nowhere in the project is SEH being
    134 // used.
    135 //
    136 // Vectored exception handling is built on top of SEH, and so it
    137 // works on a per-thread basis.
    138 //
    139 // The vectored exception handler functionality was added in Windows
    140 // XP, so if support for older versions of Windows is required,
    141 // it will have to be added.
    142 //
    143 // If we want to support as far back as Win2k, we could use the
    144 // SetUnhandledExceptionFilter API, but there's a risk of that
    145 // being entirely overwritten (it's not a chain).
    146 
    147 static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
    148 {
    149   // Lookup the current thread local recovery object.
    150   const CrashRecoveryContextImpl *CRCI = CurrentContext.get();
    151 
    152   if (!CRCI) {
    153     // Something has gone horribly wrong, so let's just tell everyone
    154     // to keep searching
    155     CrashRecoveryContext::Disable();
    156     return EXCEPTION_CONTINUE_SEARCH;
    157   }
    158 
    159   // TODO: We can capture the stack backtrace here and store it on the
    160   // implementation if we so choose.
    161 
    162   // Handle the crash
    163   const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash();
    164 
    165   // Note that we don't actually get here because HandleCrash calls
    166   // longjmp, which means the HandleCrash function never returns.
    167   llvm_unreachable("Handled the crash, should have longjmp'ed out of here");
    168 }
    169 
    170 // Because the Enable and Disable calls are static, it means that
    171 // there may not actually be an Impl available, or even a current
    172 // CrashRecoveryContext at all.  So we make use of a thread-local
    173 // exception table.  The handles contained in here will either be
    174 // non-NULL, valid VEH handles, or NULL.
    175 static sys::ThreadLocal<const void> sCurrentExceptionHandle;
    176 
    177 void CrashRecoveryContext::Enable() {
    178   sys::ScopedLock L(gCrashRecoveryContexMutex);
    179 
    180   if (gCrashRecoveryEnabled)
    181     return;
    182 
    183   gCrashRecoveryEnabled = true;
    184 
    185   // We can set up vectored exception handling now.  We will install our
    186   // handler as the front of the list, though there's no assurances that
    187   // it will remain at the front (another call could install itself before
    188   // our handler).  This 1) isn't likely, and 2) shouldn't cause problems.
    189   PVOID handle = ::AddVectoredExceptionHandler(1, ExceptionHandler);
    190   sCurrentExceptionHandle.set(handle);
    191 }
    192 
    193 void CrashRecoveryContext::Disable() {
    194   sys::ScopedLock L(gCrashRecoveryContexMutex);
    195 
    196   if (!gCrashRecoveryEnabled)
    197     return;
    198 
    199   gCrashRecoveryEnabled = false;
    200 
    201   PVOID currentHandle = const_cast<PVOID>(sCurrentExceptionHandle.get());
    202   if (currentHandle) {
    203     // Now we can remove the vectored exception handler from the chain
    204     ::RemoveVectoredExceptionHandler(currentHandle);
    205 
    206     // Reset the handle in our thread-local set.
    207     sCurrentExceptionHandle.set(NULL);
    208   }
    209 }
    210 
    211 #else
    212 
    213 // Generic POSIX implementation.
    214 //
    215 // This implementation relies on synchronous signals being delivered to the
    216 // current thread. We use a thread local object to keep track of the active
    217 // crash recovery context, and install signal handlers to invoke HandleCrash on
    218 // the active object.
    219 //
    220 // This implementation does not to attempt to chain signal handlers in any
    221 // reliable fashion -- if we get a signal outside of a crash recovery context we
    222 // simply disable crash recovery and raise the signal again.
    223 
    224 #include <signal.h>
    225 
    226 static const int Signals[] = { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP };
    227 static const unsigned NumSignals = sizeof(Signals) / sizeof(Signals[0]);
    228 static struct sigaction PrevActions[NumSignals];
    229 
    230 static void CrashRecoverySignalHandler(int Signal) {
    231   // Lookup the current thread local recovery object.
    232   const CrashRecoveryContextImpl *CRCI = CurrentContext.get();
    233 
    234   if (!CRCI) {
    235     // We didn't find a crash recovery context -- this means either we got a
    236     // signal on a thread we didn't expect it on, the application got a signal
    237     // outside of a crash recovery context, or something else went horribly
    238     // wrong.
    239     //
    240     // Disable crash recovery and raise the signal again. The assumption here is
    241     // that the enclosing application will terminate soon, and we won't want to
    242     // attempt crash recovery again.
    243     //
    244     // This call of Disable isn't thread safe, but it doesn't actually matter.
    245     CrashRecoveryContext::Disable();
    246     raise(Signal);
    247 
    248     // The signal will be thrown once the signal mask is restored.
    249     return;
    250   }
    251 
    252   // Unblock the signal we received.
    253   sigset_t SigMask;
    254   sigemptyset(&SigMask);
    255   sigaddset(&SigMask, Signal);
    256   sigprocmask(SIG_UNBLOCK, &SigMask, 0);
    257 
    258   if (CRCI)
    259     const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash();
    260 }
    261 
    262 void CrashRecoveryContext::Enable() {
    263   sys::ScopedLock L(gCrashRecoveryContexMutex);
    264 
    265   if (gCrashRecoveryEnabled)
    266     return;
    267 
    268   gCrashRecoveryEnabled = true;
    269 
    270   // Setup the signal handler.
    271   struct sigaction Handler;
    272   Handler.sa_handler = CrashRecoverySignalHandler;
    273   Handler.sa_flags = 0;
    274   sigemptyset(&Handler.sa_mask);
    275 
    276   for (unsigned i = 0; i != NumSignals; ++i) {
    277     sigaction(Signals[i], &Handler, &PrevActions[i]);
    278   }
    279 }
    280 
    281 void CrashRecoveryContext::Disable() {
    282   sys::ScopedLock L(gCrashRecoveryContexMutex);
    283 
    284   if (!gCrashRecoveryEnabled)
    285     return;
    286 
    287   gCrashRecoveryEnabled = false;
    288 
    289   // Restore the previous signal handlers.
    290   for (unsigned i = 0; i != NumSignals; ++i)
    291     sigaction(Signals[i], &PrevActions[i], 0);
    292 }
    293 
    294 #endif
    295 
    296 bool CrashRecoveryContext::RunSafely(void (*Fn)(void*), void *UserData) {
    297   // If crash recovery is disabled, do nothing.
    298   if (gCrashRecoveryEnabled) {
    299     assert(!Impl && "Crash recovery context already initialized!");
    300     CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this);
    301     Impl = CRCI;
    302 
    303     if (setjmp(CRCI->JumpBuffer) != 0) {
    304       return false;
    305     }
    306   }
    307 
    308   Fn(UserData);
    309   return true;
    310 }
    311 
    312 void CrashRecoveryContext::HandleCrash() {
    313   CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
    314   assert(CRCI && "Crash recovery context never initialized!");
    315   CRCI->HandleCrash();
    316 }
    317 
    318 const std::string &CrashRecoveryContext::getBacktrace() const {
    319   CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *) Impl;
    320   assert(CRC && "Crash recovery context never initialized!");
    321   assert(CRC->Failed && "No crash was detected!");
    322   return CRC->Backtrace;
    323 }
    324 
    325 //
    326 
    327 namespace {
    328 struct RunSafelyOnThreadInfo {
    329   void (*UserFn)(void*);
    330   void *UserData;
    331   CrashRecoveryContext *CRC;
    332   bool Result;
    333 };
    334 }
    335 
    336 static void RunSafelyOnThread_Dispatch(void *UserData) {
    337   RunSafelyOnThreadInfo *Info =
    338     reinterpret_cast<RunSafelyOnThreadInfo*>(UserData);
    339   Info->Result = Info->CRC->RunSafely(Info->UserFn, Info->UserData);
    340 }
    341 bool CrashRecoveryContext::RunSafelyOnThread(void (*Fn)(void*), void *UserData,
    342                                              unsigned RequestedStackSize) {
    343   RunSafelyOnThreadInfo Info = { Fn, UserData, this, false };
    344   llvm_execute_on_thread(RunSafelyOnThread_Dispatch, &Info, RequestedStackSize);
    345   return Info.Result;
    346 }
    347