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         //----------------------------------------------------------------------
 55         printTestCase("Training Run");
 56         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
 57             "-XX:AOTMode=record",
 58             "-XX:AOTConfiguration=" + aotConfigFile,
 59             "-Xlog:cds=debug",
 60             "-cp", appJar, helloClass);
 61 
 62         OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "train");
 63         out.shouldContain("Hello World");
 64         out.shouldContain("AOTConfiguration recorded: " + aotConfigFile);
 65         out.shouldHaveExitValue(0);
 66 
 67         //----------------------------------------------------------------------
 68         printTestCase("Assembly Phase (AOTClassLinking unspecified -> should be enabled by default)");
 69         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
 70             "-XX:AOTMode=create",
 71             "-XX:AOTConfiguration=" + aotConfigFile,
 72             "-XX:AOTCache=" + aotCacheFile,
 73             "-Xlog:cds",
 74             "-cp", appJar);
 75         out = CDSTestUtils.executeAndLog(pb, "asm");
 76         out.shouldContain("Dumping shared data to file:");
 77         out.shouldMatch("cds.*hello[.]aot");
 78         out.shouldHaveExitValue(0);
 79 
 80         //----------------------------------------------------------------------
 81         printTestCase("Production Run with AOTCache");
 82         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
 83             "-XX:AOTCache=" + aotCacheFile,
 84             "-Xlog:cds",
 85             "-cp", appJar, helloClass);
 86         out = CDSTestUtils.executeAndLog(pb, "prod");
 87         out.shouldContain("Using AOT-linked classes: true (static archive: has aot-linked classes)");
 88         out.shouldContain("Opened AOT cache hello.aot.");
 89         out.shouldContain("Hello World");
 90         out.shouldHaveExitValue(0);
 91 
 92         //----------------------------------------------------------------------
 93         printTestCase("AOTMode=off");
 94         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
 95             "-XX:AOTCache=" + aotCacheFile,
 96             "--show-version",
 97             "-Xlog:cds",
 98             "-XX:AOTMode=off",
 99             "-cp", appJar, helloClass);
