1 /* 2 * Created by Phil on 21/08/2014 3 * Copyright 2014 Two Blue Cubes Ltd. All rights reserved. 4 * 5 * Distributed under the Boost Software License, Version 1.0. (See accompanying 6 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 * 8 */ 9 #ifndef TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED 10 #define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED 11 12 13 namespace Catch { 14 15 // Report the error condition reportFatal(std::string const & message)16 inline void reportFatal( std::string const& message ) { 17 IContext& context = Catch::getCurrentContext(); 18 IResultCapture* resultCapture = context.getResultCapture(); 19 resultCapture->handleFatalErrorCondition( message ); 20 } 21 22 } // namespace Catch 23 24 #if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// 25 #include "catch_windows_h_proxy.h" 26 27 # if !defined ( CATCH_CONFIG_WINDOWS_SEH ) 28 29 namespace Catch { 30 struct FatalConditionHandler { resetCatch::FatalConditionHandler31 void reset() {} 32 }; 33 } 34 35 # else // CATCH_CONFIG_WINDOWS_SEH is defined 36 37 namespace Catch { 38 39 struct SignalDefs { DWORD id; const char* name; }; 40 extern SignalDefs signalDefs[]; 41 // There is no 1-1 mapping between signals and windows exceptions. 42 // Windows can easily distinguish between SO and SigSegV, 43 // but SigInt, SigTerm, etc are handled differently. 44 SignalDefs signalDefs[] = { 45 { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, 46 { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, 47 { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, 48 { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, 49 }; 50 51 struct FatalConditionHandler { 52 handleVectoredExceptionCatch::FatalConditionHandler53 static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { 54 for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { 55 if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { 56 reportFatal(signalDefs[i].name); 57 } 58 } 59 // If its not an exception we care about, pass it along. 60 // This stops us from eating debugger breaks etc. 61 return EXCEPTION_CONTINUE_SEARCH; 62 } 63 FatalConditionHandlerCatch::FatalConditionHandler64 FatalConditionHandler() { 65 isSet = true; 66 // 32k seems enough for Catch to handle stack overflow, 67 // but the value was found experimentally, so there is no strong guarantee 68 guaranteeSize = 32 * 1024; 69 exceptionHandlerHandle = CATCH_NULL; 70 // Register as first handler in current chain 71 exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); 72 // Pass in guarantee size to be filled 73 SetThreadStackGuarantee(&guaranteeSize); 74 } 75 resetCatch::FatalConditionHandler76 static void reset() { 77 if (isSet) { 78 // Unregister handler and restore the old guarantee 79 RemoveVectoredExceptionHandler(exceptionHandlerHandle); 80 SetThreadStackGuarantee(&guaranteeSize); 81 exceptionHandlerHandle = CATCH_NULL; 82 isSet = false; 83 } 84 } 85 ~FatalConditionHandlerCatch::FatalConditionHandler86 ~FatalConditionHandler() { 87 reset(); 88 } 89 private: 90 static bool isSet; 91 static ULONG guaranteeSize; 92 static PVOID exceptionHandlerHandle; 93 }; 94 95 bool FatalConditionHandler::isSet = false; 96 ULONG FatalConditionHandler::guaranteeSize = 0; 97 PVOID FatalConditionHandler::exceptionHandlerHandle = CATCH_NULL; 98 99 } // namespace Catch 100 101 # endif // CATCH_CONFIG_WINDOWS_SEH 102 103 #else // Not Windows - assumed to be POSIX compatible ////////////////////////// 104 105 # if !defined(CATCH_CONFIG_POSIX_SIGNALS) 106 107 namespace Catch { 108 struct FatalConditionHandler { resetCatch::FatalConditionHandler109 void reset() {} 110 }; 111 } 112 113 114 # else // CATCH_CONFIG_POSIX_SIGNALS is defined 115 116 #include <signal.h> 117 118 namespace Catch { 119 120 struct SignalDefs { 121 int id; 122 const char* name; 123 }; 124 extern SignalDefs signalDefs[]; 125 SignalDefs signalDefs[] = { 126 { SIGINT, "SIGINT - Terminal interrupt signal" }, 127 { SIGILL, "SIGILL - Illegal instruction signal" }, 128 { SIGFPE, "SIGFPE - Floating point error signal" }, 129 { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, 130 { SIGTERM, "SIGTERM - Termination request signal" }, 131 { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } 132 }; 133 134 struct FatalConditionHandler { 135 136 static bool isSet; 137 static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)]; 138 static stack_t oldSigStack; 139 static char altStackMem[SIGSTKSZ]; 140 handleSignalCatch::FatalConditionHandler141 static void handleSignal( int sig ) { 142 std::string name = "<unknown signal>"; 143 for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { 144 SignalDefs &def = signalDefs[i]; 145 if (sig == def.id) { 146 name = def.name; 147 break; 148 } 149 } 150 reset(); 151 reportFatal(name); 152 raise( sig ); 153 } 154 FatalConditionHandlerCatch::FatalConditionHandler155 FatalConditionHandler() { 156 isSet = true; 157 stack_t sigStack; 158 sigStack.ss_sp = altStackMem; 159 sigStack.ss_size = SIGSTKSZ; 160 sigStack.ss_flags = 0; 161 sigaltstack(&sigStack, &oldSigStack); 162 struct sigaction sa = { 0 }; 163 164 sa.sa_handler = handleSignal; 165 sa.sa_flags = SA_ONSTACK; 166 for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { 167 sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); 168 } 169 } 170 171 ~FatalConditionHandlerCatch::FatalConditionHandler172 ~FatalConditionHandler() { 173 reset(); 174 } resetCatch::FatalConditionHandler175 static void reset() { 176 if( isSet ) { 177 // Set signals back to previous values -- hopefully nobody overwrote them in the meantime 178 for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { 179 sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL); 180 } 181 // Return the old stack 182 sigaltstack(&oldSigStack, CATCH_NULL); 183 isSet = false; 184 } 185 } 186 }; 187 188 bool FatalConditionHandler::isSet = false; 189 struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; 190 stack_t FatalConditionHandler::oldSigStack = {}; 191 char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; 192 193 194 } // namespace Catch 195 196 # endif // CATCH_CONFIG_POSIX_SIGNALS 197 198 #endif // not Windows 199 200 #endif // TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED 201