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