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