100         out = CDSTestUtils.executeAndLog(pb, "prod");
101         out.shouldNotContain(", sharing");
102         out.shouldNotContain("Opened AOT cache hello.aot.");
103         out.shouldContain("Hello World");
104         out.shouldHaveExitValue(0);
105 
106         //----------------------------------------------------------------------
107         printTestCase("AOTMode=auto");
108         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
109             "-XX:AOTCache=" + aotCacheFile,
110             "--show-version",
111             "-Xlog:cds",
112             "-XX:AOTMode=auto",
113             "-cp", appJar, helloClass);
114         out = CDSTestUtils.executeAndLog(pb, "prod");
115         out.shouldContain(", sharing");
116         out.shouldContain("Opened AOT cache hello.aot.");
117         out.shouldContain("Hello World");
118         out.shouldHaveExitValue(0);
119 
120         //----------------------------------------------------------------------
121         printTestCase("AOTMode=on");
122         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
123             "-XX:AOTCache=" + aotCacheFile,
124             "--show-version",
125             "-Xlog:cds",
126             "-XX:AOTMode=on",
127             "-cp", appJar, helloClass);
128         out = CDSTestUtils.executeAndLog(pb, "prod");
129         out.shouldContain(", sharing");
130         out.shouldContain("Opened AOT cache hello.aot.");
131         out.shouldContain("Hello World");
132         out.shouldHaveExitValue(0);
133 
134         //----------------------------------------------------------------------
135         printTestCase("Assembly Phase with -XX:-AOTClassLinking");
136         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
137             "-XX:AOTMode=create",
138             "-XX:-AOTClassLinking",
139             "-XX:AOTConfiguration=" + aotConfigFile,
140             "-XX:AOTCache=" + aotCacheFile,
141             "-Xlog:cds",
142             "-cp", appJar);
143         out = CDSTestUtils.executeAndLog(pb, "asm");
144         out.shouldContain("Dumping shared data to file:");
145         out.shouldMatch("cds.*hello[.]aot");
146         out.shouldHaveExitValue(0);
147 
148         //----------------------------------------------------------------------
149         printTestCase("Production Run with AOTCache, which was created with -XX:-AOTClassLinking");
150         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
151             "-XX:AOTCache=" + aotCacheFile,
152             "-Xlog:cds",
153             "-cp", appJar, helloClass);
154         out = CDSTestUtils.executeAndLog(pb, "prod");
155         out.shouldContain("Using AOT-linked classes: false (static archive: no aot-linked classes)");
156         out.shouldContain("Opened AOT cache hello.aot.");
157         out.shouldContain("Hello World");
158         out.shouldHaveExitValue(0);
159     }
160 
161     static void negativeTests() throws Exception {
162        //----------------------------------------------------------------------
163         printTestCase("Mixing old and new options");
164         String mixOldNewErrSuffix = " cannot be used at the same time with -Xshare:on, -Xshare:auto, "
165             + "-Xshare:off, -Xshare:dump, DumpLoadedClassList, SharedClassListFile, "
166             + "or SharedArchiveFile";
167 
168         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
169             "-Xshare:off",
170             "-XX:AOTConfiguration=" + aotConfigFile,
171             "-cp", appJar, helloClass);
172 
173         OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "neg");
174         out.shouldContain("Option AOTConfiguration" + mixOldNewErrSuffix);
175         out.shouldNotHaveExitValue(0);
176 
177         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
178             "-XX:SharedArchiveFile=" + aotCacheFile,
179             "-XX:AOTCache=" + aotCacheFile,
180             "-cp", appJar, helloClass);
181         out = CDSTestUtils.executeAndLog(pb, "neg");
182         out.shouldContain("Option AOTCache" + mixOldNewErrSuffix);
183         out.shouldNotHaveExitValue(0);
184 
185         //----------------------------------------------------------------------
186         printTestCase("Use AOTConfiguration without AOTMode");
187         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
188             "-XX:AOTConfiguration=" + aotConfigFile,
189             "-cp", appJar, helloClass);
190 
191         out = CDSTestUtils.executeAndLog(pb, "neg");
192         out.shouldContain("AOTConfiguration can only be used with -XX:AOTMode=record or -XX:AOTMode=create");
193         out.shouldNotHaveExitValue(0);
194 
195         //----------------------------------------------------------------------
196         printTestCase("Use AOTMode without AOTConfiguration");
197         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
198             "-XX:AOTMode=record",
199             "-cp", appJar, helloClass);
200 
201         out = CDSTestUtils.executeAndLog(pb, "neg");
202         out.shouldContain("-XX:AOTMode=record cannot be used without setting AOTConfiguration");
203 
204         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
205             "-XX:AOTMode=create",
206             "-cp", appJar, helloClass);
207 
208         out = CDSTestUtils.executeAndLog(pb, "neg");
209         out.shouldContain("-XX:AOTMode=create cannot be used without setting AOTConfiguration");
210         out.shouldNotHaveExitValue(0);
211 
212         //----------------------------------------------------------------------
213         printTestCase("Bad AOTMode");
214         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
215             "-XX:AOTMode=foo",
216             "-cp", appJar, helloClass);
217 
218         out = CDSTestUtils.executeAndLog(pb, "neg");
219         out.shouldContain("Unrecognized value foo for AOTMode. Must be one of the following: off, record, create, auto, on");
220         out.shouldNotHaveExitValue(0);
221 
222         //----------------------------------------------------------------------
223         printTestCase("AOTCache specified with -XX:AOTMode=record");
224         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
225             "-XX:AOTMode=record",
226             "-XX:AOTConfiguration=" + aotConfigFile,
227             "-XX:AOTCache=" + aotCacheFile,
228             "-cp", appJar, helloClass);
229 
230         out = CDSTestUtils.executeAndLog(pb, "neg");
231         out.shouldContain("AOTCache must not be specified when using -XX:AOTMode=record");
232         out.shouldNotHaveExitValue(0);
233 
234         //----------------------------------------------------------------------
235         printTestCase("AOTCache not specified with -XX:AOTMode=create");
236         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
237             "-XX:AOTMode=create",
238             "-XX:AOTConfiguration=" + aotConfigFile,
239             "-cp", appJar, helloClass);
240 
241         out = CDSTestUtils.executeAndLog(pb, "neg");
242         out.shouldContain("AOTCache must be specified when using -XX:AOTMode=create");
243         out.shouldNotHaveExitValue(0);
244 
245         //----------------------------------------------------------------------
246         printTestCase("No such config file");
247         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
248             "-XX:AOTMode=create",
249             "-XX:AOTConfiguration=no-such-file",
250             "-XX:AOTCache=" + aotCacheFile,
251             "-cp", appJar, helloClass);
252 
253         out = CDSTestUtils.executeAndLog(pb, "neg");
254         out.shouldContain("Must be a valid AOT configuration generated by the current JVM: no-such-file");
255         out.shouldNotHaveExitValue(0);
256 
257         //----------------------------------------------------------------------
258         printTestCase("AOTConfiguration file cannot be used as a CDS archive");
259 
260         // first make sure we have a valid aotConfigFile
261         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
262             "-XX:AOTMode=record",
263             "-XX:AOTConfiguration=" + aotConfigFile,
264             "-cp", appJar, helloClass);
265 
266         out = CDSTestUtils.executeAndLog(pb, "train");
267         out.shouldHaveExitValue(0);
268 
269         // Cannot use this config file as a AOT cache
270         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
271             "-XX:AOTMode=on",
272             "-XX:AOTCache=" + aotConfigFile,
273             "-cp", appJar, helloClass);
274 
275         out = CDSTestUtils.executeAndLog(pb, "neg");
276         out.shouldContain("Not a valid AOT cache (hello.aotconfig)");
277         out.shouldNotHaveExitValue(0);
278 
279         // Cannot use this config file as a CDS archive
280         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
281             "-Xshare:on",
282             "-XX:SharedArchiveFile=" + aotConfigFile,
283             "-cp", appJar, helloClass);
284 
285         out = CDSTestUtils.executeAndLog(pb, "neg");
286         out.shouldContain("Not a valid archive (hello.aotconfig)");
287         out.shouldNotHaveExitValue(0);
288 
289         //----------------------------------------------------------------------
290         printTestCase("Classpath mismatch when creating archive");
291 
292         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
293             "-XX:AOTMode=create",
294             "-XX:AOTConfiguration=" + aotConfigFile,
295             "-XX:AOTCache=" + aotCacheFile,
296             "-cp", "noSuchJar.jar");
297 
298         out = CDSTestUtils.executeAndLog(pb, "neg");
299         out.shouldContain("class path and/or module path are not compatible with the ones " +
300                           "specified when the AOTConfiguration file was recorded");
301         out.shouldContain("Unable to use create AOT cache");
302         out.shouldHaveExitValue(1);
303 
304         //----------------------------------------------------------------------
305         printTestCase("Cannot use multiple paths in AOTConfiguration");
306 
307         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
308             "-XX:AOTMode=record",
309             "-XX:AOTConfiguration=" + aotConfigFile + File.pathSeparator + "dummy",
310             "-cp", "noSuchJar.jar");
311 
312         out = CDSTestUtils.executeAndLog(pb, "neg");
313         out.shouldContain("Option AOTConfiguration must specify a single file name");
314         out.shouldHaveExitValue(1);
315 
316         //----------------------------------------------------------------------
317         printTestCase("Cannot use multiple paths in AOTCache");
318 
319         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
320             "-XX:AOTMode=create",
321             "-XX:AOTConfiguration=" + aotConfigFile,
322             "-XX:AOTCache=" + aotCacheFile + File.pathSeparator + "dummy",
323             "-cp", "noSuchJar.jar");
324 
325         out = CDSTestUtils.executeAndLog(pb, "neg");
326         out.shouldContain("Option AOTCache must specify a single file name");
327         out.shouldHaveExitValue(1);
328 
329         //----------------------------------------------------------------------
330         printTestCase("Cannot use a dynamic CDS archive for -XX:AOTCache");
331         String staticArchive = "static.jsa";
332         String dynamicArchive = "dynamic.jsa";
333 
334         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
335             "-Xshare:dump",
336             "-XX:SharedArchiveFile=" + staticArchive);
337         out = CDSTestUtils.executeAndLog(pb, "static");
338         out.shouldHaveExitValue(0);
339 
340         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
341             "-XX:SharedArchiveFile=" + staticArchive,
342             "-XX:ArchiveClassesAtExit=" + dynamicArchive,
343             "--version");
344         out = CDSTestUtils.executeAndLog(pb, "dynamic");
345         out.shouldHaveExitValue(0);
346 
347         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
348             "-Xlog:cds",
349             "-XX:AOTMode=on",
350             "-XX:AOTCache=" + dynamicArchive,
351             "--version");
352 
353         out = CDSTestUtils.executeAndLog(pb, "neg");
354         out.shouldContain("Unable to use AOT cache.");
355         out.shouldContain("Not a valid AOT cache (dynamic.jsa)");
356         out.shouldHaveExitValue(1);
357     }
358 
359     static int testNum = 0;
360     static void printTestCase(String s) {
361         System.out.println("vvvvvvv TEST CASE " + testNum + ": " + s + " starts here vvvvvvv");
362         testNum++;
363     }
364 }