aboutsummaryrefslogtreecommitdiff
path: root/lib/Support/CrashRecoveryContext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Support/CrashRecoveryContext.cpp')
-rw-r--r--lib/Support/CrashRecoveryContext.cpp130
1 files changed, 82 insertions, 48 deletions
diff --git a/lib/Support/CrashRecoveryContext.cpp b/lib/Support/CrashRecoveryContext.cpp
index 98865f5e065e..bd38dd88201f 100644
--- a/lib/Support/CrashRecoveryContext.cpp
+++ b/lib/Support/CrashRecoveryContext.cpp
@@ -78,6 +78,9 @@ static bool gCrashRecoveryEnabled = false;
static ManagedStatic<sys::ThreadLocal<const CrashRecoveryContext>>
tlIsRecoveringFromCrash;
+static void installExceptionOrSignalHandlers();
+static void uninstallExceptionOrSignalHandlers();
+
CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() {}
CrashRecoveryContext::~CrashRecoveryContext() {
@@ -113,6 +116,23 @@ CrashRecoveryContext *CrashRecoveryContext::GetCurrent() {
return CRCI->CRC;
}
+void CrashRecoveryContext::Enable() {
+ sys::ScopedLock L(*gCrashRecoveryContextMutex);
+ // FIXME: Shouldn't this be a refcount or something?
+ if (gCrashRecoveryEnabled)
+ return;
+ gCrashRecoveryEnabled = true;
+ installExceptionOrSignalHandlers();
+}
+
+void CrashRecoveryContext::Disable() {
+ sys::ScopedLock L(*gCrashRecoveryContextMutex);
+ if (!gCrashRecoveryEnabled)
+ return;
+ gCrashRecoveryEnabled = false;
+ uninstallExceptionOrSignalHandlers();
+}
+
void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup)
{
if (!cleanup)
@@ -140,30 +160,70 @@ CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) {
delete cleanup;
}
-#ifdef LLVM_ON_WIN32
+#if defined(_MSC_VER)
+// If _MSC_VER is defined, we must have SEH. Use it if it's available. It's way
+// better than VEH. Vectored exception handling catches all exceptions happening
+// on the thread with installed exception handlers, so it can interfere with
+// internal exception handling of other libraries on that thread. SEH works
+// exactly as you would expect normal exception handling to work: it only
+// catches exceptions if they would bubble out from the stack frame with __try /
+// __except.
-#include "Windows/WindowsSupport.h"
+static void installExceptionOrSignalHandlers() {}
+static void uninstallExceptionOrSignalHandlers() {}
-// On Windows, we can make use of vectored exception handling to
-// catch most crashing situations. Note that this does mean
-// we will be alerted of exceptions *before* structured exception
-// handling has the opportunity to catch it. But that isn't likely
-// to cause problems because nowhere in the project is SEH being
-// used.
+bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
+ if (!gCrashRecoveryEnabled) {
+ Fn();
+ return true;
+ }
+
+ bool Result = true;
+ __try {
+ Fn();
+ } __except (1) { // Catch any exception.
+ Result = false;
+ }
+ return Result;
+}
+
+#else // !_MSC_VER
+
+#if defined(LLVM_ON_WIN32)
+// This is a non-MSVC compiler, probably mingw gcc or clang without
+// -fms-extensions. Use vectored exception handling (VEH).
+//
+// On Windows, we can make use of vectored exception handling to catch most
+// crashing situations. Note that this does mean we will be alerted of
+// exceptions *before* structured exception handling has the opportunity to
+// catch it. Unfortunately, this causes problems in practice with other code
+// running on threads with LLVM crash recovery contexts, so we would like to
+// eventually move away from VEH.
//
-// Vectored exception handling is built on top of SEH, and so it
-// works on a per-thread basis.
+// Vectored works on a per-thread basis, which is an advantage over
+// SetUnhandledExceptionFilter. SetUnhandledExceptionFilter also doesn't have
+// any native support for chaining exception handlers, but VEH allows more than
+// one.
//
// The vectored exception handler functionality was added in Windows
// XP, so if support for older versions of Windows is required,
// it will have to be added.
-//
-// If we want to support as far back as Win2k, we could use the
-// SetUnhandledExceptionFilter API, but there's a risk of that
-// being entirely overwritten (it's not a chain).
+
+#include "Windows/WindowsSupport.h"
static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
{
+ // DBG_PRINTEXCEPTION_WIDE_C is not properly defined on all supported
+ // compilers and platforms, so we define it manually.
+ constexpr ULONG DbgPrintExceptionWideC = 0x4001000AL;
+ switch (ExceptionInfo->ExceptionRecord->ExceptionCode)
+ {
+ case DBG_PRINTEXCEPTION_C:
+ case DbgPrintExceptionWideC:
+ case 0x406D1388: // set debugger thread name
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
+
// Lookup the current thread local recovery object.
const CrashRecoveryContextImpl *CRCI = CurrentContext->get();
@@ -192,14 +252,7 @@ static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
// non-NULL, valid VEH handles, or NULL.
static sys::ThreadLocal<const void> sCurrentExceptionHandle;
-void CrashRecoveryContext::Enable() {
- sys::ScopedLock L(*gCrashRecoveryContextMutex);
-
- if (gCrashRecoveryEnabled)
- return;
-
- gCrashRecoveryEnabled = true;
-
+static void installExceptionOrSignalHandlers() {
// We can set up vectored exception handling now. We will install our
// handler as the front of the list, though there's no assurances that
// it will remain at the front (another call could install itself before
@@ -208,14 +261,7 @@ void CrashRecoveryContext::Enable() {
sCurrentExceptionHandle.set(handle);
}
-void CrashRecoveryContext::Disable() {
- sys::ScopedLock L(*gCrashRecoveryContextMutex);
-
- if (!gCrashRecoveryEnabled)
- return;
-
- gCrashRecoveryEnabled = false;
-
+static void uninstallExceptionOrSignalHandlers() {
PVOID currentHandle = const_cast<PVOID>(sCurrentExceptionHandle.get());
if (currentHandle) {
// Now we can remove the vectored exception handler from the chain
@@ -226,7 +272,7 @@ void CrashRecoveryContext::Disable() {
}
}
-#else
+#else // !LLVM_ON_WIN32
// Generic POSIX implementation.
//
@@ -278,14 +324,7 @@ static void CrashRecoverySignalHandler(int Signal) {
const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash();
}
-void CrashRecoveryContext::Enable() {
- sys::ScopedLock L(*gCrashRecoveryContextMutex);
-
- if (gCrashRecoveryEnabled)
- return;
-
- gCrashRecoveryEnabled = true;
-
+static void installExceptionOrSignalHandlers() {
// Setup the signal handler.
struct sigaction Handler;
Handler.sa_handler = CrashRecoverySignalHandler;
@@ -297,20 +336,13 @@ void CrashRecoveryContext::Enable() {
}
}
-void CrashRecoveryContext::Disable() {
- sys::ScopedLock L(*gCrashRecoveryContextMutex);
-
- if (!gCrashRecoveryEnabled)
- return;
-
- gCrashRecoveryEnabled = false;
-
+static void uninstallExceptionOrSignalHandlers() {
// Restore the previous signal handlers.
for (unsigned i = 0; i != NumSignals; ++i)
sigaction(Signals[i], &PrevActions[i], nullptr);
}
-#endif
+#endif // !LLVM_ON_WIN32
bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
// If crash recovery is disabled, do nothing.
@@ -328,6 +360,8 @@ bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
return true;
}
+#endif // !_MSC_VER
+
void CrashRecoveryContext::HandleCrash() {
CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
assert(CRCI && "Crash recovery context never initialized!");