diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index f50ae446363..da1d7af32db 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -561,7 +561,7 @@ bool CDSConfig::is_dumping_final_static_archive() { bool CDSConfig::allow_only_single_java_thread() { // See comments in JVM_StartThread() - return is_dumping_static_archive(); + return is_dumping_classic_static_archive() || is_dumping_final_static_archive(); } bool CDSConfig::is_using_archive() { diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index ee877c474b9..3658a28c102 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -823,8 +823,10 @@ void MetaspaceShared::preload_and_dump(TRAPS) { if (CDSConfig::new_aot_flags_used()) { if (CDSConfig::is_dumping_preimage_static_archive()) { + // We are in the JVM that runs the training run. Continue execution, + // so that it can finish all clean-up and return the correct exit + // code to the OS. tty->print_cr("AOTConfiguration recorded: %s", AOTConfiguration); - vm_exit(0); } else { // The JLI launcher only recognizes the "old" -Xshare:dump flag. // When the new -XX:AOTMode=create flag is used, we can't return diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/TrainingRun.java b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/TrainingRun.java new file mode 100644 index 00000000000..fd896fd6958 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/TrainingRun.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 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 + * 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 + * @summary -XX:AOTMode=record should not interfere with app execution: (1) thread creation; (2) exit code + * @bug 8351327 + * @requires vm.cds.supports.aot.class.linking + * @comment work around JDK-8345635 + * @requires !vm.jvmci.enabled + * @library /test/jdk/lib/testlibrary /test/lib + * @build TrainingRun + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar MyTestApp + * @run driver TrainingRun AOT + */ + +import jdk.test.lib.cds.CDSAppTester; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.process.OutputAnalyzer; + +public class TrainingRun { + static final String appJar = ClassFileInstaller.getJarPath("app.jar"); + static final String mainClass = "MyTestApp"; + + public static void main(String[] args) throws Exception { + (new Tester()).run(args); + } + + static class Tester extends CDSAppTester { + public Tester() { + super(mainClass); + + // CDSAppTester usually wants the app to return exit value 0, but this test + // checks whether the training run can return 2. + setCheckExitValue(false); + } + + @Override + public String classpath(RunMode runMode) { + return appJar; + } + + @Override + public String[] appCommandLine(RunMode runMode) { + return new String[] { + mainClass, + }; + } + + @Override + public void checkExecution(OutputAnalyzer out, RunMode runMode) { + if (runMode.isApplicationExecuted()) { + out.shouldHaveExitValue(2); + out.shouldContain("Hello: x is 1"); + } + } + } +} + +class MyTestApp { + volatile static int x = 0; + + public static void main(String args[]) throws Exception { + Thread t = new Thread(() -> { + x = 1; + }); + t.start(); + t.join(); + + if (x != 1) { + throw new RuntimeException("x should be 1 but is " + x); + } + System.out.println("Hello: x is " + x); + System.out.println("I am calling System.exit(2)"); + System.exit(2); + } +} diff --git a/test/lib/jdk/test/lib/cds/CDSAppTester.java b/test/lib/jdk/test/lib/cds/CDSAppTester.java index 7b4ad6eb5c4..d9f28ed2455 100644 --- a/test/lib/jdk/test/lib/cds/CDSAppTester.java +++ b/test/lib/jdk/test/lib/cds/CDSAppTester.java @@ -56,7 +56,6 @@ abstract public class CDSAppTester { throw new SkippedException("Tests based on CDSAppTester should be excluded when -Dtest.dynamic.cds.archive is specified"); } - // Old workflow this.name = name; classListFile = name() + ".classlist"; classListFileLog = classListFile + ".log"; @@ -89,7 +88,7 @@ abstract public class CDSAppTester { TRAINING, // -XX:DumpLoadedClassList OR {-XX:AOTMode=create -XX:AOTConfiguration} DUMP_STATIC, // -Xshare:dump DUMP_DYNAMIC, // -XX:ArchiveClassesArExit - ASSEMBLY, // JEP 483 + ASSEMBLY, // JEP 483 (assembly phase, app logic not executed) PRODUCTION; // Running with the CDS archive produced from the above steps public boolean isStaticDump() { @@ -98,6 +97,24 @@ abstract public class CDSAppTester { public boolean isProductionRun() { return this == PRODUCTION; } + + // When CDSAppTester::checkExecution(out, runMode) is called, has the application been + // executed? If so, out should contain logs printed by the application's own logic. + public boolean isApplicationExecuted() { + return (this != ASSEMBLY) && (this != DUMP_STATIC); + } + } + + public boolean isDumping(RunMode runMode) { + if (isStaticWorkflow()) { + return runMode == RunMode.DUMP_STATIC; + } else if (isDynamicWorkflow()) { + return runMode == RunMode.DUMP_DYNAMIC; + } else if (isAOTWorkflow()) { + return runMode == RunMode.TRAINING || runMode == RunMode.ASSEMBLY; + } else { + return false; + } } public final String name() { @@ -184,7 +201,9 @@ abstract public class CDSAppTester { "-XX:AOTConfiguration=" + aotConfigurationFile, "-cp", classpath(runMode), logToFile(aotConfigurationFileLog, - "class+load=debug")); + "class+load=debug", + "cds=debug", + "cds+class=debug")); cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); return executeAndCheck(cmdLine, runMode, aotConfigurationFile, aotConfigurationFileLog); }