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> 17using namespace llvm; 18 19namespace { 20 21struct CrashRecoveryContextImpl; 22 23static ManagedStatic< 24 sys::ThreadLocal<const CrashRecoveryContextImpl> > CurrentContext; 25 26struct 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 39public: 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 76static ManagedStatic<sys::Mutex> gCrashRecoveryContextMutex; 77static bool gCrashRecoveryEnabled = false; 78 79static ManagedStatic<sys::ThreadLocal<const CrashRecoveryContext>> 80 tlIsRecoveringFromCrash; 81 82CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() {} 83 84CrashRecoveryContext::~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 102bool CrashRecoveryContext::isRecoveringFromCrash() { 103 return tlIsRecoveringFromCrash->get() != nullptr; 104} 105 106CrashRecoveryContext *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 117void 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 127void 128CrashRecoveryContext::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 166static 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. 194static sys::ThreadLocal<const void> sCurrentExceptionHandle; 195 196void 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 212void 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 245static const int Signals[] = 246 { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP }; 247static const unsigned NumSignals = array_lengthof(Signals); 248static struct sigaction PrevActions[NumSignals]; 249 250static 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 282void 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 301void 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 316bool 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 332void CrashRecoveryContext::HandleCrash() { 333 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl; 334 assert(CRCI && "Crash recovery context never initialized!"); 335 CRCI->HandleCrash(); 336} 337 338const 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. 346static void setThreadBackgroundPriority() { 347#ifdef __APPLE__ 348 setpriority(PRIO_DARWIN_THREAD, 0, PRIO_DARWIN_BG); 349#endif 350} 351 352static bool hasThreadBackgroundPriority() { 353#ifdef __APPLE__ 354 return getpriority(PRIO_DARWIN_THREAD, 0) == 1; 355#else 356 return false; 357#endif 358} 359 360namespace { 361struct RunSafelyOnThreadInfo { 362 function_ref<void()> Fn; 363 CrashRecoveryContext *CRC; 364 bool UseBackgroundPriority; 365 bool Result; 366}; 367} 368 369static 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} 378bool 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