mirror of
https://github.com/catchorg/Catch2.git
synced 2024-11-24 06:16:10 +01:00
Backport fix for SIGSTKSZ
This commit is contained in:
parent
72df457bab
commit
6c6cfe126a
@ -9,10 +9,12 @@
|
|||||||
#ifndef TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
|
#ifndef TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
|
||||||
#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
|
#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
|
||||||
// Report the error condition
|
//! Signals fatal error message to the run context
|
||||||
inline void reportFatal( std::string const& message ) {
|
inline void reportFatal( std::string const& message ) {
|
||||||
IContext& context = Catch::getCurrentContext();
|
IContext& context = Catch::getCurrentContext();
|
||||||
IResultCapture* resultCapture = context.getResultCapture();
|
IResultCapture* resultCapture = context.getResultCapture();
|
||||||
@ -27,8 +29,31 @@ namespace Catch {
|
|||||||
# if !defined ( CATCH_CONFIG_WINDOWS_SEH )
|
# if !defined ( CATCH_CONFIG_WINDOWS_SEH )
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
struct FatalConditionHandler {
|
class FatalConditionHandler {
|
||||||
void reset() {}
|
bool m_started = false;
|
||||||
|
|
||||||
|
// Install/disengage implementation for specific platform.
|
||||||
|
// Should be if-defed to work on current platform, can assume
|
||||||
|
// engage-disengage 1:1 pairing.
|
||||||
|
void engage_platform() {}
|
||||||
|
void disengage_platform() {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Should also have platform-specific implementations as needed
|
||||||
|
FatalConditionHandler() {}
|
||||||
|
~FatalConditionHandler() {}
|
||||||
|
|
||||||
|
void engage() {
|
||||||
|
assert(!m_started && "Handler cannot be installed twice.");
|
||||||
|
m_started = true;
|
||||||
|
engage_platform();
|
||||||
|
}
|
||||||
|
|
||||||
|
void disengage() {
|
||||||
|
assert(m_started && "Handler cannot be uninstalled without being installed first");
|
||||||
|
m_started = false;
|
||||||
|
disengage_platform();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,8 +73,6 @@ namespace Catch {
|
|||||||
{ EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
|
{ EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FatalConditionHandler {
|
|
||||||
|
|
||||||
static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
|
static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
|
||||||
for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
|
for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
|
||||||
if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) {
|
if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) {
|
||||||
@ -61,41 +84,62 @@ namespace Catch {
|
|||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
FatalConditionHandler() {
|
// Since we do not support multiple instantiations, we put these
|
||||||
isSet = true;
|
// into global variables and rely on cleaning them up in outlined
|
||||||
// 32k seems enough for Catch to handle stack overflow,
|
// constructors/destructors
|
||||||
// but the value was found experimentally, so there is no strong guarantee
|
static PVOID exceptionHandlerHandle = CATCH_NULL;
|
||||||
guaranteeSize = 32 * 1024;
|
|
||||||
exceptionHandlerHandle = CATCH_NULL;
|
class FatalConditionHandler {
|
||||||
|
bool m_started = false;
|
||||||
|
|
||||||
|
// Install/disengage implementation for specific platform.
|
||||||
|
// Should be if-defed to work on current platform, can assume
|
||||||
|
// engage-disengage 1:1 pairing.
|
||||||
|
|
||||||
|
void engage_platform() {
|
||||||
// Register as first handler in current chain
|
// Register as first handler in current chain
|
||||||
exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
|
exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
|
||||||
// Pass in guarantee size to be filled
|
if (!exceptionHandlerHandle) {
|
||||||
SetThreadStackGuarantee(&guaranteeSize);
|
throw std::runtime_error("Could not register vectored exception handler");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void reset() {
|
void disengage_platform() {
|
||||||
if (isSet) {
|
if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) {
|
||||||
// Unregister handler and restore the old guarantee
|
throw std::runtime_error("Could not unregister vectored exception handler");
|
||||||
RemoveVectoredExceptionHandler(exceptionHandlerHandle);
|
}
|
||||||
SetThreadStackGuarantee(&guaranteeSize);
|
|
||||||
exceptionHandlerHandle = CATCH_NULL;
|
exceptionHandlerHandle = CATCH_NULL;
|
||||||
isSet = false;
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
FatalConditionHandler() {
|
||||||
|
ULONG guaranteeSize = static_cast<ULONG>(32 * 1024);
|
||||||
|
if (!SetThreadStackGuarantee(&guaranteeSize)) {
|
||||||
|
// We do not want to fully error out, because needing
|
||||||
|
// the stack reserve should be rare enough anyway.
|
||||||
|
Catch::cerr()
|
||||||
|
<< "Failed to reserve piece of stack."
|
||||||
|
<< " Stack overflows will not be reported successfully.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~FatalConditionHandler() {
|
// We do not attempt to unset the stack guarantee, because
|
||||||
reset();
|
// Windows does not support lowering the stack size guarantee.
|
||||||
|
~FatalConditionHandler() {}
|
||||||
|
|
||||||
|
void engage() {
|
||||||
|
assert(!m_started && "Handler cannot be installed twice.");
|
||||||
|
m_started = true;
|
||||||
|
engage_platform();
|
||||||
|
}
|
||||||
|
|
||||||
|
void disengage() {
|
||||||
|
assert(m_started && "Handler cannot be uninstalled without being installed first");
|
||||||
|
m_started = false;
|
||||||
|
disengage_platform();
|
||||||
}
|
}
|
||||||
private:
|
|
||||||
static bool isSet;
|
|
||||||
static ULONG guaranteeSize;
|
|
||||||
static PVOID exceptionHandlerHandle;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool FatalConditionHandler::isSet = false;
|
|
||||||
ULONG FatalConditionHandler::guaranteeSize = 0;
|
|
||||||
PVOID FatalConditionHandler::exceptionHandlerHandle = CATCH_NULL;
|
|
||||||
|
|
||||||
} // namespace Catch
|
} // namespace Catch
|
||||||
|
|
||||||
# endif // CATCH_CONFIG_WINDOWS_SEH
|
# endif // CATCH_CONFIG_WINDOWS_SEH
|
||||||
@ -105,8 +149,31 @@ namespace Catch {
|
|||||||
# if !defined(CATCH_CONFIG_POSIX_SIGNALS)
|
# if !defined(CATCH_CONFIG_POSIX_SIGNALS)
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
struct FatalConditionHandler {
|
class FatalConditionHandler {
|
||||||
void reset() {}
|
bool m_started = false;
|
||||||
|
|
||||||
|
// Install/disengage implementation for specific platform.
|
||||||
|
// Should be if-defed to work on current platform, can assume
|
||||||
|
// engage-disengage 1:1 pairing.
|
||||||
|
void engage_platform() {}
|
||||||
|
void disengage_platform() {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Should also have platform-specific implementations as needed
|
||||||
|
FatalConditionHandler() {}
|
||||||
|
~FatalConditionHandler() {}
|
||||||
|
|
||||||
|
void engage() {
|
||||||
|
assert(!m_started && "Handler cannot be installed twice.");
|
||||||
|
m_started = true;
|
||||||
|
engage_platform();
|
||||||
|
}
|
||||||
|
|
||||||
|
void disengage() {
|
||||||
|
assert(m_started && "Handler cannot be uninstalled without being installed first");
|
||||||
|
m_started = false;
|
||||||
|
disengage_platform();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,15 +198,33 @@ namespace Catch {
|
|||||||
{ SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
|
{ SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FatalConditionHandler {
|
// Older GCCs trigger -Wmissing-field-initializers for T foo = {}
|
||||||
|
// which is zero initialization, but not explicit. We want to avoid
|
||||||
|
// that.
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
# pragma GCC diagnostic push
|
||||||
|
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||||
|
#endif
|
||||||
|
|
||||||
static bool isSet;
|
static char* altStackMem = CATCH_NULL;
|
||||||
static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)];
|
static std::size_t altStackSize = 0;
|
||||||
static stack_t oldSigStack;
|
static stack_t oldSigStack;
|
||||||
static char altStackMem[SIGSTKSZ];
|
static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)];
|
||||||
|
|
||||||
|
static void restorePreviousSignalHandlers() {
|
||||||
|
// We set signal handlers back to the previous ones. Hopefully
|
||||||
|
// nobody overwrote them in the meantime, and doesn't expect
|
||||||
|
// their signal handlers to live past ours given that they
|
||||||
|
// installed them after ours..
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
static void handleSignal( int sig ) {
|
static void handleSignal( int sig ) {
|
||||||
std::string name = "<unknown signal>";
|
char const * name = "<unknown signal>";
|
||||||
for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
|
for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
|
||||||
SignalDefs &def = signalDefs[i];
|
SignalDefs &def = signalDefs[i];
|
||||||
if (sig == def.id) {
|
if (sig == def.id) {
|
||||||
@ -147,13 +232,22 @@ namespace Catch {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reset();
|
// We need to restore previous signal handlers and let them do
|
||||||
reportFatal(name);
|
// their thing, so that the users can have the debugger break
|
||||||
|
// when a signal is raised, and so on.
|
||||||
|
restorePreviousSignalHandlers();
|
||||||
|
reportFatal( name );
|
||||||
raise( sig );
|
raise( sig );
|
||||||
}
|
}
|
||||||
|
|
||||||
FatalConditionHandler() {
|
class FatalConditionHandler {
|
||||||
isSet = true;
|
bool m_started = false;
|
||||||
|
|
||||||
|
// Install/disengage implementation for specific platform.
|
||||||
|
// Should be if-defed to work on current platform, can assume
|
||||||
|
// engage-disengage 1:1 pairing.
|
||||||
|
|
||||||
|
void engage_platform() {
|
||||||
stack_t sigStack;
|
stack_t sigStack;
|
||||||
sigStack.ss_sp = altStackMem;
|
sigStack.ss_sp = altStackMem;
|
||||||
sigStack.ss_size = SIGSTKSZ;
|
sigStack.ss_size = SIGSTKSZ;
|
||||||
@ -168,28 +262,42 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void disengage_platform() {
|
||||||
|
restorePreviousSignalHandlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
FatalConditionHandler() {
|
||||||
|
assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists");
|
||||||
|
if (altStackSize == 0) {
|
||||||
|
altStackSize = SIGSTKSZ;
|
||||||
|
}
|
||||||
|
altStackMem = new char[altStackSize]();
|
||||||
|
}
|
||||||
|
|
||||||
~FatalConditionHandler() {
|
~FatalConditionHandler() {
|
||||||
reset();
|
delete[] altStackMem;
|
||||||
|
// We signal that another instance can be constructed by zeroing
|
||||||
|
// out the pointer.
|
||||||
|
altStackMem = CATCH_NULL;
|
||||||
}
|
}
|
||||||
static void reset() {
|
|
||||||
if( isSet ) {
|
void engage() {
|
||||||
// Set signals back to previous values -- hopefully nobody overwrote them in the meantime
|
assert(!m_started && "Handler cannot be installed twice.");
|
||||||
for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) {
|
m_started = true;
|
||||||
sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL);
|
engage_platform();
|
||||||
}
|
|
||||||
// Return the old stack
|
|
||||||
sigaltstack(&oldSigStack, CATCH_NULL);
|
|
||||||
isSet = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void disengage() {
|
||||||
|
assert(m_started && "Handler cannot be uninstalled without being installed first");
|
||||||
|
m_started = false;
|
||||||
|
disengage_platform();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool FatalConditionHandler::isSet = false;
|
#if defined(__GNUC__)
|
||||||
struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
|
# pragma GCC diagnostic pop
|
||||||
stack_t FatalConditionHandler::oldSigStack = {};
|
#endif
|
||||||
char FatalConditionHandler::altStackMem[SIGSTKSZ] = {};
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace Catch
|
} // namespace Catch
|
||||||
|
|
||||||
@ -197,4 +305,21 @@ namespace Catch {
|
|||||||
|
|
||||||
#endif // not Windows
|
#endif // not Windows
|
||||||
|
|
||||||
|
namespace Catch {
|
||||||
|
|
||||||
|
//! Simple RAII guard for (dis)engaging the FatalConditionHandler
|
||||||
|
class FatalConditionHandlerGuard {
|
||||||
|
FatalConditionHandler* m_handler;
|
||||||
|
public:
|
||||||
|
FatalConditionHandlerGuard(FatalConditionHandler* handler):
|
||||||
|
m_handler(handler) {
|
||||||
|
m_handler->engage();
|
||||||
|
}
|
||||||
|
~FatalConditionHandlerGuard() {
|
||||||
|
m_handler->disengage();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // end namespace Catch
|
||||||
|
|
||||||
#endif // TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
|
#endif // TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
|
||||||
|
@ -363,9 +363,8 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void invokeActiveTestCase() {
|
void invokeActiveTestCase() {
|
||||||
FatalConditionHandler fatalConditionHandler; // Handle signals
|
FatalConditionHandlerGuard _(&m_fatalConditionhandler);
|
||||||
m_activeTestCase->invoke();
|
m_activeTestCase->invoke();
|
||||||
fatalConditionHandler.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -403,6 +402,7 @@ namespace Catch {
|
|||||||
std::vector<SectionEndInfo> m_unfinishedSections;
|
std::vector<SectionEndInfo> m_unfinishedSections;
|
||||||
std::vector<ITracker*> m_activeSections;
|
std::vector<ITracker*> m_activeSections;
|
||||||
TrackerContext m_trackerContext;
|
TrackerContext m_trackerContext;
|
||||||
|
FatalConditionHandler m_fatalConditionhandler;
|
||||||
size_t m_prevPassed;
|
size_t m_prevPassed;
|
||||||
bool m_shouldReportUnexpected;
|
bool m_shouldReportUnexpected;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user