xref: /freebsd-src/contrib/llvm-project/llvm/lib/Support/CrashRecoveryContext.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
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