mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-10-31 20:27:11 +01:00 
			
		
		
		
	Merge branch 'dev-signals'
This commit is contained in:
		| @@ -12,25 +12,76 @@ | |||||||
|  |  | ||||||
| namespace Catch { | namespace Catch { | ||||||
|  |  | ||||||
|     // Report the error condition then exit the process |     // Report the error condition | ||||||
|     inline void fatal( std::string const& message, int exitCode ) { |     inline void reportFatal( std::string const& message, int exitCode ) { | ||||||
|         IContext& context = Catch::getCurrentContext(); |         IContext& context = Catch::getCurrentContext(); | ||||||
|         IResultCapture* resultCapture = context.getResultCapture(); |         IResultCapture* resultCapture = context.getResultCapture(); | ||||||
|         resultCapture->handleFatalErrorCondition( message ); |         resultCapture->handleFatalErrorCondition( message ); | ||||||
|  |  | ||||||
| 		if( Catch::alwaysTrue() ) // avoids "no return" warnings |  | ||||||
|             exit( exitCode ); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } // namespace Catch | } // namespace Catch | ||||||
|  |  | ||||||
| #if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// | #if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// | ||||||
|  |  | ||||||
|  | #define NOMINMAX | ||||||
|  | #define WIN32_LEAN_AND_MEAN | ||||||
|  | #include <windows.h> | ||||||
|  | #undef WIN32_LEAN_AND_MEAN | ||||||
|  | #undef NOMINMAX | ||||||
|  |  | ||||||
|  |  | ||||||
| namespace Catch { | 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 { |     struct FatalConditionHandler { | ||||||
| 		void reset() {} |  | ||||||
| 	}; |         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, -i); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             // 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) { | ||||||
|  |             // Register as first handler in current chain | ||||||
|  |             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(handleVectoredException); | ||||||
|  |                 SetThreadStackGuarantee(&m_guaranteeSize); | ||||||
|  |                 m_isSet = false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         ~FatalConditionHandler() { | ||||||
|  |             reset(); | ||||||
|  |         } | ||||||
|  |     private: | ||||||
|  |         bool m_isSet; | ||||||
|  |         ULONG m_guaranteeSize; | ||||||
|  |     }; | ||||||
|  |  | ||||||
| } // namespace Catch | } // namespace Catch | ||||||
|  |  | ||||||
| @@ -40,7 +91,10 @@ namespace Catch { | |||||||
|  |  | ||||||
| namespace Catch { | namespace Catch { | ||||||
|  |  | ||||||
|     struct SignalDefs { int id; const char* name; }; |     struct SignalDefs { | ||||||
|  |         int id; | ||||||
|  |         const char* name; | ||||||
|  |     }; | ||||||
|     extern SignalDefs signalDefs[]; |     extern SignalDefs signalDefs[]; | ||||||
|     SignalDefs signalDefs[] = { |     SignalDefs signalDefs[] = { | ||||||
|             { SIGINT,  "SIGINT - Terminal interrupt signal" }, |             { SIGINT,  "SIGINT - Terminal interrupt signal" }, | ||||||
| @@ -49,34 +103,67 @@ namespace Catch { | |||||||
|             { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, |             { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, | ||||||
|             { SIGTERM, "SIGTERM - Termination request signal" }, |             { SIGTERM, "SIGTERM - Termination request signal" }, | ||||||
|             { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } |             { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } | ||||||
|         }; |     }; | ||||||
|  |  | ||||||
|     struct FatalConditionHandler { |     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 ) { |         static void handleSignal( int sig ) { | ||||||
|             for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) |             std::string name = "<unknown signal>"; | ||||||
|                 if( sig == signalDefs[i].id ) |             for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { | ||||||
|                     fatal( signalDefs[i].name, -sig ); |                 SignalDefs &def = signalDefs[i]; | ||||||
|             fatal( "<unknown signal>", -sig ); |                 if (sig == def.id) { | ||||||
|  |                     name = def.name; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             reset(); | ||||||
|  |             reportFatal(name, -sig); | ||||||
|  |             raise( sig ); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         FatalConditionHandler() : m_isSet( true ) { |         FatalConditionHandler() { | ||||||
|             for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) |             isSet = true; | ||||||
|                 signal( signalDefs[i].id, handleSignal ); |             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() { |         ~FatalConditionHandler() { | ||||||
|             reset(); |             reset(); | ||||||
|         } |         } | ||||||
|         void reset() { |         static void reset() { | ||||||
|             if( m_isSet ) { |             if( isSet ) { | ||||||
|                 for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) |                 // Set signals back to previous values -- hopefully nobody overwrote them in the meantime | ||||||
|                     signal( signalDefs[i].id, SIG_DFL ); |                 for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { | ||||||
|                 m_isSet = false; |                     sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL); | ||||||
|  |                 } | ||||||
|  |                 // Return the old stack | ||||||
|  |                 sigaltstack(&oldSigStack, CATCH_NULL); | ||||||
|  |                 isSet = false; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         bool m_isSet; |  | ||||||
|     }; |     }; | ||||||
|  |      | ||||||
|  |     bool FatalConditionHandler::isSet = false; | ||||||
|  |     struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; | ||||||
|  |     stack_t FatalConditionHandler::oldSigStack = {}; | ||||||
|  |     char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; | ||||||
|  |      | ||||||
|  |  | ||||||
| } // namespace Catch | } // namespace Catch | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Martin Hořeňovský
					Martin Hořeňovský