2014-08-22 09:07:39 +02:00
|
|
|
/*
|
|
|
|
* Created by Phil on 21/08/2014
|
|
|
|
* Copyright 2014 Two Blue Cubes Ltd. All rights reserved.
|
|
|
|
*
|
|
|
|
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
|
|
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
#ifndef TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
|
|
|
|
#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
|
|
|
|
|
|
|
|
|
|
|
|
namespace Catch {
|
|
|
|
|
2017-01-12 09:44:00 +01:00
|
|
|
// Report the error condition
|
2017-01-27 09:32:58 +01:00
|
|
|
inline void reportFatal( std::string const& message ) {
|
2014-08-22 09:07:39 +02:00
|
|
|
IContext& context = Catch::getCurrentContext();
|
|
|
|
IResultCapture* resultCapture = context.getResultCapture();
|
2014-08-22 20:33:28 +02:00
|
|
|
resultCapture->handleFatalErrorCondition( message );
|
2014-08-22 09:07:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Catch
|
|
|
|
|
|
|
|
#if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
|
2017-02-06 01:43:53 +01:00
|
|
|
#include "catch_windows_h_proxy.h"
|
2014-08-22 09:07:39 +02:00
|
|
|
|
2017-02-06 01:43:53 +01:00
|
|
|
# if !defined ( CATCH_CONFIG_WINDOWS_SEH )
|
2017-02-03 14:09:17 +01:00
|
|
|
|
2017-02-06 01:43:53 +01:00
|
|
|
namespace Catch {
|
|
|
|
struct FatalConditionHandler {
|
|
|
|
void reset() {}
|
|
|
|
};
|
|
|
|
}
|
2016-12-16 15:00:19 +01:00
|
|
|
|
2017-02-06 01:43:53 +01:00
|
|
|
# else // CATCH_CONFIG_WINDOWS_SEH is defined
|
2016-12-16 15:00:19 +01:00
|
|
|
|
2014-08-22 09:07:39 +02:00
|
|
|
namespace Catch {
|
|
|
|
|
2016-12-16 15:00:19 +01:00
|
|
|
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" },
|
|
|
|
};
|
|
|
|
|
2014-10-21 08:24:30 +02:00
|
|
|
struct FatalConditionHandler {
|
2016-12-16 15:00:19 +01:00
|
|
|
|
|
|
|
static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
|
|
|
|
for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
|
|
|
|
if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) {
|
2017-01-27 09:32:58 +01:00
|
|
|
reportFatal(signalDefs[i].name);
|
2016-12-16 15:00:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// If its not an exception we care about, pass it along.
|
|
|
|
// This stops us from eating debugger breaks etc.
|
|
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
|
|
}
|
|
|
|
|
2017-02-06 20:40:46 +01:00
|
|
|
FatalConditionHandler() {
|
|
|
|
isSet = true;
|
|
|
|
// 32k seems enough for Catch to handle stack overflow,
|
|
|
|
// but the value was found experimentally, so there is no strong guarantee
|
|
|
|
guaranteeSize = 32 * 1024;
|
2017-04-25 12:41:30 +02:00
|
|
|
exceptionHandlerHandle = nullptr;
|
2016-12-16 15:00:19 +01:00
|
|
|
// Register as first handler in current chain
|
2017-02-06 20:40:46 +01:00
|
|
|
exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
|
2016-12-16 15:00:19 +01:00
|
|
|
// Pass in guarantee size to be filled
|
2017-02-06 20:40:46 +01:00
|
|
|
SetThreadStackGuarantee(&guaranteeSize);
|
2016-12-16 15:00:19 +01:00
|
|
|
}
|
|
|
|
|
2017-02-06 20:40:46 +01:00
|
|
|
static void reset() {
|
|
|
|
if (isSet) {
|
2016-12-16 15:00:19 +01:00
|
|
|
// Unregister handler and restore the old guarantee
|
2017-02-06 20:40:46 +01:00
|
|
|
RemoveVectoredExceptionHandler(exceptionHandlerHandle);
|
|
|
|
SetThreadStackGuarantee(&guaranteeSize);
|
2017-04-25 12:41:30 +02:00
|
|
|
exceptionHandlerHandle = nullptr;
|
2017-02-06 20:40:46 +01:00
|
|
|
isSet = false;
|
2016-12-16 15:00:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
~FatalConditionHandler() {
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
private:
|
2017-02-06 20:40:46 +01:00
|
|
|
static bool isSet;
|
|
|
|
static ULONG guaranteeSize;
|
|
|
|
static PVOID exceptionHandlerHandle;
|
2016-12-16 15:00:19 +01:00
|
|
|
};
|
2014-08-22 09:07:39 +02:00
|
|
|
|
2017-02-06 20:40:46 +01:00
|
|
|
bool FatalConditionHandler::isSet = false;
|
|
|
|
ULONG FatalConditionHandler::guaranteeSize = 0;
|
2017-04-25 12:41:30 +02:00
|
|
|
PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr;
|
2017-02-06 20:40:46 +01:00
|
|
|
|
2014-08-22 09:07:39 +02:00
|
|
|
} // namespace Catch
|
|
|
|
|
2017-02-06 01:43:53 +01:00
|
|
|
# endif // CATCH_CONFIG_WINDOWS_SEH
|
|
|
|
|
2014-08-22 09:07:39 +02:00
|
|
|
#else // Not Windows - assumed to be POSIX compatible //////////////////////////
|
|
|
|
|
2017-02-15 17:57:22 +01:00
|
|
|
# if !defined(CATCH_CONFIG_POSIX_SIGNALS)
|
|
|
|
|
|
|
|
namespace Catch {
|
|
|
|
struct FatalConditionHandler {
|
|
|
|
void reset() {}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# else // CATCH_CONFIG_POSIX_SIGNALS is defined
|
|
|
|
|
2014-08-22 09:07:39 +02:00
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
namespace Catch {
|
|
|
|
|
2017-01-12 09:44:00 +01:00
|
|
|
struct SignalDefs {
|
|
|
|
int id;
|
|
|
|
const char* name;
|
|
|
|
};
|
2014-08-22 09:07:39 +02:00
|
|
|
extern SignalDefs signalDefs[];
|
|
|
|
SignalDefs signalDefs[] = {
|
|
|
|
{ SIGINT, "SIGINT - Terminal interrupt signal" },
|
|
|
|
{ SIGILL, "SIGILL - Illegal instruction signal" },
|
|
|
|
{ SIGFPE, "SIGFPE - Floating point error signal" },
|
|
|
|
{ SIGSEGV, "SIGSEGV - Segmentation violation signal" },
|
|
|
|
{ SIGTERM, "SIGTERM - Termination request signal" },
|
|
|
|
{ SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
|
2017-01-12 09:44:00 +01:00
|
|
|
};
|
2014-08-22 09:07:39 +02:00
|
|
|
|
|
|
|
struct FatalConditionHandler {
|
|
|
|
|
2017-01-12 09:44:00 +01:00
|
|
|
static bool isSet;
|
|
|
|
static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)];
|
|
|
|
static stack_t oldSigStack;
|
|
|
|
static char altStackMem[SIGSTKSZ];
|
2017-01-26 23:13:12 +01:00
|
|
|
|
2014-08-22 09:07:39 +02:00
|
|
|
static void handleSignal( int sig ) {
|
2017-01-12 09:44:00 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2017-01-14 15:21:44 +01:00
|
|
|
reset();
|
2017-01-27 09:32:58 +01:00
|
|
|
reportFatal(name);
|
2017-01-12 09:44:00 +01:00
|
|
|
raise( sig );
|
2014-08-22 09:07:39 +02:00
|
|
|
}
|
|
|
|
|
2017-01-12 09:44:00 +01:00
|
|
|
FatalConditionHandler() {
|
|
|
|
isSet = true;
|
2016-11-26 14:10:11 +01:00
|
|
|
stack_t sigStack;
|
2017-01-12 09:44:00 +01:00
|
|
|
sigStack.ss_sp = altStackMem;
|
2016-11-26 14:10:11 +01:00
|
|
|
sigStack.ss_size = SIGSTKSZ;
|
|
|
|
sigStack.ss_flags = 0;
|
2017-01-12 09:44:00 +01:00
|
|
|
sigaltstack(&sigStack, &oldSigStack);
|
2016-11-26 14:10:11 +01:00
|
|
|
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) {
|
2017-01-12 09:44:00 +01:00
|
|
|
sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
|
2016-11-26 14:10:11 +01:00
|
|
|
}
|
2014-08-22 09:07:39 +02:00
|
|
|
}
|
2017-01-26 23:13:12 +01:00
|
|
|
|
|
|
|
|
2014-08-22 09:07:39 +02:00
|
|
|
~FatalConditionHandler() {
|
2014-10-02 19:28:45 +02:00
|
|
|
reset();
|
|
|
|
}
|
2017-01-14 15:21:44 +01:00
|
|
|
static void reset() {
|
2017-01-12 09:44:00 +01:00
|
|
|
if( isSet ) {
|
2016-11-26 14:10:11 +01:00
|
|
|
// 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 ) {
|
2017-04-25 12:41:30 +02:00
|
|
|
sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
|
2016-11-26 14:10:11 +01:00
|
|
|
}
|
|
|
|
// Return the old stack
|
2017-04-25 12:41:30 +02:00
|
|
|
sigaltstack(&oldSigStack, nullptr);
|
2017-01-12 09:44:00 +01:00
|
|
|
isSet = false;
|
2014-10-02 19:28:45 +02:00
|
|
|
}
|
2014-08-22 09:07:39 +02:00
|
|
|
}
|
|
|
|
};
|
2017-01-26 23:13:12 +01:00
|
|
|
|
2017-01-12 09:44:00 +01:00
|
|
|
bool FatalConditionHandler::isSet = false;
|
2017-01-14 15:08:00 +01:00
|
|
|
struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
|
2017-01-12 09:44:00 +01:00
|
|
|
stack_t FatalConditionHandler::oldSigStack = {};
|
|
|
|
char FatalConditionHandler::altStackMem[SIGSTKSZ] = {};
|
2017-01-26 23:13:12 +01:00
|
|
|
|
2014-08-22 09:07:39 +02:00
|
|
|
|
|
|
|
} // namespace Catch
|
|
|
|
|
2017-02-15 17:57:22 +01:00
|
|
|
# endif // CATCH_CONFIG_POSIX_SIGNALS
|
|
|
|
|
2014-08-22 09:07:39 +02:00
|
|
|
#endif // not Windows
|
|
|
|
|
|
|
|
#endif // TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
|