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