forked from catchorg/Catch2
		
	Because the signal changes were in a different branch from the windows.h related changes, the SEH handling code included the header directly. Fixes #803
		
			
				
	
	
		
			172 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			172 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 *  Created by Phil on 21/08/2014
 | 
						|
 *  Copyright 2014 Two Blue Cubes Ltd. All rights reserved.
 | 
						|
 *
 | 
						|
 *  Distributed under the Boost Software License, Version 1.0. (See accompanying
 | 
						|
 *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 | 
						|
 *
 | 
						|
 */
 | 
						|
#ifndef TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
 | 
						|
#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
 | 
						|
 | 
						|
 | 
						|
namespace Catch {
 | 
						|
 | 
						|
    // Report the error condition
 | 
						|
    inline void reportFatal( std::string const& message ) {
 | 
						|
        IContext& context = Catch::getCurrentContext();
 | 
						|
        IResultCapture* resultCapture = context.getResultCapture();
 | 
						|
        resultCapture->handleFatalErrorCondition( message );
 | 
						|
    }
 | 
						|
 | 
						|
} // namespace Catch
 | 
						|
 | 
						|
#if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
 | 
						|
 | 
						|
 | 
						|
#include "catch_windows_h_proxy.h"
 | 
						|
 | 
						|
 | 
						|
namespace Catch {
 | 
						|
 | 
						|
    struct SignalDefs { DWORD id; const char* name; };
 | 
						|
    extern SignalDefs signalDefs[];
 | 
						|
    // There is no 1-1 mapping between signals and windows exceptions.
 | 
						|
    // Windows can easily distinguish between SO and SigSegV,
 | 
						|
    // but SigInt, SigTerm, etc are handled differently.
 | 
						|
    SignalDefs signalDefs[] = {
 | 
						|
        { EXCEPTION_ILLEGAL_INSTRUCTION,  "SIGILL - Illegal instruction signal" },
 | 
						|
        { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" },
 | 
						|
        { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" },
 | 
						|
        { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
 | 
						|
    };
 | 
						|
 | 
						|
    struct FatalConditionHandler {
 | 
						|
 | 
						|
        static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
 | 
						|
            for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
 | 
						|
                if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) {
 | 
						|
                    reportFatal(signalDefs[i].name);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            // If its not an exception we care about, pass it along.
 | 
						|
            // This stops us from eating debugger breaks etc.
 | 
						|
            return EXCEPTION_CONTINUE_SEARCH;
 | 
						|
        }
 | 
						|
 | 
						|
        // 32k seems enough for Catch to handle stack overflow,
 | 
						|
        // but the value was found experimentally, so there is no strong guarantee
 | 
						|
        FatalConditionHandler():m_isSet(true), m_guaranteeSize(32 * 1024), m_exceptionHandlerHandle(CATCH_NULL) {
 | 
						|
            // Register as first handler in current chain
 | 
						|
            m_exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
 | 
						|
            // Pass in guarantee size to be filled
 | 
						|
            SetThreadStackGuarantee(&m_guaranteeSize);
 | 
						|
        }
 | 
						|
 | 
						|
        void reset() {
 | 
						|
            if (m_isSet) {
 | 
						|
                // Unregister handler and restore the old guarantee
 | 
						|
                RemoveVectoredExceptionHandler(m_exceptionHandlerHandle);
 | 
						|
                SetThreadStackGuarantee(&m_guaranteeSize);
 | 
						|
                m_exceptionHandlerHandle = CATCH_NULL;
 | 
						|
                m_isSet = false;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        ~FatalConditionHandler() {
 | 
						|
            reset();
 | 
						|
        }
 | 
						|
    private:
 | 
						|
        bool m_isSet;
 | 
						|
        ULONG m_guaranteeSize;
 | 
						|
        PVOID m_exceptionHandlerHandle;
 | 
						|
    };
 | 
						|
 | 
						|
} // namespace Catch
 | 
						|
 | 
						|
#else // Not Windows - assumed to be POSIX compatible //////////////////////////
 | 
						|
 | 
						|
#include <signal.h>
 | 
						|
 | 
						|
namespace Catch {
 | 
						|
 | 
						|
    struct SignalDefs {
 | 
						|
        int id;
 | 
						|
        const char* name;
 | 
						|
    };
 | 
						|
    extern SignalDefs signalDefs[];
 | 
						|
    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" }
 | 
						|
    };
 | 
						|
 | 
						|
    struct FatalConditionHandler {
 | 
						|
 | 
						|
        static bool isSet;
 | 
						|
        static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)];
 | 
						|
        static stack_t oldSigStack;
 | 
						|
        static char altStackMem[SIGSTKSZ];
 | 
						|
 | 
						|
        static void handleSignal( int sig ) {
 | 
						|
            std::string name = "<unknown signal>";
 | 
						|
            for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
 | 
						|
                SignalDefs &def = signalDefs[i];
 | 
						|
                if (sig == def.id) {
 | 
						|
                    name = def.name;
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            reset();
 | 
						|
            reportFatal(name);
 | 
						|
            raise( sig );
 | 
						|
        }
 | 
						|
 | 
						|
        FatalConditionHandler() {
 | 
						|
            isSet = true;
 | 
						|
            stack_t sigStack;
 | 
						|
            sigStack.ss_sp = altStackMem;
 | 
						|
            sigStack.ss_size = SIGSTKSZ;
 | 
						|
            sigStack.ss_flags = 0;
 | 
						|
            sigaltstack(&sigStack, &oldSigStack);
 | 
						|
            struct sigaction sa = { 0 };
 | 
						|
 | 
						|
            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]);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        ~FatalConditionHandler() {
 | 
						|
            reset();
 | 
						|
        }
 | 
						|
        static void reset() {
 | 
						|
            if( isSet ) {
 | 
						|
                // Set signals back to previous values -- hopefully nobody overwrote them in the meantime
 | 
						|
                for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) {
 | 
						|
                    sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL);
 | 
						|
                }
 | 
						|
                // Return the old stack
 | 
						|
                sigaltstack(&oldSigStack, CATCH_NULL);
 | 
						|
                isSet = false;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    };
 | 
						|
 | 
						|
    bool FatalConditionHandler::isSet = false;
 | 
						|
    struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
 | 
						|
    stack_t FatalConditionHandler::oldSigStack = {};
 | 
						|
    char FatalConditionHandler::altStackMem[SIGSTKSZ] = {};
 | 
						|
 | 
						|
 | 
						|
} // namespace Catch
 | 
						|
 | 
						|
#endif // not Windows
 | 
						|
 | 
						|
#endif // TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
 |