diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index a6ce092e40d..1c2f5527ff9 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -199,6 +199,10 @@ WB_ENTRY(jint, WB_TakeLockAndHangInSafepoint(JNIEnv* env, jobject wb)) return 0; WB_END +WB_ENTRY(jlong, WB_GetMinimumJavaStackSize(JNIEnv* env, jobject o)) + return os::get_minimum_java_stack_size(); +WB_END + class WBIsKlassAliveClosure : public LockedClassesDo { Symbol* _name; int _count; @@ -3133,7 +3137,8 @@ static JNINativeMethod methods[] = { {CC"cleanMetaspaces", CC"()V", (void*)&WB_CleanMetaspaces}, {CC"rss", CC"()J", (void*)&WB_Rss}, {CC"printString", CC"(Ljava/lang/String;I)Ljava/lang/String;", (void*)&WB_PrintString}, - {CC"lockAndStuckInSafepoint", CC"()V", (void*)&WB_TakeLockAndHangInSafepoint}, + {CC"lockAndStuckInSafepoint", CC"()V", (void*)&WB_TakeLockAndHangInSafepoint}, + {CC"getMinimumJavaStackSize", CC"()J", (void*)&WB_GetMinimumJavaStackSize}, {CC"wordSize", CC"()J", (void*)&WB_WordSize}, {CC"rootChunkWordSize", CC"()J", (void*)&WB_RootChunkWordSize}, {CC"isStatic", CC"()Z", (void*)&WB_IsStaticallyLinked}, diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index ceff9b54c33..cf18388c625 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -2577,6 +2577,10 @@ jint os::set_minimum_stack_sizes() { return JNI_OK; } +jlong os::get_minimum_java_stack_size() { + return static_cast(_java_thread_min_stack_allowed); +} + // Builds a platform dependent Agent_OnLoad_ function name // which is used to find statically linked in agents. // Parameters: diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index 6798140ed4d..d585d3e5fc0 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -390,6 +390,8 @@ class os: AllStatic { static jint set_minimum_stack_sizes(); public: + // get allowed minimum java stack size + static jlong get_minimum_java_stack_size(); // Find committed memory region within specified range (start, start + size), // return true if found any static bool committed_in_range(address start, size_t size, address& committed_start, size_t& committed_size); diff --git a/test/hotspot/jtreg/runtime/ClassInitErrors/TestStackOverflowDuringInit.java b/test/hotspot/jtreg/runtime/ClassInitErrors/TestStackOverflowDuringInit.java index 180dc539795..3fee5ab131f 100644 --- a/test/hotspot/jtreg/runtime/ClassInitErrors/TestStackOverflowDuringInit.java +++ b/test/hotspot/jtreg/runtime/ClassInitErrors/TestStackOverflowDuringInit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, 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 @@ -29,16 +29,25 @@ * cause, even if we can't create the ExceptionInInitializerError * @comment This test could easily be perturbed so don't allow flag settings. * @requires vm.flagless + * @library /test/lib * @comment Run with the smallest stack possible to limit the execution time. * This is the smallest stack that is supported by all platforms. - * @run main/othervm -Xss384K -Xint TestStackOverflowDuringInit + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI TestStackOverflowDuringInit */ import java.io.ByteArrayOutputStream; import java.io.PrintStream; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.whitebox.WhiteBox; public class TestStackOverflowDuringInit { + static String expected = "java.lang.NoClassDefFoundError: Could not initialize class TestStackOverflowDuringInit$LongCache"; + static String cause = "Caused by: java.lang.StackOverflowError"; + // The setup for this is somewhat intricate. We need to trigger a // StackOverflowError during execution of the static initializer // for a class, but we need there to be insufficient stack left @@ -88,34 +97,47 @@ public class TestStackOverflowDuringInit { } } - public static void main(String[] args) throws Exception { - String expected = "java.lang.NoClassDefFoundError: Could not initialize class TestStackOverflowDuringInit$LongCache"; - String cause = "Caused by: java.lang.StackOverflowError"; + static class Launcher { + public static void main(String[] args) throws Exception { - // Pre-load, but not initialize, LongCache, else we will - // hit SOE during class loading. - System.out.println("Pre-loading ..."); - Class c = Class.forName("TestStackOverflowDuringInit$LongCache", - false, - TestStackOverflowDuringInit.class.getClassLoader()); - try { - recurse(); - } catch (Throwable ex) { - // ex.printStackTrace(); - verify_stack(ex, expected, cause); + // Pre-load, but not initialize, LongCache, else we will + // hit SOE during class loading. + System.out.println("Pre-loading ..."); + Class c = Class.forName("TestStackOverflowDuringInit$LongCache", + false, + TestStackOverflowDuringInit.class.getClassLoader()); + try { + recurse(); + } catch (Throwable ex) { + // ex.printStackTrace(); + verify_stack(ex, expected, cause); + } + } + + private static void verify_stack(Throwable e, String expected, String cause) throws Exception { + ByteArrayOutputStream byteOS = new ByteArrayOutputStream(); + try (PrintStream printStream = new PrintStream(byteOS)) { + e.printStackTrace(printStream); + } + String stackTrace = byteOS.toString("ASCII"); + System.out.println(stackTrace); + if (!stackTrace.contains(expected) || + (cause != null && !stackTrace.contains(cause))) { + throw new RuntimeException(expected + " and/or " + cause + " missing from stacktrace"); + } } } - private static void verify_stack(Throwable e, String expected, String cause) throws Exception { - ByteArrayOutputStream byteOS = new ByteArrayOutputStream(); - try (PrintStream printStream = new PrintStream(byteOS)) { - e.printStackTrace(printStream); - } - String stackTrace = byteOS.toString("ASCII"); - System.out.println(stackTrace); - if (!stackTrace.contains(expected) || - (cause != null && !stackTrace.contains(cause))) { - throw new RuntimeException(expected + " and/or " + cause + " missing from stacktrace"); - } + public static void main(String[] args) throws Exception { + WhiteBox wb = WhiteBox.getWhiteBox(); + long minimumJavaStackSize = wb.getMinimumJavaStackSize(); + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-Xss" + Long.toString(minimumJavaStackSize), "-Xint", + Launcher.class.getName()); + + OutputAnalyzer analyzer = new OutputAnalyzer(pb.start()); + analyzer.shouldHaveExitValue(0); + analyzer.shouldContain(expected); + analyzer.shouldContain(cause); } } diff --git a/test/lib/jdk/test/whitebox/WhiteBox.java b/test/lib/jdk/test/whitebox/WhiteBox.java index 8e30c089bcb..cc570caef7c 100644 --- a/test/lib/jdk/test/whitebox/WhiteBox.java +++ b/test/lib/jdk/test/whitebox/WhiteBox.java @@ -77,6 +77,7 @@ public class WhiteBox { public native long getVMLargePageSize(); public native long getHeapSpaceAlignment(); public native long getHeapAlignment(); + public native long getMinimumJavaStackSize(); public native boolean shipsFullDebugInfo(); public native boolean shipsPublicDebugInfo();