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
// Function for supporting the gtest_catch_exception flag.
// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the
// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise.
// Returns EXCEPTION_EXECUTE_HANDLER if given SEH exception was handled, or
// EXCEPTION_CONTINUE_SEARCH otherwise.
// 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
// 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
// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the
// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise.
// This function is useful as an __except condition.
int UnitTestOptions::GTestShouldProcessSEH(DWORD seh_code) {
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();
}
int UnitTestOptions::GTestProcessSEH(DWORD seh_code, const char* location) {
// Google Test should handle a SEH exception if:
// 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,
// apparently).
//
@ -877,18 +882,20 @@ int UnitTestOptions::GTestShouldProcessSEH(DWORD seh_code) {
// (see http://support.microsoft.com/kb/185294 for more information).
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))
should_handle = false;
else if (seh_code == EXCEPTION_BREAKPOINT)
should_handle = false;
else if (seh_code == EXCEPTION_STACK_OVERFLOW)
should_handle = false;
else if (seh_code == kCxxExceptionCode)
should_handle = false;
internal::ReportFailureInUnknownLocation(
TestPartResult::kFatalFailure,
FormatSehExceptionMessage(seh_code, location) +
"\n"
"Stack trace:\n" +
::testing::internal::GetCurrentOsStackTraceExceptTop(1));
return should_handle ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
return EXCEPTION_EXECUTE_HANDLER;
}
#endif // GTEST_HAS_SEH
@ -2555,18 +2562,6 @@ bool Test::HasSameFixtureClass() {
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 {
#if GTEST_HAS_EXCEPTIONS
@ -2608,22 +2603,8 @@ Result HandleSehExceptionsInMethodIfSupported(T* object, Result (T::*method)(),
#if GTEST_HAS_SEH
__try {
return (object->*method)();
} __except (internal::UnitTestOptions::GTestShouldProcessSEH( // NOLINT
GetExceptionCode())) {
// 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);
} __except (internal::UnitTestOptions::GTestProcessSEH( // NOLINT
GetExceptionCode(), location)) {
return static_cast<Result>(0);
}
#else