1616#define CATCH_VERSION_MAJOR 2
1717#define CATCH_VERSION_MINOR 13
1818#define CATCH_VERSION_PATCH 4
19+
20+ /*
21+ Note: includes changes from patch 8 needed for when MINSIGSTKSZ is no longer a
22+ constexpr.
23+ */
1924
2025#ifdef __clang__
2126# pragma clang system_header
6772// start catch_platform.h
6873
6974#ifdef __APPLE__
70- # include <TargetConditionals.h>
75+ # include <TargetConditionals.h>
7176# if TARGET_OS_OSX == 1
72- # define CATCH_PLATFORM_MAC
77+ # define CATCH_PLATFORM_MAC
7378# elif TARGET_OS_IPHONE == 1
74- # define CATCH_PLATFORM_IPHONE
75- # endif
79+ # define CATCH_PLATFORM_IPHONE
80+ # endif
7681
7782#elif defined(linux) || defined(__linux) || defined(__linux__)
7883# define CATCH_PLATFORM_LINUX
@@ -6339,8 +6344,8 @@ namespace Catch {
63396344
63406345 void writeTestCase(TestCaseNode const& testCaseNode);
63416346
6342- void writeSection(std::string const& className,
6343- std::string const& rootName,
6347+ void writeSection( std::string const& className,
6348+ std::string const& rootName,
63446349 SectionNode const& sectionNode);
63456350
63466351 void writeAssertions(SectionNode const& sectionNode);
@@ -7980,86 +7985,58 @@ namespace Catch {
79807985
79817986// start catch_fatal_condition.h
79827987
7983- // start catch_windows_h_proxy.h
7984-
7985-
7986- #if defined(CATCH_PLATFORM_WINDOWS)
7987-
7988- #if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
7989- # define CATCH_DEFINED_NOMINMAX
7990- # define NOMINMAX
7991- #endif
7992- #if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
7993- # define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
7994- # define WIN32_LEAN_AND_MEAN
7995- #endif
7996-
7997- #ifdef __AFXDLL
7998- #include <AfxWin.h>
7999- #else
8000- #include <windows.h>
8001- #endif
8002-
8003- #ifdef CATCH_DEFINED_NOMINMAX
8004- # undef NOMINMAX
8005- #endif
8006- #ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN
8007- # undef WIN32_LEAN_AND_MEAN
8008- #endif
8009-
8010- #endif // defined(CATCH_PLATFORM_WINDOWS)
8011-
8012- // end catch_windows_h_proxy.h
8013- #if defined( CATCH_CONFIG_WINDOWS_SEH )
7988+ #include <cassert>
80147989
80157990namespace Catch {
80167991
8017- struct FatalConditionHandler {
8018-
8019- static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo);
7992+ // Wrapper for platform-specific fatal error (signals/SEH) handlers
7993+ //
7994+ // Tries to be cooperative with other handlers, and not step over
7995+ // other handlers. This means that unknown structured exceptions
7996+ // are passed on, previous signal handlers are called, and so on.
7997+ //
7998+ // Can only be instantiated once, and assumes that once a signal
7999+ // is caught, the binary will end up terminating. Thus, there
8000+ class FatalConditionHandler {
8001+ bool m_started = false;
8002+
8003+ // Install/disengage implementation for specific platform.
8004+ // Should be if-defed to work on current platform, can assume
8005+ // engage-disengage 1:1 pairing.
8006+ void engage_platform();
8007+ void disengage_platform();
8008+ public:
8009+ // Should also have platform-specific implementations as needed
80208010 FatalConditionHandler();
8021- static void reset();
80228011 ~FatalConditionHandler();
80238012
8024- private:
8025- static bool isSet;
8026- static ULONG guaranteeSize;
8027- static PVOID exceptionHandlerHandle;
8028- };
8029-
8030- } // namespace Catch
8031-
8032- #elif defined ( CATCH_CONFIG_POSIX_SIGNALS )
8033-
8034- #include <signal.h>
8035-
8036- namespace Catch {
8037-
8038- struct FatalConditionHandler {
8039-
8040- static bool isSet;
8041- static struct sigaction oldSigActions[];
8042- static stack_t oldSigStack;
8043- static char altStackMem[];
8044-
8045- static void handleSignal( int sig );
8013+ void engage() {
8014+ assert(!m_started && "Handler cannot be installed twice.");
8015+ m_started = true;
8016+ engage_platform();
8017+ }
80468018
8047- FatalConditionHandler();
8048- ~FatalConditionHandler();
8049- static void reset();
8019+ void disengage() {
8020+ assert(m_started && "Handler cannot be uninstalled without being installed first");
8021+ m_started = false;
8022+ disengage_platform();
8023+ }
80508024 };
80518025
8052- } // namespace Catch
8053-
8054- #else
8055-
8056- namespace Catch {
8057- struct FatalConditionHandler {
8058- void reset();
8026+ //! Simple RAII guard for (dis)engaging the FatalConditionHandler
8027+ class FatalConditionHandlerGuard {
8028+ FatalConditionHandler* m_handler;
8029+ public:
8030+ FatalConditionHandlerGuard(FatalConditionHandler* handler):
8031+ m_handler(handler) {
8032+ m_handler->engage();
8033+ }
8034+ ~FatalConditionHandlerGuard() {
8035+ m_handler->disengage();
8036+ }
80598037 };
8060- }
80618038
8062- #endif
8039+ } // end namespace Catch
80638040
80648041// end catch_fatal_condition.h
80658042#include <string>
@@ -10743,13 +10720,18 @@ namespace Catch {
1074310720#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS )
1074410721
1074510722namespace {
10746- // Report the error condition
10723+ //! Signals fatal error message to the run context
1074710724 void reportFatal( char const * const message ) {
1074810725 Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message );
1074910726 }
10750- }
1075110727
10752- #endif // signals/SEH handling
10728+ //! Minimal size Catch2 needs for its own fatal error handling.
10729+ //! Picked anecdotally, so it might not be sufficient on all
10730+ //! platforms, and for all configurations.
10731+ constexpr std::size_t minStackSizeForErrors = 32 * 1024;
10732+ } // end unnamed namespace
10733+
10734+ #endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS
1075310735
1075410736#if defined( CATCH_CONFIG_WINDOWS_SEH )
1075510737
@@ -10793,7 +10775,7 @@ namespace Catch {
1079310775 if (isSet) {
1079410776 RemoveVectoredExceptionHandler(exceptionHandlerHandle);
1079510777 SetThreadStackGuarantee(&guaranteeSize);
10796- exceptionHandlerHandle = nullptr;
10778+ exceptionHandlerHandle = nullptr;
1079710779 isSet = false;
1079810780 }
1079910781 }
@@ -10810,17 +10792,15 @@ PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr;
1081010792
1081110793#elif defined( CATCH_CONFIG_POSIX_SIGNALS )
1081210794
10795+ #include <signal.h>
10796+
1081310797namespace Catch {
1081410798
1081510799 struct SignalDefs {
1081610800 int id;
1081710801 const char* name;
1081810802 };
1081910803
10820- // 32kb for the alternate stack seems to be sufficient. However, this value
10821- // is experimentally determined, so that's not guaranteed.
10822- static constexpr std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
10823-
1082410804 static SignalDefs signalDefs[] = {
1082510805 { SIGINT, "SIGINT - Terminal interrupt signal" },
1082610806 { SIGILL, "SIGILL - Illegal instruction signal" },
@@ -10830,24 +10810,66 @@ namespace Catch {
1083010810 { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
1083110811 };
1083210812
10833- void FatalConditionHandler::handleSignal( int sig ) {
10813+ // Older GCCs trigger -Wmissing-field-initializers for T foo = {}
10814+ // which is zero initialization, but not explicit. We want to avoid
10815+ // that.
10816+ #if defined(__GNUC__)
10817+ # pragma GCC diagnostic push
10818+ # pragma GCC diagnostic ignored "-Wmissing-field-initializers"
10819+ #endif
10820+
10821+ static char* altStackMem = nullptr;
10822+ static std::size_t altStackSize = 0;
10823+ static stack_t oldSigStack{};
10824+ static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{};
10825+
10826+ static void restorePreviousSignalHandlers() {
10827+ // We set signal handlers back to the previous ones. Hopefully
10828+ // nobody overwrote them in the meantime, and doesn't expect
10829+ // their signal handlers to live past ours given that they
10830+ // installed them after ours..
10831+ for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
10832+ sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
10833+ }
10834+ // Return the old stack
10835+ sigaltstack(&oldSigStack, nullptr);
10836+ }
10837+
10838+ static void handleSignal( int sig ) {
1083410839 char const * name = "<unknown signal>";
1083510840 for (auto const& def : signalDefs) {
1083610841 if (sig == def.id) {
1083710842 name = def.name;
1083810843 break;
1083910844 }
1084010845 }
10841- reset();
10842- reportFatal(name);
10846+ // We need to restore previous signal handlers and let them do
10847+ // their thing, so that the users can have the debugger break
10848+ // when a signal is raised, and so on.
10849+ restorePreviousSignalHandlers();
10850+ reportFatal( name );
1084310851 raise( sig );
1084410852 }
1084510853
1084610854 FatalConditionHandler::FatalConditionHandler() {
10847- isSet = true;
10855+ assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists");
10856+ if (altStackSize == 0) {
10857+ altStackSize = std::max(static_cast<size_t>(SIGSTKSZ), minStackSizeForErrors);
10858+ }
10859+ altStackMem = new char[altStackSize]();
10860+ }
10861+
10862+ FatalConditionHandler::~FatalConditionHandler() {
10863+ delete[] altStackMem;
10864+ // We signal that another instance can be constructed by zeroing
10865+ // out the pointer.
10866+ altStackMem = nullptr;
10867+ }
10868+
10869+ void FatalConditionHandler::engage_platform() {
1084810870 stack_t sigStack;
1084910871 sigStack.ss_sp = altStackMem;
10850- sigStack.ss_size = sigStackSize ;
10872+ sigStack.ss_size = altStackSize ;
1085110873 sigStack.ss_flags = 0;
1085210874 sigaltstack(&sigStack, &oldSigStack);
1085310875 struct sigaction sa = { };
@@ -10859,40 +10881,17 @@ namespace Catch {
1085910881 }
1086010882 }
1086110883
10862- FatalConditionHandler::~FatalConditionHandler() {
10863- reset();
10864- }
10884+ #if defined(__GNUC__)
10885+ # pragma GCC diagnostic pop
10886+ #endif
1086510887
10866- void FatalConditionHandler::reset() {
10867- if( isSet ) {
10868- // Set signals back to previous values -- hopefully nobody overwrote them in the meantime
10869- for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) {
10870- sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
10871- }
10872- // Return the old stack
10873- sigaltstack(&oldSigStack, nullptr);
10874- isSet = false;
10875- }
10888+ void FatalConditionHandler::disengage_platform() {
10889+ restorePreviousSignalHandlers();
1087610890 }
1087710891
10878- bool FatalConditionHandler::isSet = false;
10879- struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
10880- stack_t FatalConditionHandler::oldSigStack = {};
10881- char FatalConditionHandler::altStackMem[sigStackSize] = {};
10882-
10883- } // namespace Catch
10884-
10885- #else
10886-
10887- namespace Catch {
10888- void FatalConditionHandler::reset() {}
10889- }
10890-
10891- #endif // signals/SEH handling
10892+ } // end namespace Catch
1089210893
10893- #if defined(__GNUC__)
10894- # pragma GCC diagnostic pop
10895- #endif
10894+ #endif // CATCH_CONFIG_POSIX_SIGNALS
1089610895// end catch_fatal_condition.cpp
1089710896// start catch_generators.cpp
1089810897
@@ -12957,7 +12956,7 @@ namespace Catch {
1295712956 void RunContext::invokeActiveTestCase() {
1295812957 FatalConditionHandler fatalConditionHandler; // Handle signals
1295912958 m_activeTestCase->invoke();
12960- fatalConditionHandler.reset ();
12959+ restorePreviousSignalHandlers ();
1296112960 }
1296212961
1296312962 void RunContext::handleUnfinishedSections() {
@@ -16888,8 +16887,8 @@ namespace Catch {
1688816887 writeSection( className, "", rootSection );
1688916888 }
1689016889
16891- void JunitReporter::writeSection( std::string const& className,
16892- std::string const& rootName,
16890+ void JunitReporter::writeSection( std::string const& className,
16891+ std::string const& rootName,
1689316892 SectionNode const& sectionNode ) {
1689416893 std::string name = trim( sectionNode.stats.sectionInfo.name );
1689516894 if( !rootName.empty() )
0 commit comments