mirror of https://github.com/catchorg/Catch2.git
Merge branch 'dev-signals'
This commit is contained in:
commit
b1171bd1f2
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue