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/Signals.h"
14 #include "llvm/Support/ThreadLocal.h"
18 #include <excpt.h> // for GetExceptionInformation
21 #include <sysexits.h> // EX_IOERR
28 struct CrashRecoveryContextImpl;
31 sys::ThreadLocal<const CrashRecoveryContextImpl> > CurrentContext;
33 struct CrashRecoveryContextImpl {
34 // When threads are disabled, this links up all active
35 // CrashRecoveryContextImpls. When threads are enabled there's one thread
36 // per CrashRecoveryContext and CurrentContext is a thread-local, so only one
37 // CrashRecoveryContextImpl is active per thread and this is always null.
38 const CrashRecoveryContextImpl *Next;
40 CrashRecoveryContext *CRC;
42 volatile unsigned Failed : 1;
43 unsigned SwitchedThread : 1;
46 CrashRecoveryContextImpl(CrashRecoveryContext *CRC) : CRC(CRC),
48 SwitchedThread(false) {
49 Next = CurrentContext->get();
50 CurrentContext->set(this);
52 ~CrashRecoveryContextImpl() {
54 CurrentContext->set(Next);
57 /// Called when the separate crash-recovery thread was finished, to
58 /// indicate that we don't need to clear the thread-local CurrentContext.
59 void setSwitchedThread() {
60 #if defined(LLVM_ENABLE_THREADS) && LLVM_ENABLE_THREADS != 0
61 SwitchedThread = true;
65 // If the function ran by the CrashRecoveryContext crashes or fails, then
66 // 'RetCode' represents the returned error code, as if it was returned by a
67 // process. 'Context' represents the signal type on Unix; on Windows, it is
68 // the ExceptionContext.
69 void HandleCrash(int RetCode, uintptr_t Context) {
70 // Eliminate the current context entry, to avoid re-entering in case the
71 // cleanup code crashes.
72 CurrentContext->set(Next);
74 assert(!Failed && "Crash recovery context already failed!");
77 if (CRC->DumpStackAndCleanupOnFailure)
78 sys::CleanupOnSignal(Context);
80 CRC->RetCode = RetCode;
82 // Jump back to the RunSafely we were called under.
83 longjmp(JumpBuffer, 1);
89 static ManagedStatic<std::mutex> gCrashRecoveryContextMutex;
90 static bool gCrashRecoveryEnabled = false;
92 static ManagedStatic<sys::ThreadLocal<const CrashRecoveryContext>>
93 tlIsRecoveringFromCrash;
95 static void installExceptionOrSignalHandlers();
96 static void uninstallExceptionOrSignalHandlers();
98 CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() {}
100 CrashRecoveryContext::~CrashRecoveryContext() {
101 // Reclaim registered resources.
102 CrashRecoveryContextCleanup *i = head;
103 const CrashRecoveryContext *PC = tlIsRecoveringFromCrash->get();
104 tlIsRecoveringFromCrash->set(this);
106 CrashRecoveryContextCleanup *tmp = i;
108 tmp->cleanupFired = true;
109 tmp->recoverResources();
112 tlIsRecoveringFromCrash->set(PC);
114 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
118 bool CrashRecoveryContext::isRecoveringFromCrash() {
119 return tlIsRecoveringFromCrash->get() != nullptr;
122 CrashRecoveryContext *CrashRecoveryContext::GetCurrent() {
123 if (!gCrashRecoveryEnabled)
126 const CrashRecoveryContextImpl *CRCI = CurrentContext->get();
133 void CrashRecoveryContext::Enable() {
134 std::lock_guard<std::mutex> L(*gCrashRecoveryContextMutex);
135 // FIXME: Shouldn't this be a refcount or something?
136 if (gCrashRecoveryEnabled)
138 gCrashRecoveryEnabled = true;
139 installExceptionOrSignalHandlers();
142 void CrashRecoveryContext::Disable() {
143 std::lock_guard<std::mutex> L(*gCrashRecoveryContextMutex);
144 if (!gCrashRecoveryEnabled)
146 gCrashRecoveryEnabled = false;
147 uninstallExceptionOrSignalHandlers();
150 void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup)
155 head->prev = cleanup;
156 cleanup->next = head;
161 CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) {
164 if (cleanup == head) {
165 head = cleanup->next;
167 head->prev = nullptr;
170 cleanup->prev->next = cleanup->next;
172 cleanup->next->prev = cleanup->prev;
177 #if defined(_MSC_VER)
178 // If _MSC_VER is defined, we must have SEH. Use it if it's available. It's way
179 // better than VEH. Vectored exception handling catches all exceptions happening
180 // on the thread with installed exception handlers, so it can interfere with
181 // internal exception handling of other libraries on that thread. SEH works
182 // exactly as you would expect normal exception handling to work: it only
183 // catches exceptions if they would bubble out from the stack frame with __try /
186 static void installExceptionOrSignalHandlers() {}
187 static void uninstallExceptionOrSignalHandlers() {}
189 // We need this function because the call to GetExceptionInformation() can only
190 // occur inside the __except evaluation block
191 static int ExceptionFilter(bool DumpStackAndCleanup,
192 _EXCEPTION_POINTERS *Except) {
193 if (DumpStackAndCleanup)
194 sys::CleanupOnSignal((uintptr_t)Except);
195 return EXCEPTION_EXECUTE_HANDLER;
198 static bool InvokeFunctionCall(function_ref<void()> Fn,
199 bool DumpStackAndCleanup, int &RetCode) {
202 } __except (ExceptionFilter(DumpStackAndCleanup, GetExceptionInformation())) {
203 RetCode = GetExceptionCode();
209 bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
210 if (!gCrashRecoveryEnabled) {
214 return InvokeFunctionCall(Fn, DumpStackAndCleanupOnFailure, RetCode);
220 // This is a non-MSVC compiler, probably mingw gcc or clang without
221 // -fms-extensions. Use vectored exception handling (VEH).
223 // On Windows, we can make use of vectored exception handling to catch most
224 // crashing situations. Note that this does mean we will be alerted of
225 // exceptions *before* structured exception handling has the opportunity to
226 // catch it. Unfortunately, this causes problems in practice with other code
227 // running on threads with LLVM crash recovery contexts, so we would like to
228 // eventually move away from VEH.
230 // Vectored works on a per-thread basis, which is an advantage over
231 // SetUnhandledExceptionFilter. SetUnhandledExceptionFilter also doesn't have
232 // any native support for chaining exception handlers, but VEH allows more than
235 // The vectored exception handler functionality was added in Windows
236 // XP, so if support for older versions of Windows is required,
237 // it will have to be added.
239 #include "Windows/WindowsSupport.h"
241 static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
243 // DBG_PRINTEXCEPTION_WIDE_C is not properly defined on all supported
244 // compilers and platforms, so we define it manually.
245 constexpr ULONG DbgPrintExceptionWideC = 0x4001000AL;
246 switch (ExceptionInfo->ExceptionRecord->ExceptionCode)
248 case DBG_PRINTEXCEPTION_C:
249 case DbgPrintExceptionWideC:
250 case 0x406D1388: // set debugger thread name
251 return EXCEPTION_CONTINUE_EXECUTION;
254 // Lookup the current thread local recovery object.
255 const CrashRecoveryContextImpl *CRCI = CurrentContext->get();
258 // Something has gone horribly wrong, so let's just tell everyone
260 CrashRecoveryContext::Disable();
261 return EXCEPTION_CONTINUE_SEARCH;
264 // TODO: We can capture the stack backtrace here and store it on the
265 // implementation if we so choose.
268 const_cast<CrashRecoveryContextImpl *>(CRCI)->HandleCrash(
269 (int)ExceptionInfo->ExceptionRecord->ExceptionCode,
270 reinterpret_cast<uintptr_t>(ExceptionInfo));
272 // Note that we don't actually get here because HandleCrash calls
273 // longjmp, which means the HandleCrash function never returns.
274 llvm_unreachable("Handled the crash, should have longjmp'ed out of here");
277 // Because the Enable and Disable calls are static, it means that
278 // there may not actually be an Impl available, or even a current
279 // CrashRecoveryContext at all. So we make use of a thread-local
280 // exception table. The handles contained in here will either be
281 // non-NULL, valid VEH handles, or NULL.
282 static sys::ThreadLocal<const void> sCurrentExceptionHandle;
284 static void installExceptionOrSignalHandlers() {
285 // We can set up vectored exception handling now. We will install our
286 // handler as the front of the list, though there's no assurances that
287 // it will remain at the front (another call could install itself before
288 // our handler). This 1) isn't likely, and 2) shouldn't cause problems.
289 PVOID handle = ::AddVectoredExceptionHandler(1, ExceptionHandler);
290 sCurrentExceptionHandle.set(handle);
293 static void uninstallExceptionOrSignalHandlers() {
294 PVOID currentHandle = const_cast<PVOID>(sCurrentExceptionHandle.get());
296 // Now we can remove the vectored exception handler from the chain
297 ::RemoveVectoredExceptionHandler(currentHandle);
299 // Reset the handle in our thread-local set.
300 sCurrentExceptionHandle.set(NULL);
306 // Generic POSIX implementation.
308 // This implementation relies on synchronous signals being delivered to the
309 // current thread. We use a thread local object to keep track of the active
310 // crash recovery context, and install signal handlers to invoke HandleCrash on
311 // the active object.
313 // This implementation does not attempt to chain signal handlers in any
314 // reliable fashion -- if we get a signal outside of a crash recovery context we
315 // simply disable crash recovery and raise the signal again.
319 static const int Signals[] =
320 { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP };
321 static const unsigned NumSignals = array_lengthof(Signals);
322 static struct sigaction PrevActions[NumSignals];
324 static void CrashRecoverySignalHandler(int Signal) {
325 // Lookup the current thread local recovery object.
326 const CrashRecoveryContextImpl *CRCI = CurrentContext->get();
329 // We didn't find a crash recovery context -- this means either we got a
330 // signal on a thread we didn't expect it on, the application got a signal
331 // outside of a crash recovery context, or something else went horribly
334 // Disable crash recovery and raise the signal again. The assumption here is
335 // that the enclosing application will terminate soon, and we won't want to
336 // attempt crash recovery again.
338 // This call of Disable isn't thread safe, but it doesn't actually matter.
339 CrashRecoveryContext::Disable();
342 // The signal will be thrown once the signal mask is restored.
346 // Unblock the signal we received.
348 sigemptyset(&SigMask);
349 sigaddset(&SigMask, Signal);
350 sigprocmask(SIG_UNBLOCK, &SigMask, nullptr);
352 // As per convention, -2 indicates a crash or timeout as opposed to failure to
353 // execute (see llvm/include/llvm/Support/Program.h)
356 // Don't consider a broken pipe as a crash (see clang/lib/Driver/Driver.cpp)
357 if (Signal == SIGPIPE)
361 const_cast<CrashRecoveryContextImpl *>(CRCI)->HandleCrash(RetCode, Signal);
364 static void installExceptionOrSignalHandlers() {
365 // Setup the signal handler.
366 struct sigaction Handler;
367 Handler.sa_handler = CrashRecoverySignalHandler;
368 Handler.sa_flags = 0;
369 sigemptyset(&Handler.sa_mask);
371 for (unsigned i = 0; i != NumSignals; ++i) {
372 sigaction(Signals[i], &Handler, &PrevActions[i]);
376 static void uninstallExceptionOrSignalHandlers() {
377 // Restore the previous signal handlers.
378 for (unsigned i = 0; i != NumSignals; ++i)
379 sigaction(Signals[i], &PrevActions[i], nullptr);
384 bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
385 // If crash recovery is disabled, do nothing.
386 if (gCrashRecoveryEnabled) {
387 assert(!Impl && "Crash recovery context already initialized!");
388 CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this);
391 if (setjmp(CRCI->JumpBuffer) != 0) {
402 void CrashRecoveryContext::HandleCrash() {
403 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
404 assert(CRCI && "Crash recovery context never initialized!");
405 // As per convention, -2 indicates a crash or timeout as opposed to failure to
406 // execute (see llvm/include/llvm/Support/Program.h)
407 CRCI->HandleCrash(-2, 0);
410 // FIXME: Portability.
411 static void setThreadBackgroundPriority() {
413 setpriority(PRIO_DARWIN_THREAD, 0, PRIO_DARWIN_BG);
417 static bool hasThreadBackgroundPriority() {
419 return getpriority(PRIO_DARWIN_THREAD, 0) == 1;
426 struct RunSafelyOnThreadInfo {
427 function_ref<void()> Fn;
428 CrashRecoveryContext *CRC;
429 bool UseBackgroundPriority;
434 static void RunSafelyOnThread_Dispatch(void *UserData) {
435 RunSafelyOnThreadInfo *Info =
436 reinterpret_cast<RunSafelyOnThreadInfo*>(UserData);
438 if (Info->UseBackgroundPriority)
439 setThreadBackgroundPriority();
441 Info->Result = Info->CRC->RunSafely(Info->Fn);
443 bool CrashRecoveryContext::RunSafelyOnThread(function_ref<void()> Fn,
444 unsigned RequestedStackSize) {
445 bool UseBackgroundPriority = hasThreadBackgroundPriority();
446 RunSafelyOnThreadInfo Info = { Fn, this, UseBackgroundPriority, false };
447 llvm_execute_on_thread(RunSafelyOnThread_Dispatch, &Info,
448 RequestedStackSize == 0
450 : llvm::Optional<unsigned>(RequestedStackSize));
451 if (CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *)Impl)
452 CRC->setSwitchedThread();