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/Mutex.h"
     14 #include "llvm/Support/ThreadLocal.h"
     15 #include "llvm/Support/ErrorHandling.h"
     16 #include <setjmp.h>
     17 #include <cstdio>
     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   return EXCEPTION_CONTINUE_SEARCH;
    169 }
    170 
    171 // Because the Enable and Disable calls are static, it means that
    172 // there may not actually be an Impl available, or even a current
    173 // CrashRecoveryContext at all.  So we make use of a thread-local
    174 // exception table.  The handles contained in here will either be
    175 // non-NULL, valid VEH handles, or NULL.
    176 static sys::ThreadLocal<const void> sCurrentExceptionHandle;
    177 
    178 void CrashRecoveryContext::Enable() {
    179   sys::ScopedLock L(gCrashRecoveryContexMutex);
    180 
    181   if (gCrashRecoveryEnabled)
    182     return;
    183 
    184   gCrashRecoveryEnabled = true;
    185 
    186   // We can set up vectored exception handling now.  We will install our
    187   // handler as the front of the list, though there's no assurances that
    188   // it will remain at the front (another call could install itself before
    189   // our handler).  This 1) isn't likely, and 2) shouldn't cause problems.
    190   PVOID handle = ::AddVectoredExceptionHandler(1, ExceptionHandler);
    191   sCurrentExceptionHandle.set(handle);
    192 }
    193 
    194 void CrashRecoveryContext::Disable() {
    195   sys::ScopedLock L(gCrashRecoveryContexMutex);
    196 
    197   if (!gCrashRecoveryEnabled)
    198     return;
    199 
    200   gCrashRecoveryEnabled = false;
    201 
    202   PVOID currentHandle = const_cast<PVOID>(sCurrentExceptionHandle.get());
    203   if (currentHandle) {
    204     // Now we can remove the vectored exception handler from the chain
    205     ::RemoveVectoredExceptionHandler(currentHandle);
    206 
    207     // Reset the handle in our thread-local set.
    208     sCurrentExceptionHandle.set(NULL);
    209   }
    210 }
    211 
    212 #else
    213 
    214 // Generic POSIX implementation.
    215 //
    216 // This implementation relies on synchronous signals being delivered to the
    217 // current thread. We use a thread local object to keep track of the active
    218 // crash recovery context, and install signal handlers to invoke HandleCrash on
    219 // the active object.
    220 //
    221 // This implementation does not to attempt to chain signal handlers in any
    222 // reliable fashion -- if we get a signal outside of a crash recovery context we
    223 // simply disable crash recovery and raise the signal again.
    224 
    225 #include <signal.h>
    226 
    227 static int Signals[] = { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP };
    228 static const unsigned NumSignals = sizeof(Signals) / sizeof(Signals[0]);
    229 static struct sigaction PrevActions[NumSignals];
    230 
    231 static void CrashRecoverySignalHandler(int Signal) {
    232   // Lookup the current thread local recovery object.
    233   const CrashRecoveryContextImpl *CRCI = CurrentContext.get();
    234 
    235   if (!CRCI) {
    236     // We didn't find a crash recovery context -- this means either we got a
    237     // signal on a thread we didn't expect it on, the application got a signal
    238     // outside of a crash recovery context, or something else went horribly
    239     // wrong.
    240     //
    241     // Disable crash recovery and raise the signal again. The assumption here is
    242     // that the enclosing application will terminate soon, and we won't want to
    243     // attempt crash recovery again.
    244     //
    245     // This call of Disable isn't thread safe, but it doesn't actually matter.
    246     CrashRecoveryContext::Disable();
    247     raise(Signal);
    248 
    249     // The signal will be thrown once the signal mask is restored.
    250     return;
    251   }
    252 
    253   // Unblock the signal we received.
    254   sigset_t SigMask;
    255   sigemptyset(&SigMask);
    256   sigaddset(&SigMask, Signal);
    257   sigprocmask(SIG_UNBLOCK, &SigMask, 0);
    258 
    259   if (CRCI)
    260     const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash();
    261 }
    262 
    263 void CrashRecoveryContext::Enable() {
    264   sys::ScopedLock L(gCrashRecoveryContexMutex);
    265 
    266   if (gCrashRecoveryEnabled)
    267     return;
    268 
    269   gCrashRecoveryEnabled = true;
    270 
    271 #ifdef ENABLE_SIGNAL_OVERRIDES
    272   // Setup the signal handler.
    273   struct sigaction Handler;
    274   Handler.sa_handler = CrashRecoverySignalHandler;
    275   Handler.sa_flags = 0;
    276   sigemptyset(&Handler.sa_mask);
    277 
    278   for (unsigned i = 0; i != NumSignals; ++i) {
    279     sigaction(Signals[i], &Handler, &PrevActions[i]);
    280   }
    281 #endif
    282 }
    283 
    284 void CrashRecoveryContext::Disable() {
    285   sys::ScopedLock L(gCrashRecoveryContexMutex);
    286 
    287   if (!gCrashRecoveryEnabled)
    288     return;
    289 
    290   gCrashRecoveryEnabled = false;
    291 
    292 #ifdef ENABLE_SIGNAL_OVERRIDES
    293   // Restore the previous signal handlers.
    294   for (unsigned i = 0; i != NumSignals; ++i)
    295     sigaction(Signals[i], &PrevActions[i], 0);
    296 #endif
    297 }
    298 
    299 #endif
    300 
    301 bool CrashRecoveryContext::RunSafely(void (*Fn)(void*), void *UserData) {
    302   // If crash recovery is disabled, do nothing.
    303   if (gCrashRecoveryEnabled) {
    304     assert(!Impl && "Crash recovery context already initialized!");
    305     CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this);
    306     Impl = CRCI;
    307 
    308     if (setjmp(CRCI->JumpBuffer) != 0) {
    309       return false;
    310     }
    311   }
    312 
    313   Fn(UserData);
    314   return true;
    315 }
    316 
    317 void CrashRecoveryContext::HandleCrash() {
    318   CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
    319   assert(CRCI && "Crash recovery context never initialized!");
    320   CRCI->HandleCrash();
    321 }
    322 
    323 const std::string &CrashRecoveryContext::getBacktrace() const {
    324   CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *) Impl;
    325   assert(CRC && "Crash recovery context never initialized!");
    326   assert(CRC->Failed && "No crash was detected!");
    327   return CRC->Backtrace;
    328 }
    329 
    330 //
    331 
    332 namespace {
    333 struct RunSafelyOnThreadInfo {
    334   void (*UserFn)(void*);
    335   void *UserData;
    336   CrashRecoveryContext *CRC;
    337   bool Result;
    338 };
    339 }
    340 
    341 static void RunSafelyOnThread_Dispatch(void *UserData) {
    342   RunSafelyOnThreadInfo *Info =
    343     reinterpret_cast<RunSafelyOnThreadInfo*>(UserData);
    344   Info->Result = Info->CRC->RunSafely(Info->UserFn, Info->UserData);
    345 }
    346 bool CrashRecoveryContext::RunSafelyOnThread(void (*Fn)(void*), void *UserData,
    347                                              unsigned RequestedStackSize) {
    348   RunSafelyOnThreadInfo Info = { Fn, UserData, this, false };
    349   llvm_execute_on_thread(RunSafelyOnThread_Dispatch, &Info, RequestedStackSize);
    350   return Info.Result;
    351 }
    352