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 { | ||||
|  | ||||
|     // Report the error condition then exit the process | ||||
|     inline void fatal( std::string const& message, int exitCode ) { | ||||
|     // Report the error condition | ||||
|     inline void reportFatal( std::string const& message, int exitCode ) { | ||||
|         IContext& context = Catch::getCurrentContext(); | ||||
|         IResultCapture* resultCapture = context.getResultCapture(); | ||||
|         resultCapture->handleFatalErrorCondition( message ); | ||||
|  | ||||
| 		if( Catch::alwaysTrue() ) // avoids "no return" warnings | ||||
|             exit( exitCode ); | ||||
|     } | ||||
|  | ||||
| } // namespace Catch | ||||
|  | ||||
| #if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// | ||||
|  | ||||
| #define NOMINMAX | ||||
| #define WIN32_LEAN_AND_MEAN | ||||
| #include <windows.h> | ||||
| #undef WIN32_LEAN_AND_MEAN | ||||
| #undef NOMINMAX | ||||
|  | ||||
|  | ||||
| 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 { | ||||
| 		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 | ||||
|  | ||||
| @@ -40,7 +91,10 @@ namespace Catch { | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     struct SignalDefs { int id; const char* name; }; | ||||
|     struct SignalDefs { | ||||
|         int id; | ||||
|         const char* name; | ||||
|     }; | ||||
|     extern SignalDefs signalDefs[]; | ||||
|     SignalDefs signalDefs[] = { | ||||
|             { SIGINT,  "SIGINT - Terminal interrupt signal" }, | ||||
| @@ -49,34 +103,67 @@ namespace Catch { | ||||
|             { 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 ) { | ||||
|             for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) | ||||
|                 if( sig == signalDefs[i].id ) | ||||
|                     fatal( signalDefs[i].name, -sig ); | ||||
|             fatal( "<unknown signal>", -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, -sig); | ||||
|             raise( sig ); | ||||
|         } | ||||
|  | ||||
|         FatalConditionHandler() : m_isSet( true ) { | ||||
|             for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) | ||||
|                 signal( signalDefs[i].id, handleSignal ); | ||||
|         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(); | ||||
|         } | ||||
|         void reset() { | ||||
|             if( m_isSet ) { | ||||
|                 for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) | ||||
|                     signal( signalDefs[i].id, SIG_DFL ); | ||||
|                 m_isSet = false; | ||||
|         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 m_isSet; | ||||
|     }; | ||||
|      | ||||
|     bool FatalConditionHandler::isSet = false; | ||||
|     struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; | ||||
|     stack_t FatalConditionHandler::oldSigStack = {}; | ||||
|     char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; | ||||
|      | ||||
|  | ||||
| } // namespace Catch | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Martin Hořeňovský
					Martin Hořeňovský