Merge branch 'dev-signals'

This commit is contained in:
Martin Hořeňovský 2017-01-25 22:09:52 +01:00
commit b1171bd1f2

View File

@ -12,24 +12,75 @@
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" },
@ -53,31 +107,64 @@ namespace Catch {
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
#endif // not Windows #endif // not Windows