8351327: -XX:AOTMode=record interferes with application execution

Reviewed-by: shade, kvn, matsaave
This commit is contained in:
Ioi Lam 2025-03-13 04:57:43 +00:00
parent 41cc049f42
commit a7ad4144c8
4 changed files with 124 additions and 5 deletions

View File

@ -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() {

View File

@ -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

View File

@ -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);
}
}

View File

@ -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 <code>CDSAppTester::checkExecution(out, runMode)</code> is called, has the application been
// executed? If so, <code>out</code> 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);
}