< prev index next >

test/lib/jdk/test/lib/cds/CDSAppTester.java

Print this page

 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 }
< prev index next >