JDK-8185734 : [Windows] Structured Exception Catcher missing around gtest execution
  • Type: Bug
  • Component: hotspot
  • Sub-Component: test
  • Affected Version: 9,15
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows
  • CPU: x86
  • Submitted: 2017-08-02
  • Updated: 2025-08-08
  • Resolved: 2020-12-15
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11 JDK 17
11.0.12Fixed 17 b02Fixed
Related Reports
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
When running gtests on Windows, there is no __try/__except handler around the test execution.  As a result, SafeFetch32/SafeFetchN does not work properly on 32bit Windows during gtest execution.  There might be other error handling related issues as well, but it was SafeFetch not working that uncovered the problem.

This problem is very similar to JDK-8074860.  Some of the infrastructure mentioned there (os::win32::call_test_func_with_wrapper() and such) was removed by that change.  Since the tests in question were run during VM initialization, and that change made VM initialization properly protected, that infrastructure was no longer needed.  But with gtest we've moved a bunch of those tests out of the VM initialization context, and they are no longer protected.

I think conditionally wrapping the call to RUN_ALL_TESTS() in gtestMain.cpp with a __try/__except block using topLevelExceptionFilter will address this problem.  For the conditionalization, I suggest using the existing GTEST_HAS_SEH macro.

Comments
From PR description: At the moment, if a crash happens on Windows in gtests, the gtest SEH handler may be invoked instead of our error handler, and we just see a one-line-warning "SEH happened blabala". No hs-err file. Even worse, if a crash happens inside the VM as part of the gtests, if our SEH handler does not get involved, this may interfere with VM functionality - e.g. SafeFetch. Whether or not our SEH handler gets involved currently depends on arbitrary factors: whether the fault happens in a VM or Java thread - which have a __try/__except around their start function - or whether the fault happens directly in the thread running the test Faults in generated code are not handled on x86 but are okay x64 (where a SEH handler is registered for the code cache region) or on aarch64 (which uses VEH). This patch consists of two parts A) It surrounds the gtestlauncher main function with a SEH catcher. For that to work I also need to export the SEH handler from the hotspot. Note: It is difficult to place the SEH catcher: SEH is mutually exclusive with C++ exceptions, and since googletest uses C++ exceptions to communicate test conditions, the only place to put those __try/__except is really up here, at the entry of the gtestlauncher main() function. B) This is unfortunately not sufficient since googletest uses its own SEH catcher to wrap each test (see gtest.cc). Since that catcher sits below our catcher on the stack, it superimposes ours. In JBS, @kimbarrett suggested to build gtests with GTEST_HAS_SEH switched off to prevent gtest from using SEH. Unfortunately that won't work since the use of death tests means we need SEH. If we switch GTEST_HAS_SEH off, the death tests don't build. I also do not like this suggestion since this configuration may have a higher chance of bitrotting upstream. The solution I found is to switch off exception catching from user code as described in [3] using --gtest_catch_exceptions=0 or the environment variable GTEST_CATCH_EXCEPTIONS=0. Since we do not use C++ exceptions in the hotspot, this is fine. The only drawback is that this cannot be done from within the gtestlauncher itself. Setting the environment variable in the main function - or even during dynamic initialization - does not work because the gtestlauncher itself parses all arguments as part of dynamic initialization. So I did the next best thing and specified --gtest_catch_exceptions=0 at the places where we run the gtests. This is not perfect, but better than nothing. Testing: manually on Windows x64, x86, GH actions (Linux errors seem unrelated to this patch).
08-08-2025

Fix Request (11u): I'd like to fix this in an effort to make gtests on Windows more stable. Original PR: https://git.openjdk.java.net/jdk/pull/1757 Original patch: https://github.com/openjdk/jdk/commit/568dc29b.diff I would like to downport it since it allows us to run gtests which test signal handling on Windows in 11u. 11u patch: http://cr.openjdk.java.net/~stuefe/webrevs/backports/8185734-Windows-Structured-Exception-Catcher-missing-around-gtest-execution-11u.diff RFR-Thread: https://mail.openjdk.java.net/pipermail/jdk-updates-dev/2021-May/006117.html The original patch does not apply cleanly due to changes done in GTestWrapper.java in the wake of JEP 387. However, the delta is minimal (just one line). Patch has been tested at SAP for some weeks now. Thanks, Thomas
19-05-2021

