From 38bf1192b637cf3513cb25ac21f513bfb51cb55b Mon Sep 17 00:00:00 2001 From: Jorn Vernee Date: Thu, 21 Sep 2023 13:54:35 +0000 Subject: [PATCH] 8310228: Improve error reporting for uncaught native exceptions on Windows Reviewed-by: dholmes, djelinski --- make/test/JtregNativeHotspot.gmk | 2 + src/hotspot/os/windows/os_windows.cpp | 57 +++++++++------ .../UncaughtNativeExceptionTest.java | 71 +++++++++++++++++++ .../ErrorHandling/libNativeException.c | 32 +++++++++ 4 files changed, 140 insertions(+), 22 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/ErrorHandling/UncaughtNativeExceptionTest.java create mode 100644 test/hotspot/jtreg/runtime/ErrorHandling/libNativeException.c diff --git a/make/test/JtregNativeHotspot.gmk b/make/test/JtregNativeHotspot.gmk index 84bd6b123f5..45efa033cff 100644 --- a/make/test/JtregNativeHotspot.gmk +++ b/make/test/JtregNativeHotspot.gmk @@ -1504,6 +1504,8 @@ else BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libCompleteExit += -lpthread BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libnativeStack += -lpthread BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exeGetCreatedJavaVMs := -ljvm -lpthread + + BUILD_HOTSPOT_JTREG_EXCLUDE += libNativeException.c endif ifeq ($(ASAN_ENABLED), true) diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 5563169151b..39e6c15fed8 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -280,10 +280,12 @@ void os::run_periodic_checks(outputStream* st) { return; } +#ifndef _WIN64 // previous UnhandledExceptionFilter, if there is one static LPTOP_LEVEL_EXCEPTION_FILTER prev_uef_handler = nullptr; +#endif -LONG WINAPI Handle_FLT_Exception(struct _EXCEPTION_POINTERS* exceptionInfo); +static LONG WINAPI Uncaught_Exception_Handler(struct _EXCEPTION_POINTERS* exceptionInfo); void os::init_system_properties_values() { // sysclasspath, java_home, dll_dir @@ -397,7 +399,7 @@ void os::init_system_properties_values() { #ifndef _WIN64 // set our UnhandledExceptionFilter and save any previous one - prev_uef_handler = SetUnhandledExceptionFilter(Handle_FLT_Exception); + prev_uef_handler = SetUnhandledExceptionFilter(Uncaught_Exception_Handler); #endif // Done @@ -2534,9 +2536,7 @@ LONG Handle_IDiv_Exception(struct _EXCEPTION_POINTERS* exceptionInfo) { #if defined(_M_AMD64) || defined(_M_IX86) //----------------------------------------------------------------------------- -LONG WINAPI Handle_FLT_Exception(struct _EXCEPTION_POINTERS* exceptionInfo) { - PCONTEXT ctx = exceptionInfo->ContextRecord; -#ifndef _WIN64 +static bool handle_FLT_exception(struct _EXCEPTION_POINTERS* exceptionInfo) { // handle exception caused by native method modifying control word DWORD exception_code = exceptionInfo->ExceptionRecord->ExceptionCode; @@ -2547,34 +2547,48 @@ LONG WINAPI Handle_FLT_Exception(struct _EXCEPTION_POINTERS* exceptionInfo) { case EXCEPTION_FLT_INVALID_OPERATION: case EXCEPTION_FLT_OVERFLOW: case EXCEPTION_FLT_STACK_CHECK: - case EXCEPTION_FLT_UNDERFLOW: + case EXCEPTION_FLT_UNDERFLOW: { + PCONTEXT ctx = exceptionInfo->ContextRecord; +#ifndef _WIN64 jint fp_control_word = (* (jint*) StubRoutines::x86::addr_fpu_cntrl_wrd_std()); if (fp_control_word != ctx->FloatSave.ControlWord) { // Restore FPCW and mask out FLT exceptions ctx->FloatSave.ControlWord = fp_control_word | 0xffffffc0; // Mask out pending FLT exceptions ctx->FloatSave.StatusWord &= 0xffffff00; - return EXCEPTION_CONTINUE_EXECUTION; + return true; } +#else // !_WIN64 + // On Windows, the mxcsr control bits are non-volatile across calls + // See also CR 6192333 + // + jint MxCsr = INITIAL_MXCSR; + // we can't use StubRoutines::x86::addr_mxcsr_std() + // because in Win64 mxcsr is not saved there + if (MxCsr != ctx->MxCsr) { + ctx->MxCsr = MxCsr; + return true; + } +#endif // !_WIN64 + } } + return false; +} +#endif + +#ifndef _WIN64 +static LONG WINAPI Uncaught_Exception_Handler(struct _EXCEPTION_POINTERS* exceptionInfo) { + if (handle_FLT_exception(exceptionInfo)) { + return EXCEPTION_CONTINUE_EXECUTION; + } + + // we only override this on 32 bits, so only check it there if (prev_uef_handler != nullptr) { // We didn't handle this exception so pass it to the previous // UnhandledExceptionFilter. return (prev_uef_handler)(exceptionInfo); } -#else // !_WIN64 - // On Windows, the mxcsr control bits are non-volatile across calls - // See also CR 6192333 - // - jint MxCsr = INITIAL_MXCSR; - // we can't use StubRoutines::x86::addr_mxcsr_std() - // because in Win64 mxcsr is not saved there - if (MxCsr != ctx->MxCsr) { - ctx->MxCsr = MxCsr; - return EXCEPTION_CONTINUE_EXECUTION; - } -#endif // !_WIN64 return EXCEPTION_CONTINUE_SEARCH; } @@ -2831,9 +2845,8 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) { } #if defined(_M_AMD64) || defined(_M_IX86) - if ((in_java || in_native) && exception_code != EXCEPTION_UNCAUGHT_CXX_EXCEPTION) { - LONG result=Handle_FLT_Exception(exceptionInfo); - if (result==EXCEPTION_CONTINUE_EXECUTION) return result; + if ((in_java || in_native) && handle_FLT_exception(exceptionInfo)) { + return EXCEPTION_CONTINUE_EXECUTION; } #endif diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/UncaughtNativeExceptionTest.java b/test/hotspot/jtreg/runtime/ErrorHandling/UncaughtNativeExceptionTest.java new file mode 100644 index 00000000000..3f26461643a --- /dev/null +++ b/test/hotspot/jtreg/runtime/ErrorHandling/UncaughtNativeExceptionTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test id + * @enablePreview + * @requires os.family == "windows" + * @library /test/lib + * @run testng UncaughtNativeExceptionTest + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import org.testng.annotations.Test; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.regex.Pattern; + +import static org.testng.Assert.assertTrue; + +public class UncaughtNativeExceptionTest { + private static class Crasher { + public static void main(String[] args) throws Throwable { + System.loadLibrary("NativeException"); + throwException(); + } + + static native void throwException(); + } + + // check that we actually report the native exception, + // and don't terminate abruptly due to stack overflow error + @Test + public void testNativeExceptionReporting() throws Exception { + OutputAnalyzer output = ProcessTools.executeTestJvm( + // executeTestJvm doesn't seem to forward 'java.library.path' + "-Djava.library.path=" + System.getProperty("java.library.path"), + Crasher.class.getName()); + + File hsErrFile = HsErrFileUtils.openHsErrFileFromOutput(output); + Path hsErrPath = hsErrFile.toPath(); + assertTrue(Files.exists(hsErrPath)); + + Pattern[] positivePatterns = { + Pattern.compile(".*Internal Error \\(0x2a\\).*") + }; + HsErrFileUtils.checkHsErrFileContent(hsErrFile, positivePatterns, null, true /* check end marker */, false /* verbose */); + } +} diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/libNativeException.c b/test/hotspot/jtreg/runtime/ErrorHandling/libNativeException.c new file mode 100644 index 00000000000..3bf71cf9c67 --- /dev/null +++ b/test/hotspot/jtreg/runtime/ErrorHandling/libNativeException.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include + +#include + +const DWORD EX_CODE = 42; + +JNIEXPORT void JNICALL Java_UncaughtNativeExceptionTest_00024Crasher_throwException(JNIEnv* env, jclass cls) { + RaiseException(EX_CODE, EXCEPTION_NONCONTINUABLE, 0, NULL); +}