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/llvm-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 /// 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 static void installExceptionOrSignalHandlers(); 82 static void uninstallExceptionOrSignalHandlers(); 83 84 CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() {} 85 86 CrashRecoveryContext::~CrashRecoveryContext() { 87 // Reclaim registered resources. 88 CrashRecoveryContextCleanup *i = head; 89 const CrashRecoveryContext *PC = tlIsRecoveringFromCrash->get(); 90 tlIsRecoveringFromCrash->set(this); 91 while (i) { 92 CrashRecoveryContextCleanup *tmp = i; 93 i = tmp->next; 94 tmp->cleanupFired = true; 95 tmp->recoverResources(); 96 delete tmp; 97 } 98 tlIsRecoveringFromCrash->set(PC); 99 100 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl; 101 delete CRCI; 102 } 103 104 bool CrashRecoveryContext::isRecoveringFromCrash() { 105 return tlIsRecoveringFromCrash->get() != nullptr; 106 } 107 108 CrashRecoveryContext *CrashRecoveryContext::GetCurrent() { 109 if (!gCrashRecoveryEnabled) 110 return nullptr; 111 112 const CrashRecoveryContextImpl *CRCI = CurrentContext->get(); 113 if (!CRCI) 114 return nullptr; 115 116 return CRCI->CRC; 117 } 118 119 void CrashRecoveryContext::Enable() { 120 sys::ScopedLock L(*gCrashRecoveryContextMutex); 121 // FIXME: Shouldn't this be a refcount or something? 122 if (gCrashRecoveryEnabled) 123 return; 124 gCrashRecoveryEnabled = true; 125 installExceptionOrSignalHandlers(); 126 } 127 128 void CrashRecoveryContext::Disable() { 129 sys::ScopedLock L(*gCrashRecoveryContextMutex); 130 if (!gCrashRecoveryEnabled) 131 return; 132 gCrashRecoveryEnabled = false; 133 uninstallExceptionOrSignalHandlers(); 134 } 135 136 void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup) 137 { 138 if (!cleanup) 139 return; 140 if (head) 141 head->prev = cleanup; 142 cleanup->next = head; 143 head = cleanup; 144 } 145 146 void 147 CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) { 148 if (!cleanup) 149 return; 150 if (cleanup == head) { 151 head = cleanup->next; 152 if (head) 153 head->prev = nullptr; 154 } 155 else { 156 cleanup->prev->next = cleanup->next; 157 if (cleanup->next) 158 cleanup->next->prev = cleanup->prev; 159 } 160 delete cleanup; 161 } 162 163 #if defined(_MSC_VER) 164 // If _MSC_VER is defined, we must have SEH. Use it if it's available. It's way 165 // better than VEH. Vectored exception handling catches all exceptions happening 166 // on the thread with installed exception handlers, so it can interfere with 167 // internal exception handling of other libraries on that thread. SEH works 168 // exactly as you would expect normal exception handling to work: it only 169 // catches exceptions if they would bubble out from the stack frame with __try / 170 // __except. 171 172 static void installExceptionOrSignalHandlers() {} 173 static void uninstallExceptionOrSignalHandlers() {} 174 175 bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) { 176 if (!gCrashRecoveryEnabled) { 177 Fn(); 178 return true; 179 } 180 181 bool Result = true; 182 __try { 183 Fn(); 184 } __except (1) { // Catch any exception. 185 Result = false; 186 } 187 return Result; 188 } 189 190 #else // !_MSC_VER 191 192 #if defined(_WIN32) 193 // This is a non-MSVC compiler, probably mingw gcc or clang without 194 // -fms-extensions. Use vectored exception handling (VEH). 195 // 196 // On Windows, we can make use of vectored exception handling to catch most 197 // crashing situations. Note that this does mean we will be alerted of 198 // exceptions *before* structured exception handling has the opportunity to 199 // catch it. Unfortunately, this causes problems in practice with other code 200 // running on threads with LLVM crash recovery contexts, so we would like to 201 // eventually move away from VEH. 202 // 203 // Vectored works on a per-thread basis, which is an advantage over 204 // SetUnhandledExceptionFilter. SetUnhandledExceptionFilter also doesn't have 205 // any native support for chaining exception handlers, but VEH allows more than 206 // one. 207 // 208 // The vectored exception handler functionality was added in Windows 209 // XP, so if support for older versions of Windows is required, 210 // it will have to be added. 211 212 #include "Windows/WindowsSupport.h" 213 214 static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) 215 { 216 // DBG_PRINTEXCEPTION_WIDE_C is not properly defined on all supported 217 // compilers and platforms, so we define it manually. 218 constexpr ULONG DbgPrintExceptionWideC = 0x4001000AL; 219 switch (ExceptionInfo->ExceptionRecord->ExceptionCode) 220 { 221 case DBG_PRINTEXCEPTION_C: 222 case DbgPrintExceptionWideC: 223 case 0x406D1388: // set debugger thread name 224 return EXCEPTION_CONTINUE_EXECUTION; 225 } 226 227 // Lookup the current thread local recovery object. 228 const CrashRecoveryContextImpl *CRCI = CurrentContext->get(); 229 230 if (!CRCI) { 231 // Something has gone horribly wrong, so let's just tell everyone 232 // to keep searching 233 CrashRecoveryContext::Disable(); 234 return EXCEPTION_CONTINUE_SEARCH; 235 } 236 237 // TODO: We can capture the stack backtrace here and store it on the 238 // implementation if we so choose. 239 240 // Handle the crash 241 const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash(); 242 243 // Note that we don't actually get here because HandleCrash calls 244 // longjmp, which means the HandleCrash function never returns. 245 llvm_unreachable("Handled the crash, should have longjmp'ed out of here"); 246 } 247 248 // Because the Enable and Disable calls are static, it means that 249 // there may not actually be an Impl available, or even a current 250 // CrashRecoveryContext at all. So we make use of a thread-local 251 // exception table. The handles contained in here will either be 252 // non-NULL, valid VEH handles, or NULL. 253 static sys::ThreadLocal<const void> sCurrentExceptionHandle; 254 255 static void installExceptionOrSignalHandlers() { 256 // We can set up vectored exception handling now. We will install our 257 // handler as the front of the list, though there's no assurances that 258 // it will remain at the front (another call could install itself before 259 // our handler). This 1) isn't likely, and 2) shouldn't cause problems. 260 PVOID handle = ::AddVectoredExceptionHandler(1, ExceptionHandler); 261 sCurrentExceptionHandle.set(handle); 262 } 263 264 static void uninstallExceptionOrSignalHandlers() { 265 PVOID currentHandle = const_cast<PVOID>(sCurrentExceptionHandle.get()); 266 if (currentHandle) { 267 // Now we can remove the vectored exception handler from the chain 268 ::RemoveVectoredExceptionHandler(currentHandle); 269 270 // Reset the handle in our thread-local set. 271 sCurrentExceptionHandle.set(NULL); 272 } 273 } 274 275 #else // !_WIN32 276 277 // Generic POSIX implementation. 278 // 279 // This implementation relies on synchronous signals being delivered to the 280 // current thread. We use a thread local object to keep track of the active 281 // crash recovery context, and install signal handlers to invoke HandleCrash on 282 // the active object. 283 // 284 // This implementation does not to attempt to chain signal handlers in any 285 // reliable fashion -- if we get a signal outside of a crash recovery context we 286 // simply disable crash recovery and raise the signal again. 287 288 #include <signal.h> 289 290 static const int Signals[] = 291 { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP }; 292 static const unsigned NumSignals = array_lengthof(Signals); 293 static struct sigaction PrevActions[NumSignals]; 294 295 static void CrashRecoverySignalHandler(int Signal) { 296 // Lookup the current thread local recovery object. 297 const CrashRecoveryContextImpl *CRCI = CurrentContext->get(); 298 299 if (!CRCI) { 300 // We didn't find a crash recovery context -- this means either we got a 301 // signal on a thread we didn't expect it on, the application got a signal 302 // outside of a crash recovery context, or something else went horribly 303 // wrong. 304 // 305 // Disable crash recovery and raise the signal again. The assumption here is 306 // that the enclosing application will terminate soon, and we won't want to 307 // attempt crash recovery again. 308 // 309 // This call of Disable isn't thread safe, but it doesn't actually matter. 310 CrashRecoveryContext::Disable(); 311 raise(Signal); 312 313 // The signal will be thrown once the signal mask is restored. 314 return; 315 } 316 317 // Unblock the signal we received. 318 sigset_t SigMask; 319 sigemptyset(&SigMask); 320 sigaddset(&SigMask, Signal); 321 sigprocmask(SIG_UNBLOCK, &SigMask, nullptr); 322 323 if (CRCI) 324 const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash(); 325 } 326 327 static void installExceptionOrSignalHandlers() { 328 // Setup the signal handler. 329 struct sigaction Handler; 330 Handler.sa_handler = CrashRecoverySignalHandler; 331 Handler.sa_flags = 0; 332 sigemptyset(&Handler.sa_mask); 333 334 for (unsigned i = 0; i != NumSignals; ++i) { 335 sigaction(Signals[i], &Handler, &PrevActions[i]); 336 } 337 } 338 339 static void uninstallExceptionOrSignalHandlers() { 340 // Restore the previous signal handlers. 341 for (unsigned i = 0; i != NumSignals; ++i) 342 sigaction(Signals[i], &PrevActions[i], nullptr); 343 } 344 345 #endif // !_WIN32 346 347 bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) { 348 // If crash recovery is disabled, do nothing. 349 if (gCrashRecoveryEnabled) { 350 assert(!Impl && "Crash recovery context already initialized!"); 351 CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this); 352 Impl = CRCI; 353 354 if (setjmp(CRCI->JumpBuffer) != 0) { 355 return false; 356 } 357 } 358 359 Fn(); 360 return true; 361 } 362 363 #endif // !_MSC_VER 364 365 void CrashRecoveryContext::HandleCrash() { 366 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl; 367 assert(CRCI && "Crash recovery context never initialized!"); 368 CRCI->HandleCrash(); 369 } 370 371 // FIXME: Portability. 372 static void setThreadBackgroundPriority() { 373 #ifdef __APPLE__ 374 setpriority(PRIO_DARWIN_THREAD, 0, PRIO_DARWIN_BG); 375 #endif 376 } 377 378 static bool hasThreadBackgroundPriority() { 379 #ifdef __APPLE__ 380 return getpriority(PRIO_DARWIN_THREAD, 0) == 1; 381 #else 382 return false; 383 #endif 384 } 385 386 namespace { 387 struct RunSafelyOnThreadInfo { 388 function_ref<void()> Fn; 389 CrashRecoveryContext *CRC; 390 bool UseBackgroundPriority; 391 bool Result; 392 }; 393 } 394 395 static void RunSafelyOnThread_Dispatch(void *UserData) { 396 RunSafelyOnThreadInfo *Info = 397 reinterpret_cast<RunSafelyOnThreadInfo*>(UserData); 398 399 if (Info->UseBackgroundPriority) 400 setThreadBackgroundPriority(); 401 402 Info->Result = Info->CRC->RunSafely(Info->Fn); 403 } 404 bool CrashRecoveryContext::RunSafelyOnThread(function_ref<void()> Fn, 405 unsigned RequestedStackSize) { 406 bool UseBackgroundPriority = hasThreadBackgroundPriority(); 407 RunSafelyOnThreadInfo Info = { Fn, this, UseBackgroundPriority, false }; 408 llvm_execute_on_thread(RunSafelyOnThread_Dispatch, &Info, RequestedStackSize); 409 if (CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *)Impl) 410 CRC->setSwitchedThread(); 411 return Info.Result; 412 } 413