Changeset: 568dc29b Author: Thomas Stuefe <stuefe@openjdk.org> Date: 2020-12-15 09:02:46 +0000 URL: https://git.openjdk.java.net/jdk/commit/568dc29b
15-12-2020

Overriding GTEST_HAS_SEH is not an option since if you have death tests you need SEH. Setting GTEST_HAS_SEH=0 makes the gtest build fail.
13-12-2020

The special case of SafeFetch on x64 is currently handled by HandleExceptionFromCodeCache(). Any fault occurring caused by code in the code cache is thereby relegated to the topLevelExceptionHandler and handled correctly, and since SafeFetch uses a generated StubRoutine this works for SafeFetch too. Only works for x64, not for x86, since this code was introduced on x64 only for the changed SEH model (which separates stack meta information from the stack and makes it necessary to tell the Unwinder where the code lives). So, SafeFetch from x86 still faults gtests, as do any real faults. Not sure about aarch64. They use VEH for everything. They may have no problem at all with faults in gtests, but I do not have the hardware to try.
11-12-2020

Did some experiments: 1) wrapping `__try{}__except(topLevelExceptionFilter)` around the whole runner in gtestLauncher.cpp can be done (it requires exporting the handler from the DLL) but does not do any good. Since the SEH handler of the google test framework is chained below that handler and will get any fault first. Results in the generic "SEH .. thrown in test body", our handler does not get control. 2) Wrapping any point below that is difficult since the use of __try .. __except excludes the use of C++ exceptions. Since gtest communicates test errors with exceptions, we cannot wrap anything above TEST_VM... level. The only way I see for solving either to switch to VectoredExceptionHandling. Or, to somehow disable the google test SEH handling and go with (1).
10-12-2020

ILW=exception handling doesn't work as expected;gtest tests, windows x86;none=MLH=>P4
10-10-2017

Use SafeFetch32 (after VM initialization) to read some invalid location. Address zero might be a good reliable choice to generate a crash.
02-08-2017

ok, can you provide the steps for reproducing the problems you faced?
02-08-2017

[~iignatyev] Nope. I got here and filed this bug after running into problems in a TEST_VM test. The thread context matters, e.g. the __try/__except needs to be active on the stack when the failure occurs.
02-08-2017

my understanding of JDK-8074860 was that we might have a problem only when there is no initialized JVM yet. in gtest context, we can bump into it only in "real unit" tests, i.e. tests which run w/o pre-initialized JVM.
02-08-2017

[~iignatyev] "wrap execution of tests which don't init jvm" -- that's backward. It should be all test executions, or at least those with an initialized VM if toplevelExceptionFilter requires that.
02-08-2017

I see, I misread http://hg.openjdk.java.net/jdk9/jdk9/hotspot/rev/883ae015914d . so we need to either wrap execution of tests which don't init jvm or the whole test execution. I've glanced at topLevelExceptionFilter, it seems to be safe to use w/ uninitialized JVM.
02-08-2017

[~iignatyev] No, that's not enough. JNI_CreateJavaVM already itself wraps its body as needed (per JDK-8074860). That's why the wrapping of the tests when they were executed as part of VM initialization was no longer needed. But the gtests are run in a different execution context. See runUnitTestsInner, which (via init_jvm) calls JNI_CreateJavaVM, and then later calls RUN_ALL_TESTS(). A possible complication is that I don't know if toplevelExceptionFilter requires an initialized VM. It might be we can't use it when running tests that don't require a VM.
02-08-2017

[~kbarrett], will it be enough to just wrap JNI_CreateJavaVM in hotspot/test/native/gtestMain.cpp?
02-08-2017