Make GoogleTest handle SEH exceptions before stack unwinding rather than afterward

This ensure the erroring stack frame is visible and accessible when the handler is invoked.

Fixes #4298

PiperOrigin-RevId: 544692549
Change-Id: Ia165a8c293e8edc820da5f5ad4416546fffe2493
This commit is contained in:
Abseil Team 2023-06-30 10:32:13 -07:00 committed by Copybara-Service
parent 251e720391
commit 1f531be3a1
2 changed files with 27 additions and 46 deletions

View File

@ -387,10 +387,10 @@ class GTEST_API_ UnitTestOptions {
#ifdef GTEST_OS_WINDOWS #ifdef GTEST_OS_WINDOWS
// Function for supporting the gtest_catch_exception flag. // Function for supporting the gtest_catch_exception flag.
// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the // Returns EXCEPTION_EXECUTE_HANDLER if given SEH exception was handled, or
// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. // EXCEPTION_CONTINUE_SEARCH otherwise.
// This function is useful as an __except condition. // This function is useful as an __except condition.
static int GTestShouldProcessSEH(DWORD exception_code); static int GTestProcessSEH(DWORD seh_code, const char* location);
#endif // GTEST_OS_WINDOWS #endif // GTEST_OS_WINDOWS
// Returns true if "name" matches the ':' separated list of glob-style // Returns true if "name" matches the ':' separated list of glob-style

View File

@ -863,13 +863,18 @@ bool UnitTestOptions::FilterMatchesTest(const std::string& test_suite_name,
} }
#if GTEST_HAS_SEH #if GTEST_HAS_SEH
// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the static std::string FormatSehExceptionMessage(DWORD exception_code,
// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. const char* location) {
// This function is useful as an __except condition. Message message;
int UnitTestOptions::GTestShouldProcessSEH(DWORD seh_code) { message << "SEH exception with code 0x" << std::setbase(16) << exception_code
<< std::setbase(10) << " thrown in " << location << ".";
return message.GetString();
}
int UnitTestOptions::GTestProcessSEH(DWORD seh_code, const char* location) {
// Google Test should handle a SEH exception if: // Google Test should handle a SEH exception if:
// 1. the user wants it to, AND // 1. the user wants it to, AND
// 2. this is not a breakpoint exception, AND // 2. this is not a breakpoint exception or stack overflow, AND
// 3. this is not a C++ exception (VC++ implements them via SEH, // 3. this is not a C++ exception (VC++ implements them via SEH,
// apparently). // apparently).
// //
@ -877,18 +882,20 @@ int UnitTestOptions::GTestShouldProcessSEH(DWORD seh_code) {
// (see http://support.microsoft.com/kb/185294 for more information). // (see http://support.microsoft.com/kb/185294 for more information).
const DWORD kCxxExceptionCode = 0xe06d7363; const DWORD kCxxExceptionCode = 0xe06d7363;
bool should_handle = true; if (!GTEST_FLAG_GET(catch_exceptions) || seh_code == kCxxExceptionCode ||
seh_code == EXCEPTION_BREAKPOINT ||
seh_code == EXCEPTION_STACK_OVERFLOW) {
return EXCEPTION_CONTINUE_SEARCH; // Don't handle these exceptions
}
if (!GTEST_FLAG_GET(catch_exceptions)) internal::ReportFailureInUnknownLocation(
should_handle = false; TestPartResult::kFatalFailure,
else if (seh_code == EXCEPTION_BREAKPOINT) FormatSehExceptionMessage(seh_code, location) +
should_handle = false; "\n"
else if (seh_code == EXCEPTION_STACK_OVERFLOW) "Stack trace:\n" +
should_handle = false; ::testing::internal::GetCurrentOsStackTraceExceptTop(1));
else if (seh_code == kCxxExceptionCode)
should_handle = false;
return should_handle ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_EXECUTE_HANDLER;
} }
#endif // GTEST_HAS_SEH #endif // GTEST_HAS_SEH
@ -2555,18 +2562,6 @@ bool Test::HasSameFixtureClass() {
return true; return true;
} }
#if GTEST_HAS_SEH
static std::string FormatSehExceptionMessage(DWORD exception_code,
const char* location) {
Message message;
message << "SEH exception with code 0x" << std::setbase(16) << exception_code
<< std::setbase(10) << " thrown in " << location << ".";
return message.GetString();
}
#endif // GTEST_HAS_SEH
namespace internal { namespace internal {
#if GTEST_HAS_EXCEPTIONS #if GTEST_HAS_EXCEPTIONS
@ -2608,22 +2603,8 @@ Result HandleSehExceptionsInMethodIfSupported(T* object, Result (T::*method)(),
#if GTEST_HAS_SEH #if GTEST_HAS_SEH
__try { __try {
return (object->*method)(); return (object->*method)();
} __except (internal::UnitTestOptions::GTestShouldProcessSEH( // NOLINT } __except (internal::UnitTestOptions::GTestProcessSEH( // NOLINT
GetExceptionCode())) { GetExceptionCode(), location)) {
// We wrap an inner function because VC++ prohibits direct creation of
// objects with destructors on stack in functions using __try
// (see error C2712).
struct Wrapper {
static void ReportFailure(DWORD code, const char* location) {
return internal::ReportFailureInUnknownLocation(
TestPartResult::kFatalFailure,
FormatSehExceptionMessage(code, location) +
"\n"
"Stack trace:\n" +
::testing::internal::GetCurrentOsStackTraceExceptTop(1));
}
};
Wrapper::ReportFailure(GetExceptionCode(), location);
return static_cast<Result>(0); return static_cast<Result>(0);
} }
#else #else