1 /*
2 * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24
25 /*
26 * @test
27 * @summary "AOT" aliases for traditional CDS command-line options
28 * @requires vm.cds
29 * @requires vm.flagless
30 * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes
31 * @build Hello
32 * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar hello.jar Hello
33 * @run driver AOTFlags
34 */
35
36 import java.io.File;
37 import jdk.test.lib.cds.CDSTestUtils;
38 import jdk.test.lib.helpers.ClassFileInstaller;
39 import jdk.test.lib.process.OutputAnalyzer;
40 import jdk.test.lib.process.ProcessTools;
41
42 public class AOTFlags {
43 static String appJar = ClassFileInstaller.getJarPath("hello.jar");
44 static String aotConfigFile = "hello.aotconfig";
45 static String aotCacheFile = "hello.aot";
46 static String helloClass = "Hello";
47
48 public static void main(String[] args) throws Exception {
49 positiveTests();
50 negativeTests();
51 }
52
53 static void positiveTests() throws Exception {
54 String hasTrainingDataPattern = "MethodTrainingData *= *[1-9]";
55 String noTrainingDataPattern = "MethodTrainingData *= *0";
56 String hasAOTCodePattern = "Shared file region .ac. .: *[1-9]";
57 String noAOTCodePattern = "Shared file region .ac. .: *0";
58 String hasMappedAOTCodePattern = "Mapped [0-9]+ bytes at address 0x[0-9a-f]+ from AOT Code Cache";
59
60 //----------------------------------------------------------------------
61 printTestCase("Training Run");
62 ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
63 "-XX:AOTMode=record",
64 "-XX:AOTConfiguration=" + aotConfigFile,
65 "-Xlog:aot=debug",
66 "-cp", appJar, helloClass);
67
68 OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "train");
69 out.shouldContain("Hello World");
70 out.shouldContain("AOTConfiguration recorded: " + aotConfigFile);
71 out.shouldMatch(hasTrainingDataPattern);
72 out.shouldMatch(noAOTCodePattern);
73 out.shouldHaveExitValue(0);
74
75 //----------------------------------------------------------------------
76 printTestCase("Assembly Phase (AOTClassLinking unspecified -> should be enabled by default)");
77 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
78 "-XX:AOTMode=create",
79 "-XX:AOTConfiguration=" + aotConfigFile,
80 "-XX:AOTCache=" + aotCacheFile,
81 "-Xlog:aot",
82 "-cp", appJar);
83 out = CDSTestUtils.executeAndLog(pb, "asm");
84 out.shouldContain("AOTCache creation is complete");
85 out.shouldMatch("hello[.]aot");
86 out.shouldMatch(hasTrainingDataPattern);
87 out.shouldMatch(hasAOTCodePattern);
88 out.shouldHaveExitValue(0);
89
90 //----------------------------------------------------------------------
91 printTestCase("Production Run with AOTCache");
92 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
93 "-XX:AOTCache=" + aotCacheFile,
94 "-Xlog:aot",
95 "-Xlog:aot+codecache*",
96 "-cp", appJar, helloClass);
97 out = CDSTestUtils.executeAndLog(pb, "prod");
98 out.shouldContain("Using AOT-linked classes: true (static archive: has aot-linked classes)");
99 out.shouldContain("Opened AOT cache hello.aot.");
100 out.shouldContain("Hello World");
101 out.shouldMatch(hasMappedAOTCodePattern);
102 out.shouldHaveExitValue(0);
103
104 //----------------------------------------------------------------------
105 printTestCase("AOTMode=off");
106 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
107 "-XX:AOTCache=" + aotCacheFile,
108 "--show-version",
109 "-Xlog:aot",
110 "-XX:AOTMode=off",
111 "-cp", appJar, helloClass);
112 out = CDSTestUtils.executeAndLog(pb, "prod");
113 out.shouldNotContain(", sharing");
114 out.shouldNotContain("Opened AOT cache hello.aot.");
115 out.shouldContain("Hello World");
116 out.shouldHaveExitValue(0);
117
118 //----------------------------------------------------------------------
119 printTestCase("AOTMode=auto");
120 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
121 "-XX:AOTCache=" + aotCacheFile,
122 "--show-version",
123 "-Xlog:aot",
124 "-XX:AOTMode=auto",
125 "-cp", appJar, helloClass);
126 out = CDSTestUtils.executeAndLog(pb, "prod");
127 out.shouldContain(", sharing");
128 out.shouldContain("Opened AOT cache hello.aot.");
129 out.shouldContain("Hello World");
130 out.shouldHaveExitValue(0);
131
132 //----------------------------------------------------------------------
133 printTestCase("AOTMode=on");
134 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
135 "-XX:AOTCache=" + aotCacheFile,
136 "--show-version",
137 "-Xlog:aot",
138 "-XX:AOTMode=on",
139 "-cp", appJar, helloClass);
140 out = CDSTestUtils.executeAndLog(pb, "prod");
141 out.shouldContain(", sharing");
142 out.shouldContain("Opened AOT cache hello.aot.");
143 out.shouldContain("Hello World");
144 out.shouldHaveExitValue(0);
145
146 //----------------------------------------------------------------------
147 printTestCase("Assembly Phase with -XX:-AOTClassLinking");
148 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
149 "-XX:AOTMode=create",
150 "-XX:-AOTClassLinking",
151 "-XX:AOTConfiguration=" + aotConfigFile,
152 "-XX:AOTCache=" + aotCacheFile,
153 "-Xlog:aot=debug",
154 "-cp", appJar);
155 out = CDSTestUtils.executeAndLog(pb, "asm");
156 out.shouldContain("AOTCache creation is complete");
157 out.shouldMatch("hello[.]aot");
158 out.shouldMatch(noTrainingDataPattern);
159 out.shouldMatch(noAOTCodePattern);
160 out.shouldHaveExitValue(0);
161
162 //----------------------------------------------------------------------
163 printTestCase("Production Run with AOTCache, which was created with -XX:-AOTClassLinking");
164 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
165 "-XX:AOTCache=" + aotCacheFile,
166 "-Xlog:aot",
167 "-Xlog:aot+codecache*",
168 "-cp", appJar, helloClass);
169 out = CDSTestUtils.executeAndLog(pb, "prod");
170 out.shouldContain("Using AOT-linked classes: false (static archive: no aot-linked classes)");
171 out.shouldContain("Opened AOT cache hello.aot.");
172 out.shouldContain("Hello World");
173 out.shouldNotMatch(hasMappedAOTCodePattern);
174 out.shouldHaveExitValue(0);
175
176 //----------------------------------------------------------------------
177 printTestCase("Training run with -XX:-AOTClassLinking, but assembly run with -XX:+AOTClassLinking");
178 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
179 "-XX:AOTMode=record",
180 "-XX:-AOTClassLinking",
181 "-XX:AOTConfiguration=" + aotConfigFile,
182 "-Xlog:aot=debug",
183 "-cp", appJar, helloClass);
184 out = CDSTestUtils.executeAndLog(pb, "train");
185 out.shouldContain("Hello World");
186 out.shouldContain("AOTConfiguration recorded: " + aotConfigFile);
187 out.shouldHaveExitValue(0);
188
189 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
190 "-XX:AOTMode=create",
191 "-XX:+AOTClassLinking",
192 "-XX:AOTConfiguration=" + aotConfigFile,
193 "-XX:AOTCache=" + aotCacheFile,
194 "-Xlog:aot=debug",
195 "-cp", appJar);
196 out = CDSTestUtils.executeAndLog(pb, "asm");
197 out.shouldContain("AOTCache creation is complete");
198 out.shouldMatch("hello[.]aot");
199 out.shouldHaveExitValue(0);
200
201 //----------------------------------------------------------------------
202 printTestCase("One step training run (JEP-514");
203
204 // Set all AOTMode/AOTCacheOutput/AOTConfiguration
205 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
206 "-XX:AOTMode=record",
207 "-XX:AOTCacheOutput=" + aotCacheFile,
208 "-XX:AOTConfiguration=" + aotConfigFile,
209 "-Xlog:aot=debug",
210 "-cp", appJar, helloClass);
211 out = CDSTestUtils.executeAndLog(pb, "ontstep-train");
212 out.shouldContain("Hello World");
213 out.shouldContain("AOTConfiguration recorded: " + aotConfigFile);
214 out.shouldContain("AOTCache creation is complete: hello.aot");
215 out.shouldHaveExitValue(0);
216
217 // Set AOTCacheOutput/AOTConfiguration only; Ergo for: AOTMode=record
218 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
219 "-XX:AOTCacheOutput=" + aotCacheFile,
220 "-XX:AOTConfiguration=" + aotConfigFile,
221 "-Xlog:aot=debug",
222 "-cp", appJar, helloClass);
223 out = CDSTestUtils.executeAndLog(pb, "ontstep-train");
224 out.shouldContain("Hello World");
225 out.shouldContain("AOTConfiguration recorded: " + aotConfigFile);
226 out.shouldContain("AOTCache creation is complete: hello.aot");
227 out.shouldHaveExitValue(0);
228
229 // Set AOTCacheOutput only; Ergo for: AOTMode=record, AOTConfiguration=<temp>
230 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
231 "-XX:AOTCacheOutput=" + aotCacheFile,
232 "-Xlog:aot=debug",
233 "-cp", appJar, helloClass);
234 out = CDSTestUtils.executeAndLog(pb, "ontstep-train");
235 out.shouldContain("Hello World");
236 out.shouldContain("Temporary AOTConfiguration recorded: " + aotCacheFile + ".config");
237 out.shouldContain("AOTCache creation is complete: hello.aot");
238 out.shouldHaveExitValue(0);
239
240 // Quoating of space characters in child JVM process
241 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
242 "-XX:AOTCacheOutput=" + aotCacheFile,
243 "-Dmy.prop=My string -Xshare:off here", // -Xshare:off should not be treated as a single VM opt for the child JVM
244 "-Xlog:aot=debug",
245 "-cp", appJar, helloClass);
246 out = CDSTestUtils.executeAndLog(pb, "ontstep-train");
247 out.shouldContain("Hello World");
248 out.shouldContain("AOTCache creation is complete: hello.aot");
249 out.shouldMatch("Picked up JAVA_TOOL_OPTIONS:.* -Dmy.prop=My' 'string' '-Xshare:off' 'here");
250 out.shouldHaveExitValue(0);
251
252 // Training run with -XX:+PrintTieredEvents (see JDK-8362530).
253 printTestCase("Training run with -XX:+PrintTieredEvents");
254 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
255 "-XX:AOTMode=record",
256 "-XX:+PrintTieredEvents",
257 "-XX:AOTConfiguration=" + aotConfigFile,
258 "-cp", appJar, helloClass);
259 out = CDSTestUtils.executeAndLog(pb, "train-with-tiered-events");
260 out.shouldHaveExitValue(0);
261 }
262
263 static void negativeTests() throws Exception {
264 //----------------------------------------------------------------------
265 printTestCase("Mixing old and new options");
266 String mixOldNewErrSuffix = " cannot be used at the same time with -Xshare:on, -Xshare:auto, "
267 + "-Xshare:off, -Xshare:dump, DumpLoadedClassList, SharedClassListFile, "
268 + "or SharedArchiveFile";
269
270 ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
271 "-Xshare:off",
272 "-XX:AOTConfiguration=" + aotConfigFile,
273 "-cp", appJar, helloClass);
274
275 OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "neg");
276 out.shouldContain("Option AOTConfiguration" + mixOldNewErrSuffix);
277 out.shouldNotHaveExitValue(0);
278
279 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
280 "-XX:SharedArchiveFile=" + aotCacheFile,
281 "-XX:AOTCache=" + aotCacheFile,
282 "-cp", appJar, helloClass);
283 out = CDSTestUtils.executeAndLog(pb, "neg");
284 out.shouldContain("Option AOTCache" + mixOldNewErrSuffix);
285 out.shouldNotHaveExitValue(0);
286
287 //----------------------------------------------------------------------
288 printTestCase("Use AOTConfiguration without AOTMode/AOTCacheOutput");
289 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
290 "-XX:AOTConfiguration=" + aotConfigFile,
291 "-cp", appJar, helloClass);
292
293 out = CDSTestUtils.executeAndLog(pb, "neg");
294 out.shouldContain("AOTConfiguration can only be used with when AOTMode is record or create (selected AOTMode = auto)");
295 out.shouldNotHaveExitValue(0);
296
297 //----------------------------------------------------------------------
298 printTestCase("Use AOTConfiguration with AOTMode=on");
299 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
300 "-XX:AOTMode=on",
301 "-XX:AOTConfiguration=" + aotConfigFile,
302 "-cp", appJar, helloClass);
303
304 out = CDSTestUtils.executeAndLog(pb, "neg");
305 out.shouldContain("AOTConfiguration can only be used with when AOTMode is record or create (selected AOTMode = on)");
306 out.shouldNotHaveExitValue(0);
307
308 //----------------------------------------------------------------------
309 printTestCase("Use AOTMode without AOTCacheOutput or AOTConfiguration");
310 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
311 "-XX:AOTMode=record",
312 "-cp", appJar, helloClass);
313
314 out = CDSTestUtils.executeAndLog(pb, "neg");
315 out.shouldContain("At least one of AOTCacheOutput and AOTConfiguration must be specified when using -XX:AOTMode=record");
316
317 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
318 "-XX:AOTMode=create",
319 "-cp", appJar, helloClass);
320
321 out = CDSTestUtils.executeAndLog(pb, "neg");
322 out.shouldContain("AOTConfiguration must be specified when using -XX:AOTMode=create");
323 out.shouldNotHaveExitValue(0);
324
325 //----------------------------------------------------------------------
326 printTestCase("Bad AOTMode");
327 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
328 "-XX:AOTMode=foo",
329 "-cp", appJar, helloClass);
330
331 out = CDSTestUtils.executeAndLog(pb, "neg");
332 out.shouldContain("Unrecognized value foo for AOTMode. Must be one of the following: off, record, create, auto, on");
333 out.shouldNotHaveExitValue(0);
334
335 //----------------------------------------------------------------------
336 printTestCase("AOTCache specified with -XX:AOTMode=record");
337 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
338 "-XX:AOTMode=record",
339 "-XX:AOTConfiguration=" + aotConfigFile,
340 "-XX:AOTCache=" + aotCacheFile,
341 "-cp", appJar, helloClass);
342
343 out = CDSTestUtils.executeAndLog(pb, "neg");
344 out.shouldContain("AOTCache must not be specified when using -XX:AOTMode=record");
345 out.shouldNotHaveExitValue(0);
346
347 //----------------------------------------------------------------------
348 printTestCase("AOTCache/AOTCacheOutput not specified with -XX:AOTMode=create");
349 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
350 "-XX:AOTMode=create",
351 "-XX:AOTConfiguration=" + aotConfigFile,
352 "-cp", appJar, helloClass);
353
354 out = CDSTestUtils.executeAndLog(pb, "neg");
355 out.shouldContain("AOTCache or AOTCacheOutput must be specified when using -XX:AOTMode=create");
356 out.shouldNotHaveExitValue(0);
357
358 //----------------------------------------------------------------------
359 printTestCase("AOTCache and AOTCacheOutput have different values");
360 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
361 "-XX:AOTMode=create",
362 "-XX:AOTConfiguration=" + aotConfigFile,
363 "-XX:AOTCache=aaa",
364 "-XX:AOTCacheOutput=aaa",
365 "-cp", appJar, helloClass);
366
367 out = CDSTestUtils.executeAndLog(pb, "neg");
368 out.shouldContain("Only one of AOTCache or AOTCacheOutput can be specified");
369 out.shouldNotHaveExitValue(0);
370
371 //----------------------------------------------------------------------
372 printTestCase("No such config file");
373 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
374 "-XX:AOTMode=create",
375 "-XX:AOTConfiguration=no-such-file",
376 "-XX:AOTCache=" + aotCacheFile,
377 "-cp", appJar, helloClass);
378
379 out = CDSTestUtils.executeAndLog(pb, "neg");
380 out.shouldContain("Must be a valid AOT configuration generated by the current JVM: no-such-file");
381 out.shouldNotHaveExitValue(0);
382
383 //----------------------------------------------------------------------
384 printTestCase("AOTConfiguration file cannot be used as a CDS archive");
385
386 // first make sure we have a valid aotConfigFile
387 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
388 "-XX:AOTMode=record",
389 "-XX:AOTConfiguration=" + aotConfigFile,
390 "-cp", appJar, helloClass);
391
392 out = CDSTestUtils.executeAndLog(pb, "train");
393 out.shouldHaveExitValue(0);
394
395 // Cannot use this config file as a AOT cache
396 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
397 "-XX:AOTMode=on",
398 "-XX:AOTCache=" + aotConfigFile,
399 "-cp", appJar, helloClass);
400
401 out = CDSTestUtils.executeAndLog(pb, "neg");
402 out.shouldContain("Not a valid AOT cache (hello.aotconfig)");
403 out.shouldNotHaveExitValue(0);
404
405 // Cannot use this config file as a CDS archive
406 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
407 "-Xshare:on",
408 "-XX:SharedArchiveFile=" + aotConfigFile,
409 "-cp", appJar, helloClass);
410
411 out = CDSTestUtils.executeAndLog(pb, "neg");
412 out.shouldContain("Not a valid shared archive file (hello.aotconfig)");
413 out.shouldNotHaveExitValue(0);
414
415 //----------------------------------------------------------------------
416 printTestCase("Classpath mismatch when creating archive");
417
418 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
419 "-XX:AOTMode=create",
420 "-XX:AOTConfiguration=" + aotConfigFile,
421 "-XX:AOTCache=" + aotCacheFile,
422 "-cp", "noSuchJar.jar");
423
424 out = CDSTestUtils.executeAndLog(pb, "neg");
425 out.shouldContain("class path and/or module path are not compatible with the ones " +
426 "specified when the AOTConfiguration file was recorded");
427 out.shouldContain("Unable to use create AOT cache");
428 out.shouldHaveExitValue(1);
429
430 //----------------------------------------------------------------------
431 printTestCase("Cannot use multiple paths in AOTConfiguration");
432
433 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
434 "-XX:AOTMode=record",
435 "-XX:AOTConfiguration=" + aotConfigFile + File.pathSeparator + "dummy",
436 "-cp", "noSuchJar.jar");
437
438 out = CDSTestUtils.executeAndLog(pb, "neg");
439 out.shouldContain("Option AOTConfiguration must specify a single file name");
440 out.shouldHaveExitValue(1);
441
442 //----------------------------------------------------------------------
443 printTestCase("Cannot use multiple paths in AOTCache");
444
445 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
446 "-XX:AOTMode=create",
447 "-XX:AOTConfiguration=" + aotConfigFile,
448 "-XX:AOTCache=" + aotCacheFile + File.pathSeparator + "dummy",
449 "-cp", "noSuchJar.jar");
450
451 out = CDSTestUtils.executeAndLog(pb, "neg");
452 out.shouldContain("Option AOTCache must specify a single file name");
453 out.shouldHaveExitValue(1);
454
455 //----------------------------------------------------------------------
456 printTestCase("Cannot use a dynamic CDS archive for -XX:AOTCache");
457 String staticArchive = "static.jsa";
458 String dynamicArchive = "dynamic.jsa";
459
460 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
461 "-Xshare:dump",
462 "-XX:SharedArchiveFile=" + staticArchive);
463 out = CDSTestUtils.executeAndLog(pb, "static");
464 out.shouldHaveExitValue(0);
465
466 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
467 "-XX:SharedArchiveFile=" + staticArchive,
468 "-XX:ArchiveClassesAtExit=" + dynamicArchive,
469 "--version");
470 out = CDSTestUtils.executeAndLog(pb, "dynamic");
471 out.shouldHaveExitValue(0);
472
473 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
474 "-Xlog:aot",
475 "-XX:AOTMode=on",
476 "-XX:AOTCache=" + dynamicArchive,
477 "--version");
478
479 out = CDSTestUtils.executeAndLog(pb, "neg");
480 out.shouldContain("Unable to use AOT cache.");
481 out.shouldContain("Not a valid AOT cache (dynamic.jsa)");
482 out.shouldHaveExitValue(1);
483
484 //----------------------------------------------------------------------
485 testEmptyValue("AOTCache");
486 testEmptyValue("AOTConfiguration");
487 testEmptyValue("AOTMode");
488 testEmptyValue("AOTCacheOutput");
489 }
490
491 static void testEmptyValue(String option) throws Exception {
492 printTestCase("Empty values for " + option);
493 ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
494 "-XX:" + option + "=", "--version");
495 OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "neg");
496 out.shouldContain("Improperly specified VM option '" + option + "='");
497 out.shouldHaveExitValue(1);
498 }
499
500 static int testNum = 0;
501 static void printTestCase(String s) {
502 System.out.println("vvvvvvv TEST CASE " + testNum + ": " + s + ": starts here vvvvvvv");
503 testNum++;
504 }
505 }