1 //===--- CrashRecoveryContext.cpp - Crash Recovery ------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "llvm/Support/CrashRecoveryContext.h"
10 #include "llvm/Config/llvm-config.h"
11 #include "llvm/Support/ErrorHandling.h"
12 #include "llvm/Support/ManagedStatic.h"
13 #include "llvm/Support/Mutex.h"
14 #include "llvm/Support/ThreadLocal.h"
20 struct CrashRecoveryContextImpl;
23 sys::ThreadLocal<const CrashRecoveryContextImpl> > CurrentContext;
25 struct CrashRecoveryContextImpl {
26 // When threads are disabled, this links up all active
27 // CrashRecoveryContextImpls. When threads are enabled there's one thread
28 // per CrashRecoveryContext and CurrentContext is a thread-local, so only one
29 // CrashRecoveryContextImpl is active per thread and this is always null.
30 const CrashRecoveryContextImpl *Next;
32 CrashRecoveryContext *CRC;
34 volatile unsigned Failed : 1;
35 unsigned SwitchedThread : 1;
38 CrashRecoveryContextImpl(CrashRecoveryContext *CRC) : CRC(CRC),
40 SwitchedThread(false) {
41 Next = CurrentContext->get();
42 CurrentContext->set(this);
44 ~CrashRecoveryContextImpl() {
46 CurrentContext->set(Next);
49 /// Called when the separate crash-recovery thread was finished, to
50 /// indicate that we don't need to clear the thread-local CurrentContext.
51 void setSwitchedThread() {
52 #if defined(LLVM_ENABLE_THREADS) && LLVM_ENABLE_THREADS != 0
53 SwitchedThread = true;
58 // Eliminate the current context entry, to avoid re-entering in case the
59 // cleanup code crashes.
60 CurrentContext->set(Next);
62 assert(!Failed && "Crash recovery context already failed!");
65 // FIXME: Stash the backtrace.
67 // Jump back to the RunSafely we were called under.
68 longjmp(JumpBuffer, 1);
74 static ManagedStatic<sys::Mutex> gCrashRecoveryContextMutex;
75 static bool gCrashRecoveryEnabled = false;
77 static ManagedStatic<sys::ThreadLocal<const CrashRecoveryContext>>
78 tlIsRecoveringFromCrash;
80 static void installExceptionOrSignalHandlers();
81 static void uninstallExceptionOrSignalHandlers();
83 CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() {}
85 CrashRecoveryContext::~CrashRecoveryContext() {
86 // Reclaim registered resources.
87 CrashRecoveryContextCleanup *i = head;
88 const CrashRecoveryContext *PC = tlIsRecoveringFromCrash->get();
89 tlIsRecoveringFromCrash->set(this);
91 CrashRecoveryContextCleanup *tmp = i;
93 tmp->cleanupFired = true;
94 tmp->recoverResources();
97 tlIsRecoveringFromCrash->set(PC);
99 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
103 bool CrashRecoveryContext::isRecoveringFromCrash() {
104 return tlIsRecoveringFromCrash->get() != nullptr;
107 CrashRecoveryContext *CrashRecoveryContext::GetCurrent() {
108 if (!gCrashRecoveryEnabled)
111 const CrashRecoveryContextImpl *CRCI = CurrentContext->get();
118 void CrashRecoveryContext::Enable() {
119 sys::ScopedLock L(*gCrashRecoveryContextMutex);
120 // FIXME: Shouldn't this be a refcount or something?
121 if (gCrashRecoveryEnabled)
123 gCrashRecoveryEnabled = true;
124 installExceptionOrSignalHandlers();
127 void CrashRecoveryContext::Disable() {
128 sys::ScopedLock L(*gCrashRecoveryContextMutex);
129 if (!gCrashRecoveryEnabled)
131 gCrashRecoveryEnabled = false;
132 uninstallExceptionOrSignalHandlers();
135 void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup)
140 head->prev = cleanup;
141 cleanup->next = head;
146 CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) {
149 if (cleanup == head) {
150 head = cleanup->next;
152 head->prev = nullptr;
155 cleanup->prev->next = cleanup->next;
157 cleanup->next->prev = cleanup->prev;
162 #if defined(_MSC_VER)
163 // If _MSC_VER is defined, we must have SEH. Use it if it's available. It's way
164 // better than VEH. Vectored exception handling catches all exceptions happening
165 // on the thread with installed exception handlers, so it can interfere with
166 // internal exception handling of other libraries on that thread. SEH works
167 // exactly as you would expect normal exception handling to work: it only
168 // catches exceptions if they would bubble out from the stack frame with __try /
171 static void installExceptionOrSignalHandlers() {}
172 static void uninstallExceptionOrSignalHandlers() {}
174 bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
175 if (!gCrashRecoveryEnabled) {
183 } __except (1) { // Catch any exception.
192 // This is a non-MSVC compiler, probably mingw gcc or clang without
193 // -fms-extensions. Use vectored exception handling (VEH).
195 // On Windows, we can make use of vectored exception handling to catch most
196 // crashing situations. Note that this does mean we will be alerted of
197 // exceptions *before* structured exception handling has the opportunity to
198 // catch it. Unfortunately, this causes problems in practice with other code
199 // running on threads with LLVM crash recovery contexts, so we would like to
200 // eventually move away from VEH.
202 // Vectored works on a per-thread basis, which is an advantage over
203 // SetUnhandledExceptionFilter. SetUnhandledExceptionFilter also doesn't have
204 // any native support for chaining exception handlers, but VEH allows more than
207 // The vectored exception handler functionality was added in Windows
208 // XP, so if support for older versions of Windows is required,
209 // it will have to be added.
211 #include "Windows/WindowsSupport.h"
213 static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
215 // DBG_PRINTEXCEPTION_WIDE_C is not properly defined on all supported
216 // compilers and platforms, so we define it manually.
217 constexpr ULONG DbgPrintExceptionWideC = 0x4001000AL;
218 switch (ExceptionInfo->ExceptionRecord->ExceptionCode)
220 case DBG_PRINTEXCEPTION_C:
221 case DbgPrintExceptionWideC:
222 case 0x406D1388: // set debugger thread name
223 return EXCEPTION_CONTINUE_EXECUTION;
226 // Lookup the current thread local recovery object.
227 const CrashRecoveryContextImpl *CRCI = CurrentContext->get();
230 // Something has gone horribly wrong, so let's just tell everyone
232 CrashRecoveryContext::Disable();
233 return EXCEPTION_CONTINUE_SEARCH;
236 // TODO: We can capture the stack backtrace here and store it on the
237 // implementation if we so choose.
240 const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash();
242 // Note that we don't actually get here because HandleCrash calls
243 // longjmp, which means the HandleCrash function never returns.
244 llvm_unreachable("Handled the crash, should have longjmp'ed out of here");
247 // Because the Enable and Disable calls are static, it means that
248 // there may not actually be an Impl available, or even a current
249 // CrashRecoveryContext at all. So we make use of a thread-local
250 // exception table. The handles contained in here will either be
251 // non-NULL, valid VEH handles, or NULL.
252 static sys::ThreadLocal<const void> sCurrentExceptionHandle;
254 static void installExceptionOrSignalHandlers() {
255 // We can set up vectored exception handling now. We will install our
256 // handler as the front of the list, though there's no assurances that
257 // it will remain at the front (another call could install itself before
258 // our handler). This 1) isn't likely, and 2) shouldn't cause problems.
259 PVOID handle = ::AddVectoredExceptionHandler(1, ExceptionHandler);
260 sCurrentExceptionHandle.set(handle);
263 static void uninstallExceptionOrSignalHandlers() {
264 PVOID currentHandle = const_cast<PVOID>(sCurrentExceptionHandle.get());
266 // Now we can remove the vectored exception handler from the chain
267 ::RemoveVectoredExceptionHandler(currentHandle);
269 // Reset the handle in our thread-local set.
270 sCurrentExceptionHandle.set(NULL);
276 // Generic POSIX implementation.
278 // This implementation relies on synchronous signals being delivered to the
279 // current thread. We use a thread local object to keep track of the active
280 // crash recovery context, and install signal handlers to invoke HandleCrash on
281 // the active object.
283 // This implementation does not to attempt to chain signal handlers in any
284 // reliable fashion -- if we get a signal outside of a crash recovery context we
285 // simply disable crash recovery and raise the signal again.
289 static const int Signals[] =
290 { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP };
291 static const unsigned NumSignals = array_lengthof(Signals);
292 static struct sigaction PrevActions[NumSignals];
294 static void CrashRecoverySignalHandler(int Signal) {
295 // Lookup the current thread local recovery object.
296 const CrashRecoveryContextImpl *CRCI = CurrentContext->get();
299 // We didn't find a crash recovery context -- this means either we got a
300 // signal on a thread we didn't expect it on, the application got a signal
301 // outside of a crash recovery context, or something else went horribly
304 // Disable crash recovery and raise the signal again. The assumption here is
305 // that the enclosing application will terminate soon, and we won't want to
306 // attempt crash recovery again.
308 // This call of Disable isn't thread safe, but it doesn't actually matter.
309 CrashRecoveryContext::Disable();
312 // The signal will be thrown once the signal mask is restored.
316 // Unblock the signal we received.
318 sigemptyset(&SigMask);
319 sigaddset(&SigMask, Signal);
320 sigprocmask(SIG_UNBLOCK, &SigMask, nullptr);
323 const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash();
326 static void installExceptionOrSignalHandlers() {
327 // Setup the signal handler.
328 struct sigaction Handler;
329 Handler.sa_handler = CrashRecoverySignalHandler;
330 Handler.sa_flags = 0;
331 sigemptyset(&Handler.sa_mask);
333 for (unsigned i = 0; i != NumSignals; ++i) {
334 sigaction(Signals[i], &Handler, &PrevActions[i]);
338 static void uninstallExceptionOrSignalHandlers() {
339 // Restore the previous signal handlers.
340 for (unsigned i = 0; i != NumSignals; ++i)
341 sigaction(Signals[i], &PrevActions[i], nullptr);
346 bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
347 // If crash recovery is disabled, do nothing.
348 if (gCrashRecoveryEnabled) {
349 assert(!Impl && "Crash recovery context already initialized!");
350 CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this);
353 if (setjmp(CRCI->JumpBuffer) != 0) {
364 void CrashRecoveryContext::HandleCrash() {
365 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
366 assert(CRCI && "Crash recovery context never initialized!");
370 // FIXME: Portability.
371 static void setThreadBackgroundPriority() {
373 setpriority(PRIO_DARWIN_THREAD, 0, PRIO_DARWIN_BG);
377 static bool hasThreadBackgroundPriority() {
379 return getpriority(PRIO_DARWIN_THREAD, 0) == 1;
386 struct RunSafelyOnThreadInfo {
387 function_ref<void()> Fn;
388 CrashRecoveryContext *CRC;
389 bool UseBackgroundPriority;
394 static void RunSafelyOnThread_Dispatch(void *UserData) {
395 RunSafelyOnThreadInfo *Info =
396 reinterpret_cast<RunSafelyOnThreadInfo*>(UserData);
398 if (Info->UseBackgroundPriority)
399 setThreadBackgroundPriority();
401 Info->Result = Info->CRC->RunSafely(Info->Fn);
403 bool CrashRecoveryContext::RunSafelyOnThread(function_ref<void()> Fn,
404 unsigned RequestedStackSize) {
405 bool UseBackgroundPriority = hasThreadBackgroundPriority();
406 RunSafelyOnThreadInfo Info = { Fn, this, UseBackgroundPriority, false };
407 llvm_execute_on_thread(RunSafelyOnThread_Dispatch, &Info, RequestedStackSize);
408 if (CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *)Impl)
409 CRC->setSwitchedThread();