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