Update catch.hpp (fixes #1856)

See also catchorg/Catch2#2627
This commit is contained in:
Benoit Blanchon
2023-01-17 09:33:19 +01:00
parent dd0f7019ef
commit bf93779b4f

View File

@ -1,6 +1,6 @@
/* /*
* Catch v1.12.2 * Catch v1.12.2
* Generated: 2018-05-14 15:10:01.112442 * Generated: 2023-01-17 08:45:40.979381
* ---------------------------------------------------------- * ----------------------------------------------------------
* This file has been merged from multiple headers. Please don't edit it directly * This file has been merged from multiple headers. Please don't edit it directly
* Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
@ -214,7 +214,7 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Use variadic macros if the compiler supports them // Use variadic macros if the compiler supports them
#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ #if ( defined _MSC_VER && _MSC_VER >= 1400 && !defined __EDGE__) || \
( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \
( defined __GNUC__ && __GNUC__ >= 3 ) || \ ( defined __GNUC__ && __GNUC__ >= 3 ) || \
( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L )
@ -2129,6 +2129,9 @@ namespace Catch{
#define CATCH_TRAP() \ #define CATCH_TRAP() \
__asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
: : : "memory","r0","r3","r4" ) /* NOLINT */ : : : "memory","r0","r3","r4" ) /* NOLINT */
#elif defined(__aarch64__)
// Backport of https://github.com/catchorg/Catch2/commit/a25c1a24af8bffd35727a888a307ff0280cf9387
#define CATCH_TRAP() __asm__(".inst 0xd4200000")
#else #else
#define CATCH_TRAP() __asm__("int $3\n" : : /* NOLINT */ ) #define CATCH_TRAP() __asm__("int $3\n" : : /* NOLINT */ )
#endif #endif
@ -6392,18 +6395,21 @@ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
// #included from: catch_fatal_condition.hpp // #included from: catch_fatal_condition.hpp
#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();
resultCapture->handleFatalErrorCondition( message ); resultCapture->handleFatalErrorCondition(message);
} }
} // namespace Catch } // namespace Catch
#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// #if defined(CATCH_PLATFORM_WINDOWS) /////////////////////////////////////////
// #included from: catch_windows_h_proxy.h // #included from: catch_windows_h_proxy.h
#define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED #define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED
@ -6429,33 +6435,59 @@ namespace Catch {
#endif #endif
# 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;
};
}
# else // CATCH_CONFIG_WINDOWS_SEH is defined // 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() : m_started(false) {}
~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();
}
};
} // namespace Catch
#else // CATCH_CONFIG_WINDOWS_SEH is defined
namespace Catch { namespace Catch {
struct SignalDefs { DWORD id; const char* name; }; struct SignalDefs {
extern SignalDefs signalDefs[]; DWORD id;
// There is no 1-1 mapping between signals and windows exceptions. const char *name;
// Windows can easily distinguish between SO and SigSegV, };
// but SigInt, SigTerm, etc are handled differently. extern SignalDefs signalDefs[];
SignalDefs signalDefs[] = { // There is no 1-1 mapping between signals and windows exceptions.
{ EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, // Windows can easily distinguish between SO and SigSegV,
{ EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, // but SigInt, SigTerm, etc are handled differently.
{ EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, SignalDefs signalDefs[] = {
{ EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, {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 { 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) {
reportFatal(signalDefs[i].name); reportFatal(signalDefs[i].name);
@ -6464,86 +6496,150 @@ namespace Catch {
// If its not an exception we care about, pass it along. // If its not an exception we care about, pass it along.
// This stops us from eating debugger breaks etc. // This stops us from eating debugger breaks etc.
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;
// 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 =
// Pass in guarantee size to be filled AddVectoredExceptionHandler(1, handleVectoredException);
SetThreadStackGuarantee(&guaranteeSize); if (!exceptionHandlerHandle) {
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(
RemoveVectoredExceptionHandler(exceptionHandlerHandle); "Could not unregister vectored exception handler");
SetThreadStackGuarantee(&guaranteeSize); }
exceptionHandlerHandle = CATCH_NULL; exceptionHandlerHandle = CATCH_NULL;
isSet = false; }
public:
FatalConditionHandler() : m_started(false) {
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() {}
private:
static bool isSet;
static ULONG guaranteeSize;
static PVOID exceptionHandlerHandle;
};
bool FatalConditionHandler::isSet = false; void engage() {
ULONG FatalConditionHandler::guaranteeSize = 0; assert(!m_started && "Handler cannot be installed twice.");
PVOID FatalConditionHandler::exceptionHandlerHandle = CATCH_NULL; m_started = true;
engage_platform();
}
void disengage() {
assert(m_started &&
"Handler cannot be uninstalled without being installed first");
m_started = false;
disengage_platform();
}
};
} // namespace Catch } // namespace Catch
# endif // CATCH_CONFIG_WINDOWS_SEH #endif // CATCH_CONFIG_WINDOWS_SEH
#else // Not Windows - assumed to be POSIX compatible ////////////////////////// #else // Not Windows - assumed to be POSIX compatible //////////////////////////
# 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;
};
}
# else // CATCH_CONFIG_POSIX_SIGNALS is defined // 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() : m_started(false) {}
~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();
}
};
} // namespace Catch
#else // CATCH_CONFIG_POSIX_SIGNALS is defined
#include <signal.h> #include <signal.h>
namespace Catch { namespace Catch {
struct SignalDefs { struct SignalDefs {
int id; int id;
const char* name; const char *name;
}; };
extern SignalDefs signalDefs[]; extern SignalDefs signalDefs[];
SignalDefs signalDefs[] = { SignalDefs signalDefs[] = {
{ SIGINT, "SIGINT - Terminal interrupt signal" }, {SIGINT, "SIGINT - Terminal interrupt signal"},
{ SIGILL, "SIGILL - Illegal instruction signal" }, {SIGILL, "SIGILL - Illegal instruction signal"},
{ SIGFPE, "SIGFPE - Floating point error signal" }, {SIGFPE, "SIGFPE - Floating point error signal"},
{ SIGSEGV, "SIGSEGV - Segmentation violation signal" }, {SIGSEGV, "SIGSEGV - Segmentation violation signal"},
{ SIGTERM, "SIGTERM - Termination request signal" }, {SIGTERM, "SIGTERM - Termination request signal"},
{ 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 handleSignal( int sig ) { static void restorePreviousSignalHandlers() {
std::string name = "<unknown signal>"; // 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) {
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) {
@ -6551,54 +6647,95 @@ namespace Catch {
break; break;
} }
} }
reset(); // We need to restore previous signal handlers and let them do
// their thing, so that the users can have the debugger break
// when a signal is raised, and so on.
restorePreviousSignalHandlers();
reportFatal(name); reportFatal(name);
raise( sig ); raise(sig);
} }
FatalConditionHandler() { class FatalConditionHandler {
isSet = true; bool m_started;
// 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;
sigStack.ss_flags = 0; sigStack.ss_flags = 0;
sigaltstack(&sigStack, &oldSigStack); sigaltstack(&sigStack, &oldSigStack);
struct sigaction sa = { 0 }; struct sigaction sa = {0};
sa.sa_handler = handleSignal; sa.sa_handler = handleSignal;
sa.sa_flags = SA_ONSTACK; sa.sa_flags = SA_ONSTACK;
for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
} }
} }
~FatalConditionHandler() { void disengage_platform() { restorePreviousSignalHandlers(); }
reset();
}
static void reset() {
if( isSet ) {
// 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 ) {
sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL);
}
// Return the old stack
sigaltstack(&oldSigStack, CATCH_NULL);
isSet = false;
}
}
};
bool FatalConditionHandler::isSet = false; public:
struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; FatalConditionHandler() : m_started(false) {
stack_t FatalConditionHandler::oldSigStack = {}; assert(!altStackMem &&
char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; "Cannot initialize POSIX signal handler when one already exists");
if (altStackSize == 0) {
altStackSize = SIGSTKSZ;
}
altStackMem = new char[altStackSize]();
}
~FatalConditionHandler() {
delete[] altStackMem;
// We signal that another instance can be constructed by zeroing
// out the pointer.
altStackMem = CATCH_NULL;
}
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();
}
};
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
} // namespace Catch } // namespace Catch
# endif // CATCH_CONFIG_POSIX_SIGNALS #endif // CATCH_CONFIG_POSIX_SIGNALS
#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
#include <cassert> #include <cassert>
#include <set> #include <set>
#include <string> #include <string>
@ -6938,9 +7075,8 @@ namespace Catch {
} }
void invokeActiveTestCase() { void invokeActiveTestCase() {
FatalConditionHandler fatalConditionHandler; // Handle signals FatalConditionHandlerGuard _(&m_fatalConditionhandler);
m_activeTestCase->invoke(); m_activeTestCase->invoke();
fatalConditionHandler.reset();
} }
private: private:
@ -6978,6 +7114,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;
}; };