Index: llvm/lib/Support/CrashRecoveryContext.cpp =================================================================== --- llvm/lib/Support/CrashRecoveryContext.cpp +++ llvm/lib/Support/CrashRecoveryContext.cpp @@ -140,7 +140,26 @@ 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. + +void CrashRecoveryContext::Enable() {} +void CrashRecoveryContext::Disable() {} + +bool CrashRecoveryContext::RunSafely(function_ref Fn) { + bool Result = true; + __try { + Fn(); + } __except (1) { // Catch any exception. + Result = false; + } + return Result; +} + +#elif defined(LLVM_ON_WIN32) +// This is a non-MSVC compiler, probably mingw gcc or clang. Use VEH. #include "Windows/WindowsSupport.h" @@ -162,8 +181,7 @@ // SetUnhandledExceptionFilter API, but there's a risk of that // being entirely overwritten (it's not a chain). -static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) -{ +static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) { // Lookup the current thread local recovery object. const CrashRecoveryContextImpl *CRCI = CurrentContext->get(); @@ -226,7 +244,23 @@ } } -#else +bool CrashRecoveryContext::RunSafely(function_ref Fn) { + // If crash recovery is disabled, do nothing. + if (gCrashRecoveryEnabled) { + assert(!Impl && "Crash recovery context already initialized!"); + CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this); + Impl = CRCI; + + if (setjmp(CRCI->JumpBuffer) != 0) { + return false; + } + } + + Fn(); + return true; +} + +#else // !LLVM_ON_WIN32 // Generic POSIX implementation. // @@ -310,8 +344,6 @@ sigaction(Signals[i], &PrevActions[i], nullptr); } -#endif - bool CrashRecoveryContext::RunSafely(function_ref Fn) { // If crash recovery is disabled, do nothing. if (gCrashRecoveryEnabled) { @@ -328,6 +360,8 @@ return true; } +#endif + void CrashRecoveryContext::HandleCrash() { CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl; assert(CRCI && "Crash recovery context never initialized!"); Index: llvm/unittests/Support/CMakeLists.txt =================================================================== --- llvm/unittests/Support/CMakeLists.txt +++ llvm/unittests/Support/CMakeLists.txt @@ -11,6 +11,7 @@ BlockFrequencyTest.cpp BranchProbabilityTest.cpp CachePruningTest.cpp + CrashRecoveryTest.cpp Casting.cpp Chrono.cpp CommandLineTest.cpp Index: llvm/unittests/Support/CrashRecoveryTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/Support/CrashRecoveryTest.cpp @@ -0,0 +1,93 @@ +//===- llvm/unittest/Support/CrashRecoveryTest.cpp ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/Compiler.h" +#include "gtest/gtest.h" + +#ifdef LLVM_ON_WIN32 +#define WIN32_LEAN_AND_MEAN +#define NOGDI +#include +#endif + +using namespace llvm; +using namespace llvm::sys; + +static int GlobalInt = 0; +static void nullDeref() { *(volatile int *)nullptr = 0; } +static void incrementGlobal() { ++GlobalInt; } +static void llvmTrap() { LLVM_BUILTIN_TRAP; } + +TEST(CrashRecoveryTest, Basic) { + llvm::CrashRecoveryContext::Enable(); + GlobalInt = 0; + EXPECT_TRUE(CrashRecoveryContext().RunSafely(incrementGlobal)); + EXPECT_EQ(1, GlobalInt); + EXPECT_FALSE(CrashRecoveryContext().RunSafely(nullDeref)); + EXPECT_FALSE(CrashRecoveryContext().RunSafely(llvmTrap)); +} + +struct IncrementGlobalCleanup : CrashRecoveryContextCleanup { + IncrementGlobalCleanup(CrashRecoveryContext *CRC) + : CrashRecoveryContextCleanup(CRC) {} + virtual void recoverResources() { ++GlobalInt; } +}; + +static void noop() {} + +TEST(CrashRecoveryTest, Cleanup) { + llvm::CrashRecoveryContext::Enable(); + GlobalInt = 0; + { + CrashRecoveryContext CRC; + CRC.registerCleanup(new IncrementGlobalCleanup(&CRC)); + EXPECT_TRUE(CRC.RunSafely(noop)); + } // run cleanups + EXPECT_EQ(1, GlobalInt); + + GlobalInt = 0; + { + CrashRecoveryContext CRC; + CRC.registerCleanup(new IncrementGlobalCleanup(&CRC)); + EXPECT_FALSE(CRC.RunSafely(nullDeref)); + } // run cleanups + EXPECT_EQ(1, GlobalInt); +} + +#ifdef LLVM_ON_WIN32 +static void raiseIt() { + RaiseException(123, EXCEPTION_NONCONTINUABLE, 0, NULL); +} + +TEST(CrashRecoveryTest, RaiseException) { + llvm::CrashRecoveryContext::Enable(); + EXPECT_FALSE(CrashRecoveryContext().RunSafely(raiseIt)); +} + +static void outputString() { + OutputDebugStringA("output for debugger\n"); +} + +#undef OutputDebugString + +// FIXME: This test is broken on non-MSVC (mingw gcc or clang) because we use +// VEH there, which doesn't interact well with internally-thrown exceptions. +#ifdef _MSC_VER +#define OutputDebugString_MAYBE OutputDebugString +#else +#define OutputDebugString_MAYBE DISABLED_OutputDebugString +#endif + +TEST(CrashRecoveryTest, OutputDebugString_MAYBE) { + llvm::CrashRecoveryContext::Enable(); + EXPECT_TRUE(CrashRecoveryContext().RunSafely(outputString)); +} + +#endif