< prev index next > test/lib/jdk/test/lib/cds/CDSAppTester.java
Print this page
import java.io.File;
import jdk.test.lib.cds.CDSTestUtils;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.StringArrayUtils;
+ import jtreg.SkippedException;
/*
* This is a base class used for testing CDS functionalities with complex applications.
* You can define the application by overridding the vmArgs(), classpath() and appCommandLine()
* methods. Application-specific validation checks can be implemented with checkExecution().
+ *
+ * Note: to debug the new workflow, run jtreg with -vmoption:-DCDSAppTester.split.new.workflow=true
+ * This will run the new workflow in two separate processes that you can rerun easily inside a debugger.
+ * Also, the log files are easier to read.
*/
abstract public class CDSAppTester {
private final String name;
private final String classListFile;
private final String classListFileLog;
private final String staticArchiveFile;
private final String staticArchiveFileLog;
private final String dynamicArchiveFile;
private final String dynamicArchiveFileLog;
- private final String productionRunLog;
+ private final String codeCacheFile; // old workflow
+ private final String codeCacheFileLog;
+ private final String cdsFile; // new workflow: -XX:CacheDataStore=<foo>.cds
+ private final String cdsFileLog;
+ private final String cdsFilePreImage; // new workflow: -XX:CacheDataStore=<foo>.cds
+ private final String cdsFilePreImageLog;
+ private final String aotFile; // new workflow = cdsFile + ".code"
+ private int numProductionRuns = 0;
public CDSAppTester(String name) {
+ if (CDSTestUtils.DYNAMIC_DUMP) {
+ throw new jtreg.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";
staticArchiveFile = name() + ".static.jsa";
staticArchiveFileLog = staticArchiveFile + ".log";
dynamicArchiveFile = name() + ".dynamic.jsa";
dynamicArchiveFileLog = dynamicArchiveFile + ".log";
- productionRunLog = name() + ".production.log";
+ codeCacheFile = name() + ".code.jsa";
+ codeCacheFileLog = codeCacheFile + ".log";
+ cdsFile = name() + ".cds";
+ cdsFileLog = cdsFile + ".log";
+ cdsFilePreImage = cdsFile + ".preimage";
+ cdsFilePreImageLog = cdsFilePreImage + ".log";
+ aotFile = cdsFile + ".code";
+ }
+
+ private String productionRunLog() {
+ if (numProductionRuns == 0) {
+ return name() + ".production.log";
+ } else {
+ return name() + ".production." + numProductionRuns + ".log";
+ }
}
private enum Workflow {
STATIC, // classic -Xshare:dump workflow
DYNAMIC, // classic -XX:ArchiveClassesAtExit
+ LEYDEN_OLD, // The old "5 step workflow", to be phased out
+ LEYDEN, // The new "one step training workflow" -- see JDK-8320264
}
public enum RunMode {
CLASSLIST,
DUMP_STATIC,
DUMP_DYNAMIC,
+ DUMP_CODECACHE, // LEYDEN_OLD only
+ TRAINING, // LEYDEN only
+ TRAINING0, // LEYDEN only
+ TRAINING1, // LEYDEN only
PRODUCTION;
public boolean isStaticDump() {
return this == DUMP_STATIC;
}
// optional
public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {}
private Workflow workflow;
+ private boolean checkExitValue = true;
+
+ public final void setCheckExitValue(boolean b) {
+ checkExitValue = b;
+ }
public final boolean isStaticWorkflow() {
return workflow == Workflow.STATIC;
}
public final boolean isDynamicWorkflow() {
return workflow == Workflow.DYNAMIC;
}
+ public final boolean isLeydenOldWorkflow() {
+ return workflow == Workflow.LEYDEN_OLD;
+ }
+
+ public final boolean isLeydenWorkflow() {
+ return workflow == Workflow.LEYDEN;
+ }
+
private String logToFile(String logFile, String... logTags) {
StringBuilder sb = new StringBuilder("-Xlog:");
String prefix = "";
for (String tag : logTags) {
sb.append(prefix);
Process process = pb.start();
OutputAnalyzer output = CDSTestUtils.executeAndLog(process, runMode.toString());
for (String logFile : logFiles) {
listOutputFile(logFile);
}
- output.shouldHaveExitValue(0);
+ if (checkExitValue) {
+ output.shouldHaveExitValue(0);
+ }
CDSTestUtils.checkCommonExecExceptions(output);
checkExecution(output, runMode);
return output;
}
logToFile(staticArchiveFileLog,
"cds=debug",
"cds+class=debug",
"cds+heap=warning",
"cds+resolve=debug"));
+ if (isLeydenOldWorkflow()) {
+ cmdLine = StringArrayUtils.concat(cmdLine,
+ "-XX:+AOTClassLinking",
+ "-XX:+ArchiveDynamicProxies",
+ "-XX:+ArchiveReflectionData");
+ }
+
return executeAndCheck(cmdLine, runMode, staticArchiveFile, staticArchiveFileLog);
}
private OutputAnalyzer dumpDynamicArchive() throws Exception {
RunMode runMode = RunMode.DUMP_DYNAMIC;
logToFile(dynamicArchiveFileLog,
"cds=debug",
"cds+class=debug",
"cds+resolve=debug",
"class+load=debug"));
+ } else {
+ // Leyden "OLD" workflow step 3
+ cmdLine = StringArrayUtils.concat(vmArgs(runMode),
+ "-Xlog:cds",
+ "-XX:ArchiveClassesAtExit=" + dynamicArchiveFile,
+ "-XX:SharedArchiveFile=" + staticArchiveFile,
+ "-XX:+RecordTraining",
+ "-cp", classpath(runMode),
+ logToFile(dynamicArchiveFileLog,
+ "cds=debug",
+ "cds+class=debug",
+ "cds+resolve=debug",
+ "class+load=debug"));
}
cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
return executeAndCheck(cmdLine, runMode, dynamicArchiveFile, dynamicArchiveFileLog);
}
+
+ private OutputAnalyzer dumpCodeCache() throws Exception {
+ RunMode runMode = RunMode.DUMP_CODECACHE;
+ String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
+ "-XX:SharedArchiveFile=" + dynamicArchiveFile,
+ "-XX:+ReplayTraining",
+ "-XX:+StoreCachedCode",
+ "-XX:CachedCodeFile=" + codeCacheFile,
+ "-XX:CachedCodeMaxSize=512M",
+ "-cp", classpath(runMode),
+ logToFile(codeCacheFileLog, "scc"));
+
+ cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
+ return executeAndCheck(cmdLine, runMode, codeCacheFile, codeCacheFileLog);
+ }
+
+ private OutputAnalyzer oldProductionRun() throws Exception {
+ RunMode runMode = RunMode.PRODUCTION;
+ String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:VerifyArchivedFields=2", // make sure archived heap objects are good.
+ "-XX:SharedArchiveFile=" + dynamicArchiveFile,
+ "-XX:+ReplayTraining",
+ "-XX:+LoadCachedCode",
+ "-XX:CachedCodeFile=" + codeCacheFile,
+ "-cp", classpath(runMode),
+ logToFile(productionRunLog(), "cds", "scc*=warning"));
+ cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
+ return executeAndCheck(cmdLine, runMode, productionRunLog());
+ }
+
+ private String trainingLog(String file) {
+ return logToFile(file,
+ "cds=debug",
+ "cds+class=debug",
+ "cds+heap=warning",
+ "cds+resolve=debug");
+ }
+
+ // normal training workflow (main JVM process spawns child process)
+ private OutputAnalyzer trainingRun() throws Exception {
+ RunMode runMode = RunMode.TRAINING;
+ File f = new File(cdsFile);
+ f.delete();
+ String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
+ "-XX:+AOTClassLinking",
+ "-XX:+ArchiveDynamicProxies",
+ //"-XX:+ArchiveReflectionData",
+ "-XX:CacheDataStore=" + cdsFile,
+ "-cp", classpath(runMode),
+ // Use PID to distinguish the logs of the training process
+ // and the forked final image dump process.
+ "-Xlog:cds::uptime,level,tags,pid",
+ trainingLog(cdsFileLog));
+ cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
+ OutputAnalyzer out = executeAndCheck(cmdLine, runMode, cdsFile, cdsFileLog);
+ listOutputFile(cdsFile + ".log.0"); // the preimage dump
+ return out;
+ }
+
+ // "split" training workflow (launch the two processes manually, for easier debugging);
+ private OutputAnalyzer trainingRun0() throws Exception {
+ RunMode runMode = RunMode.TRAINING0;
+ File f = new File(cdsFile);
+ f.delete();
+ String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+CDSManualFinalImage",
+ "-XX:+AOTClassLinking",
+ "-XX:+ArchiveDynamicProxies",
+ //"-XX:+ArchiveReflectionData",
+ "-XX:CacheDataStore=" + cdsFile,
+ "-cp", classpath(runMode),
+ trainingLog(cdsFilePreImageLog));
+ cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
+ return executeAndCheck(cmdLine, runMode, cdsFilePreImage, cdsFilePreImageLog);
+ }
+ private OutputAnalyzer trainingRun1() throws Exception {
+ RunMode runMode = RunMode.TRAINING1;
+ File f = new File(cdsFile);
+ f.delete();
+ String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+AOTClassLinking",
+ "-XX:+ArchiveDynamicProxies",
+ //"-XX:+ArchiveReflectionData",
+ "-XX:CacheDataStore=" + cdsFile,
+ "-XX:CDSPreimage=" + cdsFilePreImage,
+ "-cp", classpath(runMode),
+ trainingLog(cdsFileLog));
+ cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
+ return executeAndCheck(cmdLine, runMode, cdsFile, aotFile, cdsFileLog);
+ }
+
private OutputAnalyzer productionRun() throws Exception {
+ return productionRun(null, null);
+ }
+
+ public OutputAnalyzer productionRun(String[] extraVmArgs) throws Exception {
+ return productionRun(extraVmArgs, null);
+ }
+
+ // After calling run(String[]), you can call this method to run the app again, with the AOTCache
+ // using different args to the VM and application.
+ public OutputAnalyzer productionRun(String[] extraVmArgs, String[] extraAppArgs) throws Exception {
RunMode runMode = RunMode.PRODUCTION;
String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:VerifyArchivedFields=2", // make sure archived heap objects are good.
"-cp", classpath(runMode),
- logToFile(productionRunLog, "cds"));
+ logToFile(productionRunLog(), "cds"));
if (isStaticWorkflow()) {
cmdLine = StringArrayUtils.concat(cmdLine, "-XX:SharedArchiveFile=" + staticArchiveFile);
} else if (isDynamicWorkflow()) {
cmdLine = StringArrayUtils.concat(cmdLine, "-XX:SharedArchiveFile=" + dynamicArchiveFile);
+ } else {
+ cmdLine = StringArrayUtils.concat(cmdLine, "-XX:CacheDataStore=" + cdsFile);
+ }
+
+ if (extraVmArgs != null) {
+ cmdLine = StringArrayUtils.concat(cmdLine, extraVmArgs);
}
cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
- return executeAndCheck(cmdLine, runMode, productionRunLog);
+
+ if (extraAppArgs != null) {
+ cmdLine = StringArrayUtils.concat(cmdLine, extraAppArgs);
+ }
+
+ OutputAnalyzer out = executeAndCheck(cmdLine, runMode, productionRunLog());
+ numProductionRuns ++;
+ return out;
}
public void run(String args[]) throws Exception {
String err = "Must have exactly one command line argument of the following: ";
String prefix = "";
} else {
if (args[0].equals("STATIC")) {
runStaticWorkflow();
} else if (args[0].equals("DYNAMIC")) {
runDynamicWorkflow();
+ } else if (args[0].equals("LEYDEN_OLD")) {
+ runLeydenOldWorkflow();
+ } else if (args[0].equals("LEYDEN")) {
+ runLeydenWorkflow(false);
+ } else if (args[0].equals("LEYDEN_TRAINONLY")) {
+ runLeydenWorkflow(true);
} else {
throw new RuntimeException(err);
}
}
}
private void runDynamicWorkflow() throws Exception {
this.workflow = Workflow.DYNAMIC;
dumpDynamicArchive();
productionRun();
}
+
+ private void runLeydenOldWorkflow() throws Exception {
+ this.workflow = Workflow.LEYDEN_OLD;
+ createClassList();
+ dumpStaticArchive();
+ dumpDynamicArchive();
+ dumpCodeCache();
+ oldProductionRun();
+ }
+
+ private void runLeydenWorkflow(boolean trainOnly) throws Exception {
+ this.workflow = Workflow.LEYDEN;
+ if (System.getProperty("CDSAppTester.split.new.workflow") != null) {
+ trainingRun0();
+ trainingRun1();
+ } else {
+ trainingRun();
+ }
+ if (!trainOnly) {
+ productionRun();
+ }
+ }
}
< prev index next >