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 
253     static void negativeTests() throws Exception {
254        //----------------------------------------------------------------------
255         printTestCase("Mixing old and new options");
256         String mixOldNewErrSuffix = " cannot be used at the same time with -Xshare:on, -Xshare:auto, "
257             + "-Xshare:off, -Xshare:dump, DumpLoadedClassList, SharedClassListFile, "
258             + "or SharedArchiveFile";
259 
260         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
261             "-Xshare:off",
262             "-XX:AOTConfiguration=" + aotConfigFile,
263             "-cp", appJar, helloClass);
264 
265         OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "neg");
266         out.shouldContain("Option AOTConfiguration" + mixOldNewErrSuffix);
267         out.shouldNotHaveExitValue(0);
268 
269         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
270             "-XX:SharedArchiveFile=" + aotCacheFile,
271             "-XX:AOTCache=" + aotCacheFile,
272             "-cp", appJar, helloClass);
273         out = CDSTestUtils.executeAndLog(pb, "neg");
274         out.shouldContain("Option AOTCache" + mixOldNewErrSuffix);
275         out.shouldNotHaveExitValue(0);
276 
277         //----------------------------------------------------------------------
278         printTestCase("Use AOTConfiguration without AOTMode/AOTCacheOutput");
279         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
280             "-XX:AOTConfiguration=" + aotConfigFile,
281             "-cp", appJar, helloClass);
282 
283         out = CDSTestUtils.executeAndLog(pb, "neg");
284         out.shouldContain("AOTConfiguration can only be used with when AOTMode is record or create (selected AOTMode = auto)");
285         out.shouldNotHaveExitValue(0);
286 
287         //----------------------------------------------------------------------
288         printTestCase("Use AOTConfiguration with AOTMode=on");
289         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
290             "-XX:AOTMode=on",
291             "-XX:AOTConfiguration=" + aotConfigFile,
292             "-cp", appJar, helloClass);
293 
294         out = CDSTestUtils.executeAndLog(pb, "neg");
295         out.shouldContain("AOTConfiguration can only be used with when AOTMode is record or create (selected AOTMode = on)");
296         out.shouldNotHaveExitValue(0);
297 
298         //----------------------------------------------------------------------
299         printTestCase("Use AOTMode without AOTCacheOutput or AOTConfiguration");
300         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
301             "-XX:AOTMode=record",
302             "-cp", appJar, helloClass);
303 
304         out = CDSTestUtils.executeAndLog(pb, "neg");
305         out.shouldContain("At least one of AOTCacheOutput and AOTConfiguration must be specified when using -XX:AOTMode=record");
306 
307         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
308             "-XX:AOTMode=create",
309             "-cp", appJar, helloClass);
310 
311         out = CDSTestUtils.executeAndLog(pb, "neg");
312         out.shouldContain("AOTConfiguration must be specified when using -XX:AOTMode=create");
313         out.shouldNotHaveExitValue(0);
314 
315         //----------------------------------------------------------------------
316         printTestCase("Bad AOTMode");
317         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
318             "-XX:AOTMode=foo",
319             "-cp", appJar, helloClass);
320 
321         out = CDSTestUtils.executeAndLog(pb, "neg");
322         out.shouldContain("Unrecognized value foo for AOTMode. Must be one of the following: off, record, create, auto, on");
323         out.shouldNotHaveExitValue(0);
324 
325         //----------------------------------------------------------------------
326         printTestCase("AOTCache specified with -XX:AOTMode=record");
327         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
328             "-XX:AOTMode=record",
329             "-XX:AOTConfiguration=" + aotConfigFile,
330             "-XX:AOTCache=" + aotCacheFile,
331             "-cp", appJar, helloClass);
332 
333         out = CDSTestUtils.executeAndLog(pb, "neg");
334         out.shouldContain("AOTCache must not be specified when using -XX:AOTMode=record");
335         out.shouldNotHaveExitValue(0);
336 
337         //----------------------------------------------------------------------
338         printTestCase("AOTCache/AOTCacheOutput not specified with -XX:AOTMode=create");
339         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
340             "-XX:AOTMode=create",
341             "-XX:AOTConfiguration=" + aotConfigFile,
342             "-cp", appJar, helloClass);
343 
344         out = CDSTestUtils.executeAndLog(pb, "neg");
345         out.shouldContain("AOTCache or AOTCacheOutput must be specified when using -XX:AOTMode=create");
346         out.shouldNotHaveExitValue(0);
347 
348         //----------------------------------------------------------------------
349         printTestCase("AOTCache and AOTCacheOutput have different values");
350         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
351             "-XX:AOTMode=create",
352             "-XX:AOTConfiguration=" + aotConfigFile,
353             "-XX:AOTCache=aaa",
354             "-XX:AOTCacheOutput=aaa",
355             "-cp", appJar, helloClass);
356 
357         out = CDSTestUtils.executeAndLog(pb, "neg");
358         out.shouldContain("Only one of AOTCache or AOTCacheOutput can be specified");
359         out.shouldNotHaveExitValue(0);
360 
361         //----------------------------------------------------------------------
362         printTestCase("No such config file");
363         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
364             "-XX:AOTMode=create",
365             "-XX:AOTConfiguration=no-such-file",
366             "-XX:AOTCache=" + aotCacheFile,
367             "-cp", appJar, helloClass);
368 
369         out = CDSTestUtils.executeAndLog(pb, "neg");
370         out.shouldContain("Must be a valid AOT configuration generated by the current JVM: no-such-file");
371         out.shouldNotHaveExitValue(0);
372 
373         //----------------------------------------------------------------------
374         printTestCase("AOTConfiguration file cannot be used as a CDS archive");
375 
376         // first make sure we have a valid aotConfigFile
377         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
378             "-XX:AOTMode=record",
379             "-XX:AOTConfiguration=" + aotConfigFile,
380             "-cp", appJar, helloClass);
381 
382         out = CDSTestUtils.executeAndLog(pb, "train");
383         out.shouldHaveExitValue(0);
384 
385         // Cannot use this config file as a AOT cache
386         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
387             "-XX:AOTMode=on",
388             "-XX:AOTCache=" + aotConfigFile,
389             "-cp", appJar, helloClass);
390 
391         out = CDSTestUtils.executeAndLog(pb, "neg");
392         out.shouldContain("Not a valid AOT cache (hello.aotconfig)");
393         out.shouldNotHaveExitValue(0);
394 
395         // Cannot use this config file as a CDS archive
396         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
397             "-Xshare:on",
398             "-XX:SharedArchiveFile=" + aotConfigFile,
399             "-cp", appJar, helloClass);
400 
401         out = CDSTestUtils.executeAndLog(pb, "neg");
402         out.shouldContain("Not a valid shared archive file (hello.aotconfig)");
403         out.shouldNotHaveExitValue(0);
404 
405         //----------------------------------------------------------------------
406         printTestCase("Classpath mismatch when creating archive");
407 
408         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
409             "-XX:AOTMode=create",
410             "-XX:AOTConfiguration=" + aotConfigFile,
411             "-XX:AOTCache=" + aotCacheFile,
412             "-cp", "noSuchJar.jar");
413 
414         out = CDSTestUtils.executeAndLog(pb, "neg");
415         out.shouldContain("class path and/or module path are not compatible with the ones " +
416                           "specified when the AOTConfiguration file was recorded");
417         out.shouldContain("Unable to use create AOT cache");
418         out.shouldHaveExitValue(1);
419 
420         //----------------------------------------------------------------------
421         printTestCase("Cannot use multiple paths in AOTConfiguration");
422 
423         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
424             "-XX:AOTMode=record",
425             "-XX:AOTConfiguration=" + aotConfigFile + File.pathSeparator + "dummy",
426             "-cp", "noSuchJar.jar");
427 
428         out = CDSTestUtils.executeAndLog(pb, "neg");
429         out.shouldContain("Option AOTConfiguration must specify a single file name");
430         out.shouldHaveExitValue(1);
431 
432         //----------------------------------------------------------------------
433         printTestCase("Cannot use multiple paths in AOTCache");
434 
435         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
436             "-XX:AOTMode=create",
437             "-XX:AOTConfiguration=" + aotConfigFile,
438             "-XX:AOTCache=" + aotCacheFile + File.pathSeparator + "dummy",
439             "-cp", "noSuchJar.jar");
440 
441         out = CDSTestUtils.executeAndLog(pb, "neg");
442         out.shouldContain("Option AOTCache must specify a single file name");
443         out.shouldHaveExitValue(1);
444 
445         //----------------------------------------------------------------------
446         printTestCase("Cannot use a dynamic CDS archive for -XX:AOTCache");
447         String staticArchive = "static.jsa";
448         String dynamicArchive = "dynamic.jsa";
449 
450         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
451             "-Xshare:dump",
452             "-XX:SharedArchiveFile=" + staticArchive);
453         out = CDSTestUtils.executeAndLog(pb, "static");
454         out.shouldHaveExitValue(0);
455 
456         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
457             "-XX:SharedArchiveFile=" + staticArchive,
458             "-XX:ArchiveClassesAtExit=" + dynamicArchive,
459             "--version");
460         out = CDSTestUtils.executeAndLog(pb, "dynamic");
461         out.shouldHaveExitValue(0);
462 
463         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
464             "-Xlog:aot",
465             "-XX:AOTMode=on",
466             "-XX:AOTCache=" + dynamicArchive,
467             "--version");
468 
469         out = CDSTestUtils.executeAndLog(pb, "neg");
470         out.shouldContain("Unable to use AOT cache.");
471         out.shouldContain("Not a valid AOT cache (dynamic.jsa)");
472         out.shouldHaveExitValue(1);
473 
474         //----------------------------------------------------------------------
475         testEmptyValue("AOTCache");
476         testEmptyValue("AOTConfiguration");
477         testEmptyValue("AOTMode");
478     }
479 
480     static void testEmptyValue(String option) throws Exception {
481         printTestCase("Empty values for " + option);
482         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
483             "-XX:" + option + "=", "--version");
484         OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "neg");
485         out.shouldContain("Improperly specified VM option '" + option + "='");
486         out.shouldHaveExitValue(1);
487     }
488 
489     static int testNum = 0;
490     static void printTestCase(String s) {
491         System.out.println("vvvvvvv TEST CASE " + testNum + ": " + s + ": starts here vvvvvvv");
492         testNum++;
493     }
494 }