|
@@ -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,27 +160,56 @@ 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)
|
|
|
{
|
|
@@ -203,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
|
|
@@ -219,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
|
|
@@ -237,7 +272,7 @@ void CrashRecoveryContext::Disable() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-#else
|
|
|
+#else // !LLVM_ON_WIN32
|
|
|
|
|
|
// Generic POSIX implementation.
|
|
|
//
|
|
@@ -289,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;
|
|
@@ -308,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.
|
|
@@ -339,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!");
|