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();