10b57cec5SDimitry Andric //===--- CrashRecoveryContext.cpp - Crash Recovery ------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric
90b57cec5SDimitry Andric #include "llvm/Support/CrashRecoveryContext.h"
100b57cec5SDimitry Andric #include "llvm/Config/llvm-config.h"
110b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h"
1281ad6265SDimitry Andric #include "llvm/Support/ExitCodes.h"
13480093f4SDimitry Andric #include "llvm/Support/Signals.h"
14fe6060f1SDimitry Andric #include "llvm/Support/thread.h"
15bdd1243dSDimitry Andric #include <cassert>
168bcb0991SDimitry Andric #include <mutex>
170b57cec5SDimitry Andric #include <setjmp.h>
18480093f4SDimitry Andric
190b57cec5SDimitry Andric using namespace llvm;
200b57cec5SDimitry Andric
210b57cec5SDimitry Andric namespace {
220b57cec5SDimitry Andric
230b57cec5SDimitry Andric struct CrashRecoveryContextImpl;
24bdd1243dSDimitry Andric static LLVM_THREAD_LOCAL const CrashRecoveryContextImpl *CurrentContext;
250b57cec5SDimitry Andric
260b57cec5SDimitry Andric struct CrashRecoveryContextImpl {
270b57cec5SDimitry Andric // When threads are disabled, this links up all active
280b57cec5SDimitry Andric // CrashRecoveryContextImpls. When threads are enabled there's one thread
290b57cec5SDimitry Andric // per CrashRecoveryContext and CurrentContext is a thread-local, so only one
300b57cec5SDimitry Andric // CrashRecoveryContextImpl is active per thread and this is always null.
310b57cec5SDimitry Andric const CrashRecoveryContextImpl *Next;
320b57cec5SDimitry Andric
330b57cec5SDimitry Andric CrashRecoveryContext *CRC;
340b57cec5SDimitry Andric ::jmp_buf JumpBuffer;
350b57cec5SDimitry Andric volatile unsigned Failed : 1;
360b57cec5SDimitry Andric unsigned SwitchedThread : 1;
3713138422SDimitry Andric unsigned ValidJumpBuffer : 1;
380b57cec5SDimitry Andric
390b57cec5SDimitry Andric public:
CrashRecoveryContextImpl__anon00542cf30111::CrashRecoveryContextImpl4013138422SDimitry Andric CrashRecoveryContextImpl(CrashRecoveryContext *CRC) noexcept
4113138422SDimitry Andric : CRC(CRC), Failed(false), SwitchedThread(false), ValidJumpBuffer(false) {
42bdd1243dSDimitry Andric Next = CurrentContext;
43bdd1243dSDimitry Andric CurrentContext = this;
440b57cec5SDimitry Andric }
~CrashRecoveryContextImpl__anon00542cf30111::CrashRecoveryContextImpl450b57cec5SDimitry Andric ~CrashRecoveryContextImpl() {
460b57cec5SDimitry Andric if (!SwitchedThread)
47bdd1243dSDimitry Andric CurrentContext = Next;
480b57cec5SDimitry Andric }
490b57cec5SDimitry Andric
500b57cec5SDimitry Andric /// Called when the separate crash-recovery thread was finished, to
510b57cec5SDimitry Andric /// indicate that we don't need to clear the thread-local CurrentContext.
setSwitchedThread__anon00542cf30111::CrashRecoveryContextImpl520b57cec5SDimitry Andric void setSwitchedThread() {
530b57cec5SDimitry Andric #if defined(LLVM_ENABLE_THREADS) && LLVM_ENABLE_THREADS != 0
540b57cec5SDimitry Andric SwitchedThread = true;
550b57cec5SDimitry Andric #endif
560b57cec5SDimitry Andric }
570b57cec5SDimitry Andric
58480093f4SDimitry Andric // If the function ran by the CrashRecoveryContext crashes or fails, then
59480093f4SDimitry Andric // 'RetCode' represents the returned error code, as if it was returned by a
60480093f4SDimitry Andric // process. 'Context' represents the signal type on Unix; on Windows, it is
61480093f4SDimitry Andric // the ExceptionContext.
HandleCrash__anon00542cf30111::CrashRecoveryContextImpl62480093f4SDimitry Andric void HandleCrash(int RetCode, uintptr_t Context) {
630b57cec5SDimitry Andric // Eliminate the current context entry, to avoid re-entering in case the
640b57cec5SDimitry Andric // cleanup code crashes.
65bdd1243dSDimitry Andric CurrentContext = Next;
660b57cec5SDimitry Andric
670b57cec5SDimitry Andric assert(!Failed && "Crash recovery context already failed!");
680b57cec5SDimitry Andric Failed = true;
690b57cec5SDimitry Andric
70480093f4SDimitry Andric if (CRC->DumpStackAndCleanupOnFailure)
71480093f4SDimitry Andric sys::CleanupOnSignal(Context);
72480093f4SDimitry Andric
73480093f4SDimitry Andric CRC->RetCode = RetCode;
740b57cec5SDimitry Andric
750b57cec5SDimitry Andric // Jump back to the RunSafely we were called under.
7613138422SDimitry Andric if (ValidJumpBuffer)
770b57cec5SDimitry Andric longjmp(JumpBuffer, 1);
7813138422SDimitry Andric
7913138422SDimitry Andric // Otherwise let the caller decide of the outcome of the crash. Currently
8013138422SDimitry Andric // this occurs when using SEH on Windows with MSVC or clang-cl.
810b57cec5SDimitry Andric }
820b57cec5SDimitry Andric };
830b57cec5SDimitry Andric
getCrashRecoveryContextMutex()84bdd1243dSDimitry Andric std::mutex &getCrashRecoveryContextMutex() {
85bdd1243dSDimitry Andric static std::mutex CrashRecoveryContextMutex;
86bdd1243dSDimitry Andric return CrashRecoveryContextMutex;
87bdd1243dSDimitry Andric }
88bdd1243dSDimitry Andric
890b57cec5SDimitry Andric static bool gCrashRecoveryEnabled = false;
900b57cec5SDimitry Andric
91bdd1243dSDimitry Andric static LLVM_THREAD_LOCAL const CrashRecoveryContext *IsRecoveringFromCrash;
92bdd1243dSDimitry Andric
93bdd1243dSDimitry Andric } // namespace
940b57cec5SDimitry Andric
950b57cec5SDimitry Andric static void installExceptionOrSignalHandlers();
960b57cec5SDimitry Andric static void uninstallExceptionOrSignalHandlers();
970b57cec5SDimitry Andric
9881ad6265SDimitry Andric CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() = default;
990b57cec5SDimitry Andric
CrashRecoveryContext()100e8d8bef9SDimitry Andric CrashRecoveryContext::CrashRecoveryContext() {
101e8d8bef9SDimitry Andric // On Windows, if abort() was previously triggered (and caught by a previous
102e8d8bef9SDimitry Andric // CrashRecoveryContext) the Windows CRT removes our installed signal handler,
103e8d8bef9SDimitry Andric // so we need to install it again.
104e8d8bef9SDimitry Andric sys::DisableSystemDialogsOnCrash();
105e8d8bef9SDimitry Andric }
106e8d8bef9SDimitry Andric
~CrashRecoveryContext()1070b57cec5SDimitry Andric CrashRecoveryContext::~CrashRecoveryContext() {
1080b57cec5SDimitry Andric // Reclaim registered resources.
1090b57cec5SDimitry Andric CrashRecoveryContextCleanup *i = head;
110bdd1243dSDimitry Andric const CrashRecoveryContext *PC = IsRecoveringFromCrash;
111bdd1243dSDimitry Andric IsRecoveringFromCrash = this;
1120b57cec5SDimitry Andric while (i) {
1130b57cec5SDimitry Andric CrashRecoveryContextCleanup *tmp = i;
1140b57cec5SDimitry Andric i = tmp->next;
1150b57cec5SDimitry Andric tmp->cleanupFired = true;
1160b57cec5SDimitry Andric tmp->recoverResources();
1170b57cec5SDimitry Andric delete tmp;
1180b57cec5SDimitry Andric }
119bdd1243dSDimitry Andric IsRecoveringFromCrash = PC;
1200b57cec5SDimitry Andric
1210b57cec5SDimitry Andric CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
1220b57cec5SDimitry Andric delete CRCI;
1230b57cec5SDimitry Andric }
1240b57cec5SDimitry Andric
isRecoveringFromCrash()1250b57cec5SDimitry Andric bool CrashRecoveryContext::isRecoveringFromCrash() {
126bdd1243dSDimitry Andric return IsRecoveringFromCrash != nullptr;
1270b57cec5SDimitry Andric }
1280b57cec5SDimitry Andric
GetCurrent()1290b57cec5SDimitry Andric CrashRecoveryContext *CrashRecoveryContext::GetCurrent() {
1300b57cec5SDimitry Andric if (!gCrashRecoveryEnabled)
1310b57cec5SDimitry Andric return nullptr;
1320b57cec5SDimitry Andric
133bdd1243dSDimitry Andric const CrashRecoveryContextImpl *CRCI = CurrentContext;
1340b57cec5SDimitry Andric if (!CRCI)
1350b57cec5SDimitry Andric return nullptr;
1360b57cec5SDimitry Andric
1370b57cec5SDimitry Andric return CRCI->CRC;
1380b57cec5SDimitry Andric }
1390b57cec5SDimitry Andric
Enable()1400b57cec5SDimitry Andric void CrashRecoveryContext::Enable() {
141bdd1243dSDimitry Andric std::lock_guard<std::mutex> L(getCrashRecoveryContextMutex());
1420b57cec5SDimitry Andric // FIXME: Shouldn't this be a refcount or something?
1430b57cec5SDimitry Andric if (gCrashRecoveryEnabled)
1440b57cec5SDimitry Andric return;
1450b57cec5SDimitry Andric gCrashRecoveryEnabled = true;
1460b57cec5SDimitry Andric installExceptionOrSignalHandlers();
1470b57cec5SDimitry Andric }
1480b57cec5SDimitry Andric
Disable()1490b57cec5SDimitry Andric void CrashRecoveryContext::Disable() {
150bdd1243dSDimitry Andric std::lock_guard<std::mutex> L(getCrashRecoveryContextMutex());
1510b57cec5SDimitry Andric if (!gCrashRecoveryEnabled)
1520b57cec5SDimitry Andric return;
1530b57cec5SDimitry Andric gCrashRecoveryEnabled = false;
1540b57cec5SDimitry Andric uninstallExceptionOrSignalHandlers();
1550b57cec5SDimitry Andric }
1560b57cec5SDimitry Andric
registerCleanup(CrashRecoveryContextCleanup * cleanup)1570b57cec5SDimitry Andric void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup)
1580b57cec5SDimitry Andric {
1590b57cec5SDimitry Andric if (!cleanup)
1600b57cec5SDimitry Andric return;
1610b57cec5SDimitry Andric if (head)
1620b57cec5SDimitry Andric head->prev = cleanup;
1630b57cec5SDimitry Andric cleanup->next = head;
1640b57cec5SDimitry Andric head = cleanup;
1650b57cec5SDimitry Andric }
1660b57cec5SDimitry Andric
1670b57cec5SDimitry Andric void
unregisterCleanup(CrashRecoveryContextCleanup * cleanup)1680b57cec5SDimitry Andric CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) {
1690b57cec5SDimitry Andric if (!cleanup)
1700b57cec5SDimitry Andric return;
1710b57cec5SDimitry Andric if (cleanup == head) {
1720b57cec5SDimitry Andric head = cleanup->next;
1730b57cec5SDimitry Andric if (head)
1740b57cec5SDimitry Andric head->prev = nullptr;
1750b57cec5SDimitry Andric }
1760b57cec5SDimitry Andric else {
1770b57cec5SDimitry Andric cleanup->prev->next = cleanup->next;
1780b57cec5SDimitry Andric if (cleanup->next)
1790b57cec5SDimitry Andric cleanup->next->prev = cleanup->prev;
1800b57cec5SDimitry Andric }
1810b57cec5SDimitry Andric delete cleanup;
1820b57cec5SDimitry Andric }
1830b57cec5SDimitry Andric
1840b57cec5SDimitry Andric #if defined(_MSC_VER)
18513138422SDimitry Andric
18613138422SDimitry Andric #include <windows.h> // for GetExceptionInformation
18713138422SDimitry Andric
1880b57cec5SDimitry Andric // If _MSC_VER is defined, we must have SEH. Use it if it's available. It's way
1890b57cec5SDimitry Andric // better than VEH. Vectored exception handling catches all exceptions happening
1900b57cec5SDimitry Andric // on the thread with installed exception handlers, so it can interfere with
1910b57cec5SDimitry Andric // internal exception handling of other libraries on that thread. SEH works
1920b57cec5SDimitry Andric // exactly as you would expect normal exception handling to work: it only
1930b57cec5SDimitry Andric // catches exceptions if they would bubble out from the stack frame with __try /
1940b57cec5SDimitry Andric // __except.
1950b57cec5SDimitry Andric
installExceptionOrSignalHandlers()1960b57cec5SDimitry Andric static void installExceptionOrSignalHandlers() {}
uninstallExceptionOrSignalHandlers()1970b57cec5SDimitry Andric static void uninstallExceptionOrSignalHandlers() {}
1980b57cec5SDimitry Andric
199480093f4SDimitry Andric // We need this function because the call to GetExceptionInformation() can only
200480093f4SDimitry Andric // occur inside the __except evaluation block
ExceptionFilter(_EXCEPTION_POINTERS * Except)20113138422SDimitry Andric static int ExceptionFilter(_EXCEPTION_POINTERS *Except) {
20213138422SDimitry Andric // Lookup the current thread local recovery object.
203bdd1243dSDimitry Andric const CrashRecoveryContextImpl *CRCI = CurrentContext;
20413138422SDimitry Andric
20513138422SDimitry Andric if (!CRCI) {
20613138422SDimitry Andric // Something has gone horribly wrong, so let's just tell everyone
20713138422SDimitry Andric // to keep searching
20813138422SDimitry Andric CrashRecoveryContext::Disable();
20913138422SDimitry Andric return EXCEPTION_CONTINUE_SEARCH;
21013138422SDimitry Andric }
21113138422SDimitry Andric
21213138422SDimitry Andric int RetCode = (int)Except->ExceptionRecord->ExceptionCode;
21313138422SDimitry Andric if ((RetCode & 0xF0000000) == 0xE0000000)
21413138422SDimitry Andric RetCode &= ~0xF0000000; // this crash was generated by sys::Process::Exit
21513138422SDimitry Andric
21613138422SDimitry Andric // Handle the crash
21713138422SDimitry Andric const_cast<CrashRecoveryContextImpl *>(CRCI)->HandleCrash(
21813138422SDimitry Andric RetCode, reinterpret_cast<uintptr_t>(Except));
21913138422SDimitry Andric
220480093f4SDimitry Andric return EXCEPTION_EXECUTE_HANDLER;
221480093f4SDimitry Andric }
222480093f4SDimitry Andric
22313138422SDimitry Andric #if defined(__clang__) && defined(_M_IX86)
22413138422SDimitry Andric // Work around PR44697.
22513138422SDimitry Andric __attribute__((optnone))
22613138422SDimitry Andric #endif
RunSafely(function_ref<void ()> Fn)2270b57cec5SDimitry Andric bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
2280b57cec5SDimitry Andric if (!gCrashRecoveryEnabled) {
2290b57cec5SDimitry Andric Fn();
2300b57cec5SDimitry Andric return true;
2310b57cec5SDimitry Andric }
23213138422SDimitry Andric assert(!Impl && "Crash recovery context already initialized!");
23313138422SDimitry Andric Impl = new CrashRecoveryContextImpl(this);
23413138422SDimitry Andric __try {
23513138422SDimitry Andric Fn();
23613138422SDimitry Andric } __except (ExceptionFilter(GetExceptionInformation())) {
23713138422SDimitry Andric return false;
23813138422SDimitry Andric }
23913138422SDimitry Andric return true;
2400b57cec5SDimitry Andric }
2410b57cec5SDimitry Andric
2420b57cec5SDimitry Andric #else // !_MSC_VER
2430b57cec5SDimitry Andric
2440b57cec5SDimitry Andric #if defined(_WIN32)
2450b57cec5SDimitry Andric // This is a non-MSVC compiler, probably mingw gcc or clang without
2460b57cec5SDimitry Andric // -fms-extensions. Use vectored exception handling (VEH).
2470b57cec5SDimitry Andric //
2480b57cec5SDimitry Andric // On Windows, we can make use of vectored exception handling to catch most
2490b57cec5SDimitry Andric // crashing situations. Note that this does mean we will be alerted of
2500b57cec5SDimitry Andric // exceptions *before* structured exception handling has the opportunity to
2510b57cec5SDimitry Andric // catch it. Unfortunately, this causes problems in practice with other code
2520b57cec5SDimitry Andric // running on threads with LLVM crash recovery contexts, so we would like to
2530b57cec5SDimitry Andric // eventually move away from VEH.
2540b57cec5SDimitry Andric //
2550b57cec5SDimitry Andric // Vectored works on a per-thread basis, which is an advantage over
2560b57cec5SDimitry Andric // SetUnhandledExceptionFilter. SetUnhandledExceptionFilter also doesn't have
2570b57cec5SDimitry Andric // any native support for chaining exception handlers, but VEH allows more than
2580b57cec5SDimitry Andric // one.
2590b57cec5SDimitry Andric //
2600b57cec5SDimitry Andric // The vectored exception handler functionality was added in Windows
2610b57cec5SDimitry Andric // XP, so if support for older versions of Windows is required,
2620b57cec5SDimitry Andric // it will have to be added.
2630b57cec5SDimitry Andric
2648c27c554SDimitry Andric #include "llvm/Support/Windows/WindowsSupport.h"
2650b57cec5SDimitry Andric
ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)2660b57cec5SDimitry Andric static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
2670b57cec5SDimitry Andric {
2680b57cec5SDimitry Andric // DBG_PRINTEXCEPTION_WIDE_C is not properly defined on all supported
2690b57cec5SDimitry Andric // compilers and platforms, so we define it manually.
2700b57cec5SDimitry Andric constexpr ULONG DbgPrintExceptionWideC = 0x4001000AL;
2710b57cec5SDimitry Andric switch (ExceptionInfo->ExceptionRecord->ExceptionCode)
2720b57cec5SDimitry Andric {
2730b57cec5SDimitry Andric case DBG_PRINTEXCEPTION_C:
2740b57cec5SDimitry Andric case DbgPrintExceptionWideC:
2750b57cec5SDimitry Andric case 0x406D1388: // set debugger thread name
2760b57cec5SDimitry Andric return EXCEPTION_CONTINUE_EXECUTION;
2770b57cec5SDimitry Andric }
2780b57cec5SDimitry Andric
2790b57cec5SDimitry Andric // Lookup the current thread local recovery object.
280bdd1243dSDimitry Andric const CrashRecoveryContextImpl *CRCI = CurrentContext;
2810b57cec5SDimitry Andric
2820b57cec5SDimitry Andric if (!CRCI) {
2830b57cec5SDimitry Andric // Something has gone horribly wrong, so let's just tell everyone
2840b57cec5SDimitry Andric // to keep searching
2850b57cec5SDimitry Andric CrashRecoveryContext::Disable();
2860b57cec5SDimitry Andric return EXCEPTION_CONTINUE_SEARCH;
2870b57cec5SDimitry Andric }
2880b57cec5SDimitry Andric
2890b57cec5SDimitry Andric // TODO: We can capture the stack backtrace here and store it on the
2900b57cec5SDimitry Andric // implementation if we so choose.
2910b57cec5SDimitry Andric
29213138422SDimitry Andric int RetCode = (int)ExceptionInfo->ExceptionRecord->ExceptionCode;
29313138422SDimitry Andric if ((RetCode & 0xF0000000) == 0xE0000000)
29413138422SDimitry Andric RetCode &= ~0xF0000000; // this crash was generated by sys::Process::Exit
29513138422SDimitry Andric
2960b57cec5SDimitry Andric // Handle the crash
297480093f4SDimitry Andric const_cast<CrashRecoveryContextImpl *>(CRCI)->HandleCrash(
29813138422SDimitry Andric RetCode, reinterpret_cast<uintptr_t>(ExceptionInfo));
2990b57cec5SDimitry Andric
3000b57cec5SDimitry Andric // Note that we don't actually get here because HandleCrash calls
3010b57cec5SDimitry Andric // longjmp, which means the HandleCrash function never returns.
3020b57cec5SDimitry Andric llvm_unreachable("Handled the crash, should have longjmp'ed out of here");
3030b57cec5SDimitry Andric }
3040b57cec5SDimitry Andric
3050b57cec5SDimitry Andric // Because the Enable and Disable calls are static, it means that
3060b57cec5SDimitry Andric // there may not actually be an Impl available, or even a current
3070b57cec5SDimitry Andric // CrashRecoveryContext at all. So we make use of a thread-local
3080b57cec5SDimitry Andric // exception table. The handles contained in here will either be
3090b57cec5SDimitry Andric // non-NULL, valid VEH handles, or NULL.
310bdd1243dSDimitry Andric static LLVM_THREAD_LOCAL const void* sCurrentExceptionHandle;
3110b57cec5SDimitry Andric
installExceptionOrSignalHandlers()3120b57cec5SDimitry Andric static void installExceptionOrSignalHandlers() {
3130b57cec5SDimitry Andric // We can set up vectored exception handling now. We will install our
3140b57cec5SDimitry Andric // handler as the front of the list, though there's no assurances that
3150b57cec5SDimitry Andric // it will remain at the front (another call could install itself before
3160b57cec5SDimitry Andric // our handler). This 1) isn't likely, and 2) shouldn't cause problems.
3170b57cec5SDimitry Andric PVOID handle = ::AddVectoredExceptionHandler(1, ExceptionHandler);
318bdd1243dSDimitry Andric sCurrentExceptionHandle = handle;
3190b57cec5SDimitry Andric }
3200b57cec5SDimitry Andric
uninstallExceptionOrSignalHandlers()3210b57cec5SDimitry Andric static void uninstallExceptionOrSignalHandlers() {
322bdd1243dSDimitry Andric PVOID currentHandle = const_cast<PVOID>(sCurrentExceptionHandle);
3230b57cec5SDimitry Andric if (currentHandle) {
3240b57cec5SDimitry Andric // Now we can remove the vectored exception handler from the chain
3250b57cec5SDimitry Andric ::RemoveVectoredExceptionHandler(currentHandle);
3260b57cec5SDimitry Andric
3270b57cec5SDimitry Andric // Reset the handle in our thread-local set.
328bdd1243dSDimitry Andric sCurrentExceptionHandle = NULL;
3290b57cec5SDimitry Andric }
3300b57cec5SDimitry Andric }
3310b57cec5SDimitry Andric
3320b57cec5SDimitry Andric #else // !_WIN32
3330b57cec5SDimitry Andric
3340b57cec5SDimitry Andric // Generic POSIX implementation.
3350b57cec5SDimitry Andric //
3360b57cec5SDimitry Andric // This implementation relies on synchronous signals being delivered to the
3370b57cec5SDimitry Andric // current thread. We use a thread local object to keep track of the active
3380b57cec5SDimitry Andric // crash recovery context, and install signal handlers to invoke HandleCrash on
3390b57cec5SDimitry Andric // the active object.
3400b57cec5SDimitry Andric //
341480093f4SDimitry Andric // This implementation does not attempt to chain signal handlers in any
3420b57cec5SDimitry Andric // reliable fashion -- if we get a signal outside of a crash recovery context we
3430b57cec5SDimitry Andric // simply disable crash recovery and raise the signal again.
3440b57cec5SDimitry Andric
3450b57cec5SDimitry Andric #include <signal.h>
3460b57cec5SDimitry Andric
3470b57cec5SDimitry Andric static const int Signals[] =
3480b57cec5SDimitry Andric { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP };
349bdd1243dSDimitry Andric static const unsigned NumSignals = std::size(Signals);
3500b57cec5SDimitry Andric static struct sigaction PrevActions[NumSignals];
3510b57cec5SDimitry Andric
CrashRecoverySignalHandler(int Signal)3520b57cec5SDimitry Andric static void CrashRecoverySignalHandler(int Signal) {
3530b57cec5SDimitry Andric // Lookup the current thread local recovery object.
354bdd1243dSDimitry Andric const CrashRecoveryContextImpl *CRCI = CurrentContext;
3550b57cec5SDimitry Andric
3560b57cec5SDimitry Andric if (!CRCI) {
3570b57cec5SDimitry Andric // We didn't find a crash recovery context -- this means either we got a
3580b57cec5SDimitry Andric // signal on a thread we didn't expect it on, the application got a signal
3590b57cec5SDimitry Andric // outside of a crash recovery context, or something else went horribly
3600b57cec5SDimitry Andric // wrong.
3610b57cec5SDimitry Andric //
3620b57cec5SDimitry Andric // Disable crash recovery and raise the signal again. The assumption here is
3630b57cec5SDimitry Andric // that the enclosing application will terminate soon, and we won't want to
3640b57cec5SDimitry Andric // attempt crash recovery again.
3650b57cec5SDimitry Andric //
3660b57cec5SDimitry Andric // This call of Disable isn't thread safe, but it doesn't actually matter.
3670b57cec5SDimitry Andric CrashRecoveryContext::Disable();
3680b57cec5SDimitry Andric raise(Signal);
3690b57cec5SDimitry Andric
3700b57cec5SDimitry Andric // The signal will be thrown once the signal mask is restored.
3710b57cec5SDimitry Andric return;
3720b57cec5SDimitry Andric }
3730b57cec5SDimitry Andric
3740b57cec5SDimitry Andric // Unblock the signal we received.
3750b57cec5SDimitry Andric sigset_t SigMask;
3760b57cec5SDimitry Andric sigemptyset(&SigMask);
3770b57cec5SDimitry Andric sigaddset(&SigMask, Signal);
3780b57cec5SDimitry Andric sigprocmask(SIG_UNBLOCK, &SigMask, nullptr);
3790b57cec5SDimitry Andric
380e8d8bef9SDimitry Andric // Return the same error code as if the program crashed, as mentioned in the
381e8d8bef9SDimitry Andric // section "Exit Status for Commands":
382e8d8bef9SDimitry Andric // https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html
383e8d8bef9SDimitry Andric int RetCode = 128 + Signal;
384480093f4SDimitry Andric
385480093f4SDimitry Andric // Don't consider a broken pipe as a crash (see clang/lib/Driver/Driver.cpp)
386480093f4SDimitry Andric if (Signal == SIGPIPE)
387480093f4SDimitry Andric RetCode = EX_IOERR;
388480093f4SDimitry Andric
3890b57cec5SDimitry Andric if (CRCI)
390480093f4SDimitry Andric const_cast<CrashRecoveryContextImpl *>(CRCI)->HandleCrash(RetCode, Signal);
3910b57cec5SDimitry Andric }
3920b57cec5SDimitry Andric
installExceptionOrSignalHandlers()3930b57cec5SDimitry Andric static void installExceptionOrSignalHandlers() {
3940b57cec5SDimitry Andric // Setup the signal handler.
3950b57cec5SDimitry Andric struct sigaction Handler;
3960b57cec5SDimitry Andric Handler.sa_handler = CrashRecoverySignalHandler;
3970b57cec5SDimitry Andric Handler.sa_flags = 0;
3980b57cec5SDimitry Andric sigemptyset(&Handler.sa_mask);
3990b57cec5SDimitry Andric
4000b57cec5SDimitry Andric for (unsigned i = 0; i != NumSignals; ++i) {
4010b57cec5SDimitry Andric sigaction(Signals[i], &Handler, &PrevActions[i]);
4020b57cec5SDimitry Andric }
4030b57cec5SDimitry Andric }
4040b57cec5SDimitry Andric
uninstallExceptionOrSignalHandlers()4050b57cec5SDimitry Andric static void uninstallExceptionOrSignalHandlers() {
4060b57cec5SDimitry Andric // Restore the previous signal handlers.
4070b57cec5SDimitry Andric for (unsigned i = 0; i != NumSignals; ++i)
4080b57cec5SDimitry Andric sigaction(Signals[i], &PrevActions[i], nullptr);
4090b57cec5SDimitry Andric }
4100b57cec5SDimitry Andric
4110b57cec5SDimitry Andric #endif // !_WIN32
4120b57cec5SDimitry Andric
RunSafely(function_ref<void ()> Fn)4130b57cec5SDimitry Andric bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
4140b57cec5SDimitry Andric // If crash recovery is disabled, do nothing.
4150b57cec5SDimitry Andric if (gCrashRecoveryEnabled) {
4160b57cec5SDimitry Andric assert(!Impl && "Crash recovery context already initialized!");
4170b57cec5SDimitry Andric CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this);
4180b57cec5SDimitry Andric Impl = CRCI;
4190b57cec5SDimitry Andric
42013138422SDimitry Andric CRCI->ValidJumpBuffer = true;
4210b57cec5SDimitry Andric if (setjmp(CRCI->JumpBuffer) != 0) {
4220b57cec5SDimitry Andric return false;
4230b57cec5SDimitry Andric }
4240b57cec5SDimitry Andric }
4250b57cec5SDimitry Andric
4260b57cec5SDimitry Andric Fn();
4270b57cec5SDimitry Andric return true;
4280b57cec5SDimitry Andric }
4290b57cec5SDimitry Andric
4300b57cec5SDimitry Andric #endif // !_MSC_VER
4310b57cec5SDimitry Andric
HandleExit(int RetCode)432349cc55cSDimitry Andric [[noreturn]] void CrashRecoveryContext::HandleExit(int RetCode) {
43313138422SDimitry Andric #if defined(_WIN32)
434*06c3fb27SDimitry Andric // Since the exception code is actually of NTSTATUS type, we use the
435*06c3fb27SDimitry Andric // Microsoft-recommended 0xE prefix, to signify that this is a user error.
436*06c3fb27SDimitry Andric // This value is a combination of the customer field (bit 29) and severity
437*06c3fb27SDimitry Andric // field (bits 30-31) in the NTSTATUS specification.
43813138422SDimitry Andric ::RaiseException(0xE0000000 | RetCode, 0, 0, NULL);
43913138422SDimitry Andric #else
44013138422SDimitry Andric // On Unix we don't need to raise an exception, we go directly to
44113138422SDimitry Andric // HandleCrash(), then longjmp will unwind the stack for us.
4420b57cec5SDimitry Andric CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *)Impl;
4430b57cec5SDimitry Andric assert(CRCI && "Crash recovery context never initialized!");
44413138422SDimitry Andric CRCI->HandleCrash(RetCode, 0 /*no sig num*/);
44513138422SDimitry Andric #endif
44613138422SDimitry Andric llvm_unreachable("Most likely setjmp wasn't called!");
4470b57cec5SDimitry Andric }
4480b57cec5SDimitry Andric
isCrash(int RetCode)44981ad6265SDimitry Andric bool CrashRecoveryContext::isCrash(int RetCode) {
450e8d8bef9SDimitry Andric #if defined(_WIN32)
451*06c3fb27SDimitry Andric // On Windows, the code is interpreted as NTSTATUS. The two high bits
452*06c3fb27SDimitry Andric // represent the severity. Values starting with 0x80000000 are reserved for
453*06c3fb27SDimitry Andric // "warnings"; values of 0xC0000000 and up are for "errors". In practice, both
454*06c3fb27SDimitry Andric // are interpreted as a non-continuable signal.
455e8d8bef9SDimitry Andric unsigned Code = ((unsigned)RetCode & 0xF0000000) >> 28;
456e8d8bef9SDimitry Andric if (Code != 0xC && Code != 8)
457e8d8bef9SDimitry Andric return false;
458e8d8bef9SDimitry Andric #else
459e8d8bef9SDimitry Andric // On Unix, signals are represented by return codes of 128 or higher.
460e8d8bef9SDimitry Andric // Exit code 128 is a reserved value and should not be raised as a signal.
461e8d8bef9SDimitry Andric if (RetCode <= 128)
462e8d8bef9SDimitry Andric return false;
46381ad6265SDimitry Andric #endif
46481ad6265SDimitry Andric return true;
46581ad6265SDimitry Andric }
46681ad6265SDimitry Andric
throwIfCrash(int RetCode)46781ad6265SDimitry Andric bool CrashRecoveryContext::throwIfCrash(int RetCode) {
46881ad6265SDimitry Andric if (!isCrash(RetCode))
46981ad6265SDimitry Andric return false;
47081ad6265SDimitry Andric #if defined(_WIN32)
47181ad6265SDimitry Andric ::RaiseException(RetCode, 0, 0, NULL);
47281ad6265SDimitry Andric #else
473e8d8bef9SDimitry Andric llvm::sys::unregisterHandlers();
474e8d8bef9SDimitry Andric raise(RetCode - 128);
475e8d8bef9SDimitry Andric #endif
476e8d8bef9SDimitry Andric return true;
477e8d8bef9SDimitry Andric }
478e8d8bef9SDimitry Andric
4790b57cec5SDimitry Andric // FIXME: Portability.
setThreadBackgroundPriority()4800b57cec5SDimitry Andric static void setThreadBackgroundPriority() {
4810b57cec5SDimitry Andric #ifdef __APPLE__
4820b57cec5SDimitry Andric setpriority(PRIO_DARWIN_THREAD, 0, PRIO_DARWIN_BG);
4830b57cec5SDimitry Andric #endif
4840b57cec5SDimitry Andric }
4850b57cec5SDimitry Andric
hasThreadBackgroundPriority()4860b57cec5SDimitry Andric static bool hasThreadBackgroundPriority() {
4870b57cec5SDimitry Andric #ifdef __APPLE__
4880b57cec5SDimitry Andric return getpriority(PRIO_DARWIN_THREAD, 0) == 1;
4890b57cec5SDimitry Andric #else
4900b57cec5SDimitry Andric return false;
4910b57cec5SDimitry Andric #endif
4920b57cec5SDimitry Andric }
4930b57cec5SDimitry Andric
4940b57cec5SDimitry Andric namespace {
4950b57cec5SDimitry Andric struct RunSafelyOnThreadInfo {
4960b57cec5SDimitry Andric function_ref<void()> Fn;
4970b57cec5SDimitry Andric CrashRecoveryContext *CRC;
4980b57cec5SDimitry Andric bool UseBackgroundPriority;
4990b57cec5SDimitry Andric bool Result;
5000b57cec5SDimitry Andric };
501fe6060f1SDimitry Andric } // namespace
5020b57cec5SDimitry Andric
RunSafelyOnThread_Dispatch(void * UserData)5030b57cec5SDimitry Andric static void RunSafelyOnThread_Dispatch(void *UserData) {
5040b57cec5SDimitry Andric RunSafelyOnThreadInfo *Info =
5050b57cec5SDimitry Andric reinterpret_cast<RunSafelyOnThreadInfo*>(UserData);
5060b57cec5SDimitry Andric
5070b57cec5SDimitry Andric if (Info->UseBackgroundPriority)
5080b57cec5SDimitry Andric setThreadBackgroundPriority();
5090b57cec5SDimitry Andric
5100b57cec5SDimitry Andric Info->Result = Info->CRC->RunSafely(Info->Fn);
5110b57cec5SDimitry Andric }
RunSafelyOnThread(function_ref<void ()> Fn,unsigned RequestedStackSize)5120b57cec5SDimitry Andric bool CrashRecoveryContext::RunSafelyOnThread(function_ref<void()> Fn,
5130b57cec5SDimitry Andric unsigned RequestedStackSize) {
5140b57cec5SDimitry Andric bool UseBackgroundPriority = hasThreadBackgroundPriority();
5150b57cec5SDimitry Andric RunSafelyOnThreadInfo Info = { Fn, this, UseBackgroundPriority, false };
516fe6060f1SDimitry Andric llvm::thread Thread(RequestedStackSize == 0
517bdd1243dSDimitry Andric ? std::nullopt
518bdd1243dSDimitry Andric : std::optional<unsigned>(RequestedStackSize),
519fe6060f1SDimitry Andric RunSafelyOnThread_Dispatch, &Info);
520fe6060f1SDimitry Andric Thread.join();
521fe6060f1SDimitry Andric
5220b57cec5SDimitry Andric if (CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *)Impl)
5230b57cec5SDimitry Andric CRC->setSwitchedThread();
5240b57cec5SDimitry Andric return Info.Result;
5250b57cec5SDimitry Andric }
526