36 * You can define the application by overridding the vmArgs(), classpath() and appCommandLine()
37 * methods. Application-specific validation checks can be implemented with checkExecution().
38 *
39 * The AOT workflow runs with one-step training by default. For debugging purposes, run
40 * jtreg with -vmoption:-DCDSAppTester.two.step.training=true. This will run -XX:AOTMode=record
41 * and -XX:AOTMode=record in two separate processes that you can rerun easily inside a debugger.
42 * Also, the log files are easier to read.
43 */
44 abstract public class CDSAppTester {
45 private final String name;
46 private final String classListFile;
47 private final String classListFileLog;
48 private final String aotConfigurationFile;
49 private final String aotConfigurationFileLog;
50 private final String staticArchiveFile;
51 private final String staticArchiveFileLog;
52 private final String aotCacheFile;
53 private final String aotCacheFileLog;
54 private final String dynamicArchiveFile;
55 private final String dynamicArchiveFileLog;
56 private final String tempBaseArchiveFile;
57 private int numProductionRuns = 0;
58 private String whiteBoxJar = null;
59 private boolean inOneStepTraining = false;
60
61 /**
62 * All files created in the CDS/AOT workflow will be name + extension. E.g.
63 * - name.aot
64 * - name.aotconfig
65 * - name.classlist
66 * - name.jsa
67 */
68 public CDSAppTester(String name) {
69 if (CDSTestUtils.DYNAMIC_DUMP) {
70 throw new SkippedException("Tests based on CDSAppTester should be excluded when -Dtest.dynamic.cds.archive is specified");
71 }
72
73 this.name = name;
74 classListFile = name() + ".classlist";
75 classListFileLog = logFileName(classListFile);
76 aotConfigurationFile = name() + ".aotconfig";
77 aotConfigurationFileLog = logFileName(aotConfigurationFile);
78 staticArchiveFile = name() + ".static.jsa";
79 staticArchiveFileLog = logFileName(staticArchiveFile);
80 aotCacheFile = name() + ".aot";
81 aotCacheFileLog = logFileName(aotCacheFile);;
82 dynamicArchiveFile = name() + ".dynamic.jsa";
83 dynamicArchiveFileLog = logFileName(dynamicArchiveFile);
84 tempBaseArchiveFile = name() + ".temp-base.jsa";
85 }
86
87 private String productionRunLog() {
88 if (numProductionRuns == 0) {
89 return logFileName(name() + ".production");
90 } else {
91 return logFileName(name() + ".production." + numProductionRuns);
92 }
93 }
94
95 private static String logFileName(String file) {
96 file = file.replace("\"", "%22");
97 file = file.replace("'", "%27");
98 return file + ".log";
99 }
100
101 private enum Workflow {
102 STATIC, // classic -Xshare:dump workflow
103 DYNAMIC, // classic -XX:ArchiveClassesAtExit
104 AOT, // JEP 483 Ahead-of-Time Class Loading & Linking
105 }
106
107 public enum RunMode {
108 TRAINING, // -XX:DumpLoadedClassList OR {-XX:AOTMode=record -XX:AOTConfiguration}
109 DUMP_STATIC, // -Xshare:dump
110 DUMP_DYNAMIC, // -XX:ArchiveClassesArExit
111 ASSEMBLY, // JEP 483 (assembly phase, app logic not executed)
112 PRODUCTION; // Running with the CDS archive produced from the above steps
113
114 public boolean isStaticDump() {
115 return this == DUMP_STATIC;
116 }
117 public boolean isProductionRun() {
118 return this == PRODUCTION;
119 }
120
121 // When <code>CDSAppTester::checkExecution(out, runMode)</code> is called, has the application been
122 // executed? If so, <code>out</code> should contain logs printed by the application's own logic.
123 public boolean isApplicationExecuted() {
124 return (this != ASSEMBLY) && (this != DUMP_STATIC);
125 }
126 }
127
128 public boolean isDumping(RunMode runMode) {
129 if (isStaticWorkflow()) {
130 return runMode == RunMode.DUMP_STATIC;
131 } else if (isDynamicWorkflow()) {
132 return runMode == RunMode.DUMP_DYNAMIC;
133 } else if (isAOTWorkflow()) {
134 return runMode == RunMode.TRAINING || runMode == RunMode.ASSEMBLY;
135 } else {
136 return false;
137 }
138 }
139
140 public final String name() {
141 return this.name;
142 }
143
144 // optional
145 public String[] vmArgs(RunMode runMode) {
146 return new String[0];
147 }
148
149 // optional
150 public String classpath(RunMode runMode) {
151 return null;
152 }
153
154 // optional
155 public String modulepath(RunMode runMode) {
156 return null;
169 public final void setCheckExitValue(boolean b) {
170 checkExitValue = b;
171 }
172
173 public final void useWhiteBox(String whiteBoxJar) {
174 this.whiteBoxJar = whiteBoxJar;
175 }
176
177 public final boolean isStaticWorkflow() {
178 return workflow == Workflow.STATIC;
179 }
180
181 public final boolean isDynamicWorkflow() {
182 return workflow == Workflow.DYNAMIC;
183 }
184
185 public final boolean isAOTWorkflow() {
186 return workflow == Workflow.AOT;
187 }
188
189 private String logToFile(String logFile, String... logTags) {
190 StringBuilder sb = new StringBuilder("-Xlog:arguments");
191 String prefix = ",";
192 for (String tag : logTags) {
193 sb.append(prefix);
194 sb.append(tag);
195 }
196 sb.append(":file=" + logFile + "::filesize=0");
197 return sb.toString();
198 }
199
200 private void listOutputFile(String file) {
201 File f = new File(file);
202 if (f.exists()) {
203 System.out.println("[output file: " + file + " " + f.length() + " bytes]");
204 } else {
205 System.out.println("[output file: " + file + " does not exist]");
206 }
207 }
208
209 private OutputAnalyzer executeAndCheck(String[] cmdLine, RunMode runMode, String... logFiles) throws Exception {
210 ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(cmdLine);
211 Process process = pb.start();
212 OutputAnalyzer output = CDSTestUtils.executeAndLog(process, runMode.toString());
213 for (String logFile : logFiles) {
214 listOutputFile(logFile);
215 }
216 if (checkExitValue) {
217 output.shouldHaveExitValue(0);
218 }
219 output.shouldNotContain(CDSTestUtils.MSG_STATIC_FIELD_MAY_HOLD_DIFFERENT_VALUE);
220 CDSTestUtils.checkCommonExecExceptions(output);
221 checkExecution(output, runMode);
222 return output;
223 }
224
225 private String[] addCommonVMArgs(RunMode runMode, String[] cmdLine) {
226 cmdLine = addClassOrModulePath(runMode, cmdLine);
227 cmdLine = addWhiteBox(cmdLine);
228 return cmdLine;
229 }
230
231 private String[] addClassOrModulePath(RunMode runMode, String[] cmdLine) {
232 String cp = classpath(runMode);
233 if (cp == null) {
234 // Override the "-cp ...." added by Jtreg
235 cmdLine = StringArrayUtils.concat(cmdLine, "-Djava.class.path=");
236 } else {
237 cmdLine = StringArrayUtils.concat(cmdLine, "-cp", cp);
238 }
239 String mp = modulepath(runMode);
244 }
245
246 private String[] addWhiteBox(String[] cmdLine) {
247 if (whiteBoxJar != null) {
248 cmdLine = StringArrayUtils.concat(cmdLine,
249 "-XX:+UnlockDiagnosticVMOptions",
250 "-XX:+WhiteBoxAPI",
251 "-Xbootclasspath/a:" + whiteBoxJar);
252 }
253 return cmdLine;
254 }
255
256 private OutputAnalyzer recordAOTConfiguration() throws Exception {
257 RunMode runMode = RunMode.TRAINING;
258 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
259 "-XX:AOTMode=record",
260 "-XX:AOTConfiguration=" + aotConfigurationFile,
261 logToFile(aotConfigurationFileLog,
262 "class+load=debug",
263 "aot=debug",
264 "cds=debug",
265 "cds+class=debug"));
266 cmdLine = addCommonVMArgs(runMode, cmdLine);
267 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
268 return executeAndCheck(cmdLine, runMode, aotConfigurationFile, aotConfigurationFileLog);
269 }
270
271 private OutputAnalyzer createAOTCacheOneStep() throws Exception {
272 RunMode runMode = RunMode.TRAINING;
273 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
274 "-XX:AOTMode=record",
275 "-XX:AOTCacheOutput=" + aotCacheFile,
276 logToFile(aotCacheFileLog,
277 "class+load=debug",
278 "cds=debug",
279 "cds+class=debug"));
280 cmdLine = addCommonVMArgs(runMode, cmdLine);
281 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
282 OutputAnalyzer out = executeAndCheck(cmdLine, runMode, aotCacheFile, aotCacheFileLog);
283 listOutputFile(aotCacheFileLog + ".0"); // the log file for the training run
284 return out;
285 }
286
287 private OutputAnalyzer createClassList() throws Exception {
288 RunMode runMode = RunMode.TRAINING;
289 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
290 "-Xshare:off",
291 "-XX:DumpLoadedClassList=" + classListFile,
292 logToFile(classListFileLog,
293 "class+load=debug"));
294 cmdLine = addCommonVMArgs(runMode, cmdLine);
295 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
296 return executeAndCheck(cmdLine, runMode, classListFile, classListFileLog);
297 }
298
299 private OutputAnalyzer dumpStaticArchive() throws Exception {
300 RunMode runMode = RunMode.DUMP_STATIC;
301 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
302 "-Xlog:aot",
303 "-Xlog:aot+heap=error",
304 "-Xlog:cds",
305 "-Xshare:dump",
306 "-XX:SharedArchiveFile=" + staticArchiveFile,
307 "-XX:SharedClassListFile=" + classListFile,
308 logToFile(staticArchiveFileLog,
309 "aot=debug",
310 "cds=debug",
311 "cds+class=debug",
312 "aot+heap=warning",
313 "cds+resolve=debug"));
314 cmdLine = addCommonVMArgs(runMode, cmdLine);
315 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
316 return executeAndCheck(cmdLine, runMode, staticArchiveFile, staticArchiveFileLog);
317 }
318
319 private OutputAnalyzer createAOTCache() throws Exception {
320 RunMode runMode = RunMode.ASSEMBLY;
321 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
322 "-Xlog:aot",
323 "-Xlog:aot+heap=error",
324 "-Xlog:cds",
325 "-XX:AOTMode=create",
326 "-XX:AOTConfiguration=" + aotConfigurationFile,
327 "-XX:AOTCache=" + aotCacheFile,
328 logToFile(aotCacheFileLog,
329 "aot=debug",
330 "cds=debug",
331 "cds+class=debug",
332 "aot+heap=warning",
333 "cds+resolve=debug"));
334 cmdLine = addCommonVMArgs(runMode, cmdLine);
335 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
336 return executeAndCheck(cmdLine, runMode, aotCacheFile, aotCacheFileLog);
337 }
338
339 // Creating a dynamic CDS archive (with -XX:ArchiveClassesAtExit=<foo>.jsa) requires that the current
340 // JVM process is using a static archive (which is usually the default CDS archive included in the JDK).
341 // However, if the JDK doesn't include a default CDS archive that's compatible with the set of
342 // VM options used by this test, we need to create a temporary static archive to be used with -XX:ArchiveClassesAtExit.
343 private String getBaseArchiveForDynamicArchive() throws Exception {
344 WhiteBox wb = WhiteBox.getWhiteBox();
345 if (wb.isSharingEnabled()) {
346 // This current JVM is able to use a default CDS archive included by the JDK, so
347 // if we launch a JVM child process (with the same set of options as the current JVM),
348 // that process is also able to use the same default CDS archive for creating
349 // a dynamic archive.
350 return null;
351 } else {
352 // This current JVM is unable to use a default CDS archive, so let's create a temporary
353 // static archive to be used with -XX:ArchiveClassesAtExit.
357 opts.setArchiveName(tempBaseArchiveFile);
358 opts.addSuffix("-Djava.class.path=");
359 OutputAnalyzer out = CDSTestUtils.createArchive(opts);
360 CDSTestUtils.checkBaseDump(out);
361 }
362 return tempBaseArchiveFile;
363 }
364 }
365
366 private OutputAnalyzer dumpDynamicArchive() throws Exception {
367 RunMode runMode = RunMode.DUMP_DYNAMIC;
368 String[] cmdLine = new String[0];
369 String baseArchive = getBaseArchiveForDynamicArchive();
370 if (isDynamicWorkflow()) {
371 // "classic" dynamic archive
372 cmdLine = StringArrayUtils.concat(vmArgs(runMode),
373 "-Xlog:aot",
374 "-Xlog:cds",
375 "-XX:ArchiveClassesAtExit=" + dynamicArchiveFile,
376 logToFile(dynamicArchiveFileLog,
377 "aot=debug",
378 "cds=debug",
379 "cds+class=debug",
380 "cds+resolve=debug",
381 "class+load=debug"));
382 cmdLine = addCommonVMArgs(runMode, cmdLine);
383 }
384 if (baseArchive != null) {
385 cmdLine = StringArrayUtils.concat(cmdLine, "-XX:SharedArchiveFile=" + baseArchive);
386 }
387 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
388 return executeAndCheck(cmdLine, runMode, dynamicArchiveFile, dynamicArchiveFileLog);
389 }
390
391 public OutputAnalyzer productionRun() throws Exception {
392 return productionRun(null, null);
393 }
394
395 public OutputAnalyzer productionRun(String[] extraVmArgs) throws Exception {
396 return productionRun(extraVmArgs, null);
397 }
398
399 // After calling run(String[]), you can call this method to run the app again, with the AOTCache
400 // using different args to the VM and application.
401 public OutputAnalyzer productionRun(String[] extraVmArgs, String[] extraAppArgs) throws Exception {
402 RunMode runMode = RunMode.PRODUCTION;
403 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
404 "-XX:+UnlockDiagnosticVMOptions",
405 "-XX:VerifyArchivedFields=2", // make sure archived heap objects are good.
406 logToFile(productionRunLog(), "aot", "cds"));
407 cmdLine = addCommonVMArgs(runMode, cmdLine);
408
409 if (isStaticWorkflow()) {
410 cmdLine = StringArrayUtils.concat(cmdLine, "-Xshare:on", "-XX:SharedArchiveFile=" + staticArchiveFile);
411 } else if (isDynamicWorkflow()) {
412 cmdLine = StringArrayUtils.concat(cmdLine, "-Xshare:on", "-XX:SharedArchiveFile=" + dynamicArchiveFile);
413 } else if (isAOTWorkflow()) {
414 cmdLine = StringArrayUtils.concat(cmdLine, "-XX:AOTMode=on", "-XX:AOTCache=" + aotCacheFile);
415 }
416
417 if (extraVmArgs != null) {
418 cmdLine = StringArrayUtils.concat(cmdLine, extraVmArgs);
419 }
420
421 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
422
423 if (extraAppArgs != null) {
424 cmdLine = StringArrayUtils.concat(cmdLine, extraAppArgs);
425 }
426
427 OutputAnalyzer out = executeAndCheck(cmdLine, runMode, productionRunLog());
428 numProductionRuns ++;
429 return out;
430 }
431
432 public void run(String... args) throws Exception {
433 String err = "Must have at least one command line argument of the following: ";
434 String prefix = "";
435 for (Workflow wf : Workflow.values()) {
436 err += prefix;
437 err += wf;
438 prefix = ", ";
439 }
440 if (args.length < 1) {
441 throw new RuntimeException(err);
442 } else {
443 if (args[0].equals("STATIC")) {
444 runStaticWorkflow();
445 } else if (args[0].equals("DYNAMIC")) {
446 runDynamicWorkflow();
447 } else if (args[0].equals("AOT")) {
448 runAOTWorkflow(args);
449 } else {
450 throw new RuntimeException(err);
451 }
452 }
453 }
454
455 public void runStaticWorkflow() throws Exception {
456 this.workflow = Workflow.STATIC;
457 createClassList();
458 dumpStaticArchive();
459 productionRun();
460 }
461
462 public void runDynamicWorkflow() throws Exception {
463 this.workflow = Workflow.DYNAMIC;
464 dumpDynamicArchive();
465 productionRun();
466 }
467
468 // See JEP 483
469 public void runAOTWorkflow(String... args) throws Exception {
470 this.workflow = Workflow.AOT;
471 boolean oneStepTraining = true; // by default use onestep trainning
472
473 if (System.getProperty("CDSAppTester.two.step.training") != null) {
474 oneStepTraining = false;
475 }
476
477 if (args.length > 1) {
478 // Tests such as test/hotspot/jtreg/runtime/cds/appcds/aotCache/SpecialCacheNames.java
479 // use --one-step-training or --two-step-training to force a certain training workflow.
480 if (args[1].equals("--one-step-training")) {
481 oneStepTraining = true;
482 } else if (args[1].equals("--two-step-training")) {
483 oneStepTraining = false;
484 } else {
485 throw new RuntimeException("Unknown option: " + args[1]);
486 }
487 }
488
489 if (oneStepTraining) {
490 try {
491 inOneStepTraining = true;
492 createAOTCacheOneStep();
493 } finally {
494 inOneStepTraining = false;
495 }
496 } else {
497 recordAOTConfiguration();
498 createAOTCache();
499 }
500 productionRun();
501 }
502
503 // See JEP 483; stop at the assembly run; do not execute production run
504 public void runAOTAssemblyWorkflow() throws Exception {
505 this.workflow = Workflow.AOT;
506 recordAOTConfiguration();
507 createAOTCache();
508 }
509 }
|
36 * You can define the application by overridding the vmArgs(), classpath() and appCommandLine()
37 * methods. Application-specific validation checks can be implemented with checkExecution().
38 *
39 * The AOT workflow runs with one-step training by default. For debugging purposes, run
40 * jtreg with -vmoption:-DCDSAppTester.two.step.training=true. This will run -XX:AOTMode=record
41 * and -XX:AOTMode=record in two separate processes that you can rerun easily inside a debugger.
42 * Also, the log files are easier to read.
43 */
44 abstract public class CDSAppTester {
45 private final String name;
46 private final String classListFile;
47 private final String classListFileLog;
48 private final String aotConfigurationFile;
49 private final String aotConfigurationFileLog;
50 private final String staticArchiveFile;
51 private final String staticArchiveFileLog;
52 private final String aotCacheFile;
53 private final String aotCacheFileLog;
54 private final String dynamicArchiveFile;
55 private final String dynamicArchiveFileLog;
56 private final String cdsFile; // new workflow: -XX:CacheDataStore=<foo>.cds
57 private final String cdsFileLog;
58 private final String cdsFilePreImage; // new workflow: -XX:CacheDataStore=<foo>.cds
59 private final String cdsFilePreImageLog;
60 private final String tempBaseArchiveFile;
61 private int numProductionRuns = 0;
62 private String whiteBoxJar = null;
63 private boolean inOneStepTraining = false;
64
65 /**
66 * All files created in the CDS/AOT workflow will be name + extension. E.g.
67 * - name.aot
68 * - name.aotconfig
69 * - name.classlist
70 * - name.jsa
71 */
72 public CDSAppTester(String name) {
73 if (CDSTestUtils.DYNAMIC_DUMP) {
74 throw new SkippedException("Tests based on CDSAppTester should be excluded when -Dtest.dynamic.cds.archive is specified");
75 }
76
77 this.name = name;
78 classListFile = name() + ".classlist";
79 classListFileLog = logFileName(classListFile);
80 aotConfigurationFile = name() + ".aotconfig";
81 aotConfigurationFileLog = logFileName(aotConfigurationFile);
82 staticArchiveFile = name() + ".static.jsa";
83 staticArchiveFileLog = logFileName(staticArchiveFile);
84 aotCacheFile = name() + ".aot";
85 aotCacheFileLog = logFileName(aotCacheFile);;
86 dynamicArchiveFile = name() + ".dynamic.jsa";
87 dynamicArchiveFileLog = logFileName(dynamicArchiveFile);
88 cdsFile = name() + ".cds";
89 cdsFileLog = logFileName(cdsFile);
90 cdsFilePreImage = cdsFile + ".preimage";
91 cdsFilePreImageLog = logFileName(cdsFilePreImage);
92 tempBaseArchiveFile = name() + ".temp-base.jsa";
93 }
94
95 private String productionRunLog() {
96 if (numProductionRuns == 0) {
97 return logFileName(name() + ".production");
98 } else {
99 return logFileName(name() + ".production." + numProductionRuns);
100 }
101 }
102
103 private static String logFileName(String file) {
104 file = file.replace("\"", "%22");
105 file = file.replace("'", "%27");
106 return file + ".log";
107 }
108
109 private enum Workflow {
110 STATIC, // classic -Xshare:dump workflow
111 DYNAMIC, // classic -XX:ArchiveClassesAtExit
112 AOT, // JEP 483 Ahead-of-Time Class Loading & Linking
113 LEYDEN, // The new "one step training workflow" -- see JDK-8320264
114 }
115
116 public enum RunMode {
117 TRAINING, // -XX:DumpLoadedClassList OR {-XX:AOTMode=record -XX:AOTConfiguration}
118 TRAINING0, // LEYDEN only
119 TRAINING1, // LEYDEN only (assembly phase, app logic not executed)
120 DUMP_STATIC, // -Xshare:dump
121 DUMP_DYNAMIC, // -XX:ArchiveClassesArExit
122 ASSEMBLY, // JEP 483 (assembly phase, app logic not executed)
123 PRODUCTION; // Running with the CDS archive produced from the above steps
124
125 public boolean isStaticDump() {
126 return this == DUMP_STATIC;
127 }
128 public boolean isProductionRun() {
129 return this == PRODUCTION;
130 }
131
132 // When <code>CDSAppTester::checkExecution(out, runMode)</code> is called, has the application been
133 // executed? If so, <code>out</code> should contain logs printed by the application's own logic.
134 public boolean isApplicationExecuted() {
135 return (this != TRAINING1) && (this != ASSEMBLY) && (this != DUMP_STATIC);
136 }
137 }
138
139 public boolean isDumping(RunMode runMode) {
140 if (isStaticWorkflow()) {
141 return runMode == RunMode.DUMP_STATIC;
142 } else if (isDynamicWorkflow()) {
143 return runMode == RunMode.DUMP_DYNAMIC;
144 } else if (isAOTWorkflow()) {
145 return runMode == RunMode.TRAINING || runMode == RunMode.ASSEMBLY;
146 } else {
147 return runMode == RunMode.TRAINING || runMode == RunMode.TRAINING0 || runMode == RunMode.TRAINING1;
148 }
149 }
150
151 public final String name() {
152 return this.name;
153 }
154
155 // optional
156 public String[] vmArgs(RunMode runMode) {
157 return new String[0];
158 }
159
160 // optional
161 public String classpath(RunMode runMode) {
162 return null;
163 }
164
165 // optional
166 public String modulepath(RunMode runMode) {
167 return null;
180 public final void setCheckExitValue(boolean b) {
181 checkExitValue = b;
182 }
183
184 public final void useWhiteBox(String whiteBoxJar) {
185 this.whiteBoxJar = whiteBoxJar;
186 }
187
188 public final boolean isStaticWorkflow() {
189 return workflow == Workflow.STATIC;
190 }
191
192 public final boolean isDynamicWorkflow() {
193 return workflow == Workflow.DYNAMIC;
194 }
195
196 public final boolean isAOTWorkflow() {
197 return workflow == Workflow.AOT;
198 }
199
200 public final boolean isLeydenWorkflow() {
201 return workflow == Workflow.LEYDEN;
202 }
203
204 private String logToFile(String logFile, String... logTags) {
205 StringBuilder sb = new StringBuilder("-Xlog:arguments");
206 String prefix = ",";
207 for (String tag : logTags) {
208 sb.append(prefix);
209 sb.append(tag);
210 }
211 sb.append(":file=" + logFile + "::filesize=0");
212 return sb.toString();
213 }
214
215 private void listOutputFile(String file) {
216 File f = new File(file);
217 if (f.exists()) {
218 System.out.println("[output file: " + file + " " + f.length() + " bytes]");
219 } else {
220 System.out.println("[output file: " + file + " does not exist]");
221 }
222 }
223
224 private OutputAnalyzer executeAndCheck(String[] cmdLine, RunMode runMode, String... logFiles) throws Exception {
225 ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(cmdLine);
226 Process process = pb.start();
227 OutputAnalyzer output = CDSTestUtils.executeAndLog(process, runMode.toString());
228 for (String logFile : logFiles) {
229 listOutputFile(logFile);
230 }
231 if (checkExitValue) {
232 output.shouldHaveExitValue(0);
233 }
234 //output.shouldNotContain(CDSTestUtils.MSG_STATIC_FIELD_MAY_HOLD_DIFFERENT_VALUE); // FIXME -- leyden+JEP483 merge
235 CDSTestUtils.checkCommonExecExceptions(output);
236 checkExecution(output, runMode);
237 return output;
238 }
239
240 private String[] addCommonVMArgs(RunMode runMode, String[] cmdLine) {
241 cmdLine = addClassOrModulePath(runMode, cmdLine);
242 cmdLine = addWhiteBox(cmdLine);
243 return cmdLine;
244 }
245
246 private String[] addClassOrModulePath(RunMode runMode, String[] cmdLine) {
247 String cp = classpath(runMode);
248 if (cp == null) {
249 // Override the "-cp ...." added by Jtreg
250 cmdLine = StringArrayUtils.concat(cmdLine, "-Djava.class.path=");
251 } else {
252 cmdLine = StringArrayUtils.concat(cmdLine, "-cp", cp);
253 }
254 String mp = modulepath(runMode);
259 }
260
261 private String[] addWhiteBox(String[] cmdLine) {
262 if (whiteBoxJar != null) {
263 cmdLine = StringArrayUtils.concat(cmdLine,
264 "-XX:+UnlockDiagnosticVMOptions",
265 "-XX:+WhiteBoxAPI",
266 "-Xbootclasspath/a:" + whiteBoxJar);
267 }
268 return cmdLine;
269 }
270
271 private OutputAnalyzer recordAOTConfiguration() throws Exception {
272 RunMode runMode = RunMode.TRAINING;
273 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
274 "-XX:AOTMode=record",
275 "-XX:AOTConfiguration=" + aotConfigurationFile,
276 logToFile(aotConfigurationFileLog,
277 "class+load=debug",
278 "aot=debug",
279 "aot+class=debug"));
280 cmdLine = addCommonVMArgs(runMode, cmdLine);
281 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
282 return executeAndCheck(cmdLine, runMode, aotConfigurationFile, aotConfigurationFileLog);
283 }
284
285 private OutputAnalyzer createAOTCacheOneStep() throws Exception {
286 RunMode runMode = RunMode.TRAINING;
287 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
288 "-XX:AOTMode=record",
289 "-XX:AOTCacheOutput=" + aotCacheFile,
290 logToFile(aotCacheFileLog,
291 "class+load=debug",
292 "aot=debug",
293 "aot+class=debug"));
294 cmdLine = addCommonVMArgs(runMode, cmdLine);
295 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
296 OutputAnalyzer out = executeAndCheck(cmdLine, runMode, aotCacheFile, aotCacheFileLog);
297 listOutputFile(aotCacheFileLog + ".0"); // the log file for the training run
298 return out;
299 }
300
301 private OutputAnalyzer createClassList() throws Exception {
302 RunMode runMode = RunMode.TRAINING;
303 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
304 "-Xshare:off",
305 "-XX:DumpLoadedClassList=" + classListFile,
306 logToFile(classListFileLog,
307 "class+load=debug"));
308 cmdLine = addCommonVMArgs(runMode, cmdLine);
309 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
310 return executeAndCheck(cmdLine, runMode, classListFile, classListFileLog);
311 }
312
313 private OutputAnalyzer dumpStaticArchive() throws Exception {
314 RunMode runMode = RunMode.DUMP_STATIC;
315 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
316 "-Xlog:cds",
317 "-Xlog:cds+heap=error",
318 "-Xshare:dump",
319 "-XX:SharedArchiveFile=" + staticArchiveFile,
320 "-XX:SharedClassListFile=" + classListFile,
321 logToFile(staticArchiveFileLog,
322 "cds=debug",
323 "cds+class=debug",
324 "cds+heap=warning",
325 "cds+resolve=debug"));
326 cmdLine = addCommonVMArgs(runMode, cmdLine);
327 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
328 return executeAndCheck(cmdLine, runMode, staticArchiveFile, staticArchiveFileLog);
329 }
330
331 private OutputAnalyzer createAOTCache() throws Exception {
332 RunMode runMode = RunMode.ASSEMBLY;
333 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
334 "-Xlog:aot",
335 "-Xlog:aot+heap=error",
336 "-XX:AOTMode=create",
337 "-XX:AOTConfiguration=" + aotConfigurationFile,
338 "-XX:AOTCache=" + aotCacheFile,
339 logToFile(aotCacheFileLog,
340 "aot=debug",
341 "aot+class=debug",
342 "aot+heap=warning",
343 "aot+resolve=debug"));
344 cmdLine = addCommonVMArgs(runMode, cmdLine);
345 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
346 return executeAndCheck(cmdLine, runMode, aotCacheFile, aotCacheFileLog);
347 }
348
349 // Creating a dynamic CDS archive (with -XX:ArchiveClassesAtExit=<foo>.jsa) requires that the current
350 // JVM process is using a static archive (which is usually the default CDS archive included in the JDK).
351 // However, if the JDK doesn't include a default CDS archive that's compatible with the set of
352 // VM options used by this test, we need to create a temporary static archive to be used with -XX:ArchiveClassesAtExit.
353 private String getBaseArchiveForDynamicArchive() throws Exception {
354 WhiteBox wb = WhiteBox.getWhiteBox();
355 if (wb.isSharingEnabled()) {
356 // This current JVM is able to use a default CDS archive included by the JDK, so
357 // if we launch a JVM child process (with the same set of options as the current JVM),
358 // that process is also able to use the same default CDS archive for creating
359 // a dynamic archive.
360 return null;
361 } else {
362 // This current JVM is unable to use a default CDS archive, so let's create a temporary
363 // static archive to be used with -XX:ArchiveClassesAtExit.
367 opts.setArchiveName(tempBaseArchiveFile);
368 opts.addSuffix("-Djava.class.path=");
369 OutputAnalyzer out = CDSTestUtils.createArchive(opts);
370 CDSTestUtils.checkBaseDump(out);
371 }
372 return tempBaseArchiveFile;
373 }
374 }
375
376 private OutputAnalyzer dumpDynamicArchive() throws Exception {
377 RunMode runMode = RunMode.DUMP_DYNAMIC;
378 String[] cmdLine = new String[0];
379 String baseArchive = getBaseArchiveForDynamicArchive();
380 if (isDynamicWorkflow()) {
381 // "classic" dynamic archive
382 cmdLine = StringArrayUtils.concat(vmArgs(runMode),
383 "-Xlog:aot",
384 "-Xlog:cds",
385 "-XX:ArchiveClassesAtExit=" + dynamicArchiveFile,
386 logToFile(dynamicArchiveFileLog,
387 "cds=debug",
388 "cds+class=debug",
389 "cds+resolve=debug",
390 "class+load=debug"));
391 cmdLine = addCommonVMArgs(runMode, cmdLine);
392 }
393 if (baseArchive != null) {
394 cmdLine = StringArrayUtils.concat(cmdLine, "-XX:SharedArchiveFile=" + baseArchive);
395 }
396 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
397 return executeAndCheck(cmdLine, runMode, dynamicArchiveFile, dynamicArchiveFileLog);
398 }
399
400 private String trainingLog(String file) {
401 return logToFile(file,
402 "cds=debug",
403 "cds+class=debug",
404 "cds+heap=warning",
405 "cds+resolve=debug");
406 }
407
408 // normal training workflow (main JVM process spawns child process)
409 private OutputAnalyzer trainingRun() throws Exception {
410 RunMode runMode = RunMode.TRAINING;
411 File f = new File(cdsFile);
412 f.delete();
413 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
414 "-XX:+AOTClassLinking",
415 "-XX:+ArchiveDynamicProxies",
416 //"-XX:+ArchiveReflectionData",
417 "-XX:CacheDataStore=" + cdsFile,
418 "-cp", classpath(runMode),
419 // Use PID to distinguish the logs of the training process
420 // and the forked final image dump process.
421 "-Xlog:cds::uptime,level,tags,pid",
422 trainingLog(cdsFileLog));
423 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
424 OutputAnalyzer out = executeAndCheck(cmdLine, runMode, cdsFile, cdsFileLog);
425 listOutputFile(cdsFileLog + ".0"); // the preimage dump
426 return out;
427 }
428
429 // "split" training workflow (launch the two processes manually, for easier debugging);
430 private OutputAnalyzer trainingRun0() throws Exception {
431 RunMode runMode = RunMode.TRAINING0;
432 File f = new File(cdsFile);
433 f.delete();
434 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
435 "-XX:+UnlockDiagnosticVMOptions",
436 "-XX:+CDSManualFinalImage",
437 "-XX:+AOTClassLinking",
438 "-XX:+ArchiveDynamicProxies",
439 //"-XX:+ArchiveReflectionData",
440 "-XX:CacheDataStore=" + cdsFile,
441 "-cp", classpath(runMode),
442 trainingLog(cdsFilePreImageLog));
443 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
444 return executeAndCheck(cmdLine, runMode, cdsFilePreImage, cdsFilePreImageLog);
445 }
446 private OutputAnalyzer trainingRun1() throws Exception {
447 RunMode runMode = RunMode.TRAINING1;
448 File f = new File(cdsFile);
449 f.delete();
450 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
451 "-XX:+UnlockDiagnosticVMOptions",
452 "-XX:+AOTClassLinking",
453 "-XX:+ArchiveDynamicProxies",
454 //"-XX:+ArchiveReflectionData",
455 "-XX:CacheDataStore=" + cdsFile,
456 "-XX:CDSPreimage=" + cdsFilePreImage,
457 "-cp", classpath(runMode),
458 trainingLog(cdsFileLog));
459 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
460 return executeAndCheck(cmdLine, runMode, cdsFile, cdsFileLog);
461 }
462
463 public OutputAnalyzer productionRun() throws Exception {
464 return productionRun(null, null);
465 }
466
467 public OutputAnalyzer productionRun(String[] extraVmArgs) throws Exception {
468 return productionRun(extraVmArgs, null);
469 }
470
471 // After calling run(String[]), you can call this method to run the app again, with the AOTCache
472 // using different args to the VM and application.
473 public OutputAnalyzer productionRun(String[] extraVmArgs, String[] extraAppArgs) throws Exception {
474 RunMode runMode = RunMode.PRODUCTION;
475 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
476 "-XX:+UnlockDiagnosticVMOptions",
477 "-XX:VerifyArchivedFields=2", // make sure archived heap objects are good.
478 logToFile(productionRunLog(), "aot", "cds"));
479 cmdLine = addCommonVMArgs(runMode, cmdLine);
480
481 if (isStaticWorkflow()) {
482 cmdLine = StringArrayUtils.concat(cmdLine, "-Xshare:on", "-XX:SharedArchiveFile=" + staticArchiveFile);
483 } else if (isDynamicWorkflow()) {
484 cmdLine = StringArrayUtils.concat(cmdLine, "-Xshare:on", "-XX:SharedArchiveFile=" + dynamicArchiveFile);
485 } else if (isAOTWorkflow()) {
486 cmdLine = StringArrayUtils.concat(cmdLine, "-XX:AOTMode=on", "-XX:AOTCache=" + aotCacheFile);
487 } else {
488 cmdLine = StringArrayUtils.concat(cmdLine, "-XX:CacheDataStore=" + cdsFile);
489 }
490
491 if (extraVmArgs != null) {
492 cmdLine = StringArrayUtils.concat(cmdLine, extraVmArgs);
493 }
494
495 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
496
497 if (extraAppArgs != null) {
498 cmdLine = StringArrayUtils.concat(cmdLine, extraAppArgs);
499 }
500
501 OutputAnalyzer out = executeAndCheck(cmdLine, runMode, productionRunLog());
502 numProductionRuns ++;
503 return out;
504 }
505
506 public void run(String... args) throws Exception {
507 String err = "Must have at least one command line argument of the following: ";
508 String prefix = "";
509 for (Workflow wf : Workflow.values()) {
510 err += prefix;
511 err += wf;
512 prefix = ", ";
513 }
514 if (args.length < 1) {
515 throw new RuntimeException(err);
516 } else {
517 if (args[0].equals("STATIC")) {
518 runStaticWorkflow();
519 } else if (args[0].equals("DYNAMIC")) {
520 runDynamicWorkflow();
521 } else if (args[0].equals("AOT")) {
522 runAOTWorkflow(args);
523 } else if (args[0].equals("LEYDEN")) {
524 runLeydenWorkflow(false);
525 } else if (args[0].equals("LEYDEN_TRAINONLY")) {
526 runLeydenWorkflow(true);
527 } else {
528 throw new RuntimeException(err);
529 }
530 }
531 }
532
533 public void runStaticWorkflow() throws Exception {
534 this.workflow = Workflow.STATIC;
535 createClassList();
536 dumpStaticArchive();
537 productionRun();
538 }
539
540 public void runDynamicWorkflow() throws Exception {
541 this.workflow = Workflow.DYNAMIC;
542 dumpDynamicArchive();
543 productionRun();
544 }
545
546 // See JEP 483; stop at the assembly run; do not execute production run
547 public void runAOTAssemblyWorkflow() throws Exception {
548 this.workflow = Workflow.AOT;
549 recordAOTConfiguration();
550 createAOTCache();
551 }
552
553 // See JEP 483
554 public void runAOTWorkflow(String... args) throws Exception {
555 this.workflow = Workflow.AOT;
556 boolean oneStepTraining = true; // by default use onestep trainning
557
558 if (System.getProperty("CDSAppTester.two.step.training") != null) {
559 oneStepTraining = false;
560 }
561
562 if (args.length > 1) {
563 // Tests such as test/hotspot/jtreg/runtime/cds/appcds/aotCache/SpecialCacheNames.java
564 // use --one-step-training or --two-step-training to force a certain training workflow.
565 if (args[1].equals("--one-step-training")) {
566 oneStepTraining = true;
567 } else if (args[1].equals("--two-step-training")) {
568 oneStepTraining = false;
569 } else {
570 throw new RuntimeException("Unknown option: " + args[1]);
571 }
572 }
573
574 if (oneStepTraining) {
575 try {
576 inOneStepTraining = true;
577 createAOTCacheOneStep();
578 } finally {
579 inOneStepTraining = false;
580 }
581 } else {
582 recordAOTConfiguration();
583 createAOTCache();
584 }
585 productionRun();
586 }
587
588 private void runLeydenWorkflow(boolean trainOnly) throws Exception {
589 this.workflow = Workflow.LEYDEN;
590 if (System.getProperty("CDSAppTester.split.new.workflow") != null) {
591 trainingRun0();
592 trainingRun1();
593 } else {
594 trainingRun();
595 }
596 if (!trainOnly) {
597 productionRun();
598 }
599 }
600 }
|