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