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