| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include "catch_fatal_condition.h" |
|
|
| #include "catch_context.h" |
| #include "catch_enforce.h" |
| #include "catch_run_context.h" |
| #include "catch_windows_h_proxy.h" |
|
|
| #include <algorithm> |
|
|
| #if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS ) |
|
|
| namespace Catch { |
|
|
| |
| |
| void FatalConditionHandler::engage_platform() {} |
| void FatalConditionHandler::disengage_platform() {} |
| FatalConditionHandler::FatalConditionHandler() = default; |
| FatalConditionHandler::~FatalConditionHandler() = default; |
|
|
| } |
|
|
| #endif |
|
|
| #if defined( CATCH_CONFIG_WINDOWS_SEH ) && defined( CATCH_CONFIG_POSIX_SIGNALS ) |
| #error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time" |
| #endif |
|
|
| #if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) |
|
|
| namespace { |
| |
| void reportFatal( char const * const message ) { |
| Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); |
| } |
|
|
| |
| |
| |
| constexpr std::size_t minStackSizeForErrors = 32 * 1024; |
| } |
|
|
| #endif |
|
|
| #if defined( CATCH_CONFIG_WINDOWS_SEH ) |
|
|
| namespace Catch { |
|
|
| struct SignalDefs { DWORD id; const char* name; }; |
|
|
| |
| |
| |
| static SignalDefs signalDefs[] = { |
| { static_cast<DWORD>(EXCEPTION_ILLEGAL_INSTRUCTION), "SIGILL - Illegal instruction signal" }, |
| { static_cast<DWORD>(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow" }, |
| { static_cast<DWORD>(EXCEPTION_ACCESS_VIOLATION), "SIGSEGV - Segmentation violation signal" }, |
| { static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error" }, |
| }; |
|
|
| static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { |
| for (auto const& def : signalDefs) { |
| if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { |
| reportFatal(def.name); |
| } |
| } |
| |
| |
| return EXCEPTION_CONTINUE_SEARCH; |
| } |
|
|
| |
| |
| |
| static PVOID exceptionHandlerHandle = nullptr; |
|
|
|
|
| |
| |
| FatalConditionHandler::FatalConditionHandler() { |
| ULONG guaranteeSize = static_cast<ULONG>(minStackSizeForErrors); |
| if (!SetThreadStackGuarantee(&guaranteeSize)) { |
| |
| |
| Catch::cerr() |
| << "Failed to reserve piece of stack." |
| << " Stack overflows will not be reported successfully."; |
| } |
| } |
|
|
| |
| |
| FatalConditionHandler::~FatalConditionHandler() = default; |
|
|
|
|
| void FatalConditionHandler::engage_platform() { |
| |
| exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); |
| if (!exceptionHandlerHandle) { |
| CATCH_RUNTIME_ERROR("Could not register vectored exception handler"); |
| } |
| } |
|
|
| void FatalConditionHandler::disengage_platform() { |
| if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) { |
| CATCH_RUNTIME_ERROR("Could not unregister vectored exception handler"); |
| } |
| exceptionHandlerHandle = nullptr; |
| } |
|
|
| } |
|
|
| #endif |
|
|
| #if defined( CATCH_CONFIG_POSIX_SIGNALS ) |
|
|
| #include <signal.h> |
|
|
| namespace Catch { |
|
|
| struct SignalDefs { |
| int id; |
| const char* name; |
| }; |
|
|
| static SignalDefs signalDefs[] = { |
| { SIGINT, "SIGINT - Terminal interrupt signal" }, |
| { SIGILL, "SIGILL - Illegal instruction signal" }, |
| { SIGFPE, "SIGFPE - Floating point error signal" }, |
| { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, |
| { SIGTERM, "SIGTERM - Termination request signal" }, |
| { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } |
| }; |
|
|
| |
| |
| |
| #if defined(__GNUC__) |
| # pragma GCC diagnostic push |
| # pragma GCC diagnostic ignored "-Wmissing-field-initializers" |
| #endif |
|
|
| static char* altStackMem = nullptr; |
| static std::size_t altStackSize = 0; |
| static stack_t oldSigStack{}; |
| static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{}; |
|
|
| static void restorePreviousSignalHandlers() { |
| |
| |
| |
| |
| for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { |
| sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); |
| } |
| |
| sigaltstack(&oldSigStack, nullptr); |
| } |
|
|
| static void handleSignal( int sig ) { |
| char const * name = "<unknown signal>"; |
| for (auto const& def : signalDefs) { |
| if (sig == def.id) { |
| name = def.name; |
| break; |
| } |
| } |
| |
| |
| |
| restorePreviousSignalHandlers(); |
| reportFatal( name ); |
| raise( sig ); |
| } |
|
|
| FatalConditionHandler::FatalConditionHandler() { |
| assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists"); |
| if (altStackSize == 0) { |
| altStackSize = std::max(static_cast<size_t>(SIGSTKSZ), minStackSizeForErrors); |
| } |
| altStackMem = new char[altStackSize](); |
| } |
|
|
| FatalConditionHandler::~FatalConditionHandler() { |
| delete[] altStackMem; |
| |
| |
| altStackMem = nullptr; |
| } |
|
|
| void FatalConditionHandler::engage_platform() { |
| stack_t sigStack; |
| sigStack.ss_sp = altStackMem; |
| sigStack.ss_size = altStackSize; |
| sigStack.ss_flags = 0; |
| sigaltstack(&sigStack, &oldSigStack); |
| struct sigaction sa = { }; |
|
|
| sa.sa_handler = handleSignal; |
| sa.sa_flags = SA_ONSTACK; |
| for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { |
| sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); |
| } |
| } |
|
|
| #if defined(__GNUC__) |
| # pragma GCC diagnostic pop |
| #endif |
|
|
|
|
| void FatalConditionHandler::disengage_platform() { |
| restorePreviousSignalHandlers(); |
| } |
|
|
| } |
|
|
| #endif |
|
|