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 CDS should fail to load if production time GC flags do not match training run.
28 * @requires vm.flagless
29 * @requires vm.cds.write.archived.java.heap
30 * @library /test/jdk/lib/testlibrary /test/lib
31 * @build LeydenGCFlags
32 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
33 * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar HelloApp
34 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI LeydenGCFlags
35 */
36
37 import jdk.test.whitebox.WhiteBox;
38 import jdk.test.whitebox.gc.GC;
39
40 import jdk.test.lib.cds.CDSAppTester;
41 import jdk.test.lib.helpers.ClassFileInstaller;
42 import jdk.test.lib.process.OutputAnalyzer;
43 import jdk.test.lib.StringArrayUtils;
44
45 public class LeydenGCFlags {
46 static final String appJar = ClassFileInstaller.getJarPath("app.jar");
47 static final String mainClass = "HelloApp";
48 static final String ERROR_GC_SUPPORTED = "Cannot create the AOT configuration file: UseCompressedClassPointers must be enabled, and collector must be G1, Parallel, Serial, Epsilon, or Shenandoah";
49 static final String ERROR_GC_MISMATCH = "CDS archive has aot-linked classes. It cannot be used because GC used during dump time (.*) is not the same as runtime (.*)";
50 static final String ERROR_COOP_MISMATCH = "Disable Startup Code Cache: 'HelloApp.cds.code' was created with CompressedOops::shift.. = .* vs current .*";
51
52 static String trainingArgs[];
53 static String productionArgs[];
54 static boolean shouldFailDump;
55 static boolean shouldFailRun;
56 static String productFailPattern;
57
58 public static void main(String[] args) throws Exception {
59 if (GC.Z.isSupported()) {
60 // ZGC not supported for now
61 fail_dump("-XX:+UseZGC", "-Xmx8g", ERROR_GC_SUPPORTED);
62 }
63
64 // Serial, Parallel and Shenandoah collectors are allowed to be used,
65 // as long as the same one is used between training and production
66 if (GC.Serial.isSupported()) {
67 good("-XX:+UseSerialGC", "-XX:+UseSerialGC");
68 }
69 if (GC.Parallel.isSupported()) {
70 good("-XX:+UseParallelGC", "-XX:+UseParallelGC");
71 }
72 if (GC.Shenandoah.isSupported()) {
73 good("-XX:+UseShenandoahGC", "-XX:+UseShenandoahGC");
74 }
75
76 // Fail if production uses a different collector than training
77 if (GC.Parallel.isSupported() && GC.G1.isSupported()) {
78 fail_run("-XX:+UseParallelGC", "-XX:+UseG1GC", ERROR_GC_MISMATCH );
79 }
80 if (GC.Parallel.isSupported()) {
81 fail_run(null, "-XX:+UseParallelGC", ERROR_GC_MISMATCH );
82 }
83
84 if (false) { // Disabled for now, as on MacOS we cannot guarantee to get differnt coop encodings
85 // Different oop encodings
86 fail_run(array("-XX:-UseCompatibleCompressedOops", "-Xmx128m"),
87 array("-XX:-UseCompatibleCompressedOops","-Xmx8g"),
88 ERROR_COOP_MISMATCH);
89 fail_run(array("-XX:-UseCompatibleCompressedOops", "-Xmx8g"),
90 array("-XX:-UseCompatibleCompressedOops","-Xmx128m"),
91 ERROR_COOP_MISMATCH);
92 }
93
94 // FIXME -- this causes
95 // java.lang.invoke.WrongMethodTypeException: handle's method type ()Object but found ()Object
96 /* fail_run(null, "NoSuchApp"); */
97 }
98
99 static String[] array(String... strings) {
100 return strings;
101 }
102 static void good(Object t, Object p) throws Exception {
103 shouldFailDump = false;
104 shouldFailRun = false;
105 run(t, p);
106 }
107
108 static void fail_dump(Object t, Object p, String regexp) throws Exception {
109 shouldFailDump = true;
110 shouldFailRun = true;
111 productFailPattern = regexp;
112 trainingArgs = makeArgs(t);
113 productionArgs = makeArgs(p);
114 Tester tester = new Tester();
115 tester.setCheckExitValue(false);
116 tester.run(new String[] {"AOT"} );
117 }
118
119 static void fail_run(Object t, Object p, String regexp) throws Exception {
120 shouldFailDump = false;
121 shouldFailRun = true;
122 productFailPattern = regexp;
123 run(t, p);
124 }
125
126 static void run(Object t, Object p) throws Exception {
127 trainingArgs = makeArgs(t);
128 productionArgs = makeArgs(p);
129 Tester tester = new Tester();
130 tester.run(new String[] {"AOT"} );
131 }
132
133 static String[] makeArgs(Object o) {
134 if (o == null) {
135 return new String[0];
136 }
137 if (o instanceof String[]) {
138 return (String[])o;
139 } else {
140 return new String[] { (String)o };
141 }
142 }
143
144 static class Tester extends CDSAppTester {
145 public Tester() {
146 super(mainClass);
147 }
148
149 @Override
150 public String classpath(RunMode runMode) {
151 return appJar;
152 }
153
154 @Override
155 public String[] appCommandLine(RunMode runMode) {
156 if (runMode.isProductionRun()) {
157 return StringArrayUtils.concat(productionArgs, "-XX:AOTMode=auto", mainClass);
158 } else {
159 return StringArrayUtils.concat(trainingArgs, "-Xlog:aot", mainClass);
160 }
161 }
162
163 @Override
164 public void checkExecution(OutputAnalyzer out, RunMode runMode) {
165 if (shouldFailDump) {
166 if (runMode != RunMode.PRODUCTION) {
167 out.shouldMatch(productFailPattern);
168 out.shouldHaveExitValue(1);
169 }
170 } else if (shouldFailRun) {
171 if (runMode == RunMode.PRODUCTION) {
172 out.shouldMatch(productFailPattern);
173 }
174 } else {
175 out.shouldContain("Hello Leyden");
176
177 if (isDumping(runMode)) {
178 // We should dump the heap even if the collector is not G1
179 out.shouldContain("Shared file region (hp)");
180 }
181
182 }
183 }
184 }
185 }
186
187 class HelloApp {
188 public static void main(String args[]) {
189 System.out.println("Hello Leyden");
190 }
191 }