1 /*
2 * Copyright (c) 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 Sanity test of AOT Code Cache with compressed oops configurations
28 * @requires vm.cds.supports.aot.code.caching
29 * @requires vm.compMode != "Xcomp"
30 * @requires vm.bits == 64
31 * @requires vm.opt.final.UseCompressedOops
32 * @comment The test verifies AOT checks during VM startup and not code generation.
33 * No need to run it with -Xcomp. It takes a lot of time to complete all
34 * subtests with this flag.
35 * @library /test/lib /test/setup_aot
36 * @build AOTCodeCompressedOopsTest JavacBenchApp
37 * @run driver/timeout=480 jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
38 * JavacBenchApp
39 * JavacBenchApp$ClassFile
40 * JavacBenchApp$FileManager
41 * JavacBenchApp$SourceFile
42 * @run driver/timeout=480 AOTCodeCompressedOopsTest
43 */
44
45 import java.util.ArrayList;
46 import java.util.List;
47 import java.util.regex.Matcher;
48 import java.util.regex.Pattern;
49
50 import jdk.test.lib.cds.CDSAppTester;
51 import jdk.test.lib.process.OutputAnalyzer;
52
53 public class AOTCodeCompressedOopsTest {
54 public static void main(String... args) throws Exception {
55 {
56 Tester t = new Tester();
57 t.setHeapConfig(Tester.RunMode.ASSEMBLY, true, true);
58 t.runAOTAssemblyWorkflow();
59 t.setHeapConfig(Tester.RunMode.PRODUCTION, true, true);
60 t.productionRun();
61 t.setHeapConfig(Tester.RunMode.PRODUCTION, true, false);
62 t.productionRun();
63 t.setHeapConfig(Tester.RunMode.PRODUCTION, false, false);
64 t.productionRun();
65 }
66 {
67 Tester t = new Tester();
68 t.setHeapConfig(Tester.RunMode.ASSEMBLY, true, false);
69 t.runAOTAssemblyWorkflow();
70 t.setHeapConfig(Tester.RunMode.PRODUCTION, true, true);
71 t.productionRun();
72 t.setHeapConfig(Tester.RunMode.PRODUCTION, true, false);
73 t.productionRun();
74 t.setHeapConfig(Tester.RunMode.PRODUCTION, false, false);
75 t.productionRun();
76 }
77 {
78 Tester t = new Tester();
79 t.setHeapConfig(Tester.RunMode.ASSEMBLY, false, false);
80 t.runAOTAssemblyWorkflow();
81 t.setHeapConfig(Tester.RunMode.PRODUCTION, true, true);
82 t.productionRun();
83 t.setHeapConfig(Tester.RunMode.PRODUCTION, true, false);
84 t.productionRun();
85 t.setHeapConfig(Tester.RunMode.PRODUCTION, false, false);
86 t.productionRun();
87 }
88 }
89 static class Tester extends CDSAppTester {
90 boolean zeroBaseInAsmPhase, zeroBaseInProdPhase;
91 boolean zeroShiftInAsmPhase, zeroShiftInProdPhase;
92
93 public Tester() {
94 super("AOTCodeCompressedOopsTest");
95 }
96
97 public void setHeapConfig(RunMode runMode, boolean isBaseZero, boolean isShiftZero) {
98 if (runMode == RunMode.ASSEMBLY) {
99 zeroBaseInAsmPhase = isBaseZero;
100 zeroShiftInAsmPhase = isShiftZero;
101 } else if (runMode == RunMode.PRODUCTION) {
102 zeroBaseInProdPhase = isBaseZero;
103 zeroShiftInProdPhase = isShiftZero;
104 }
105 }
106
107 @Override
108 public String classpath(RunMode runMode) {
109 return "app.jar";
110 }
111
112 List<String> getVMArgsForHeapConfig(boolean isBaseZero, boolean isShiftZero) {
113 List<String> list = new ArrayList<String>();
114 // Note the VM options used are best-effort to get the desired base and shift,
115 // but it is OS dependent, so we may not get the desired configuration.
116 if (isBaseZero && isShiftZero) {
117 list.add("-Xmx128m"); // Set max heap < 4G;
118 } else if (isBaseZero && !isShiftZero) {
119 list.add("-Xmx6g"); // Set max heap > 4G for shift to be non-zero
120 } else if (!isBaseZero && !isShiftZero) {
121 list.add("-Xmx6g");
122 list.add("-XX:HeapBaseMinAddress=32g");
123 }
124 return list;
125 }
126
127 @Override
128 public String[] vmArgs(RunMode runMode) {
129 switch (runMode) {
130 case RunMode.ASSEMBLY: {
131 List<String> args = getVMArgsForHeapConfig(zeroBaseInAsmPhase, zeroShiftInAsmPhase);
132 args.addAll(List.of("-XX:+UnlockDiagnosticVMOptions",
133 "-Xlog:aot=info",
134 "-Xlog:aot+codecache+init=debug",
135 "-Xlog:aot+codecache+exit=debug"));
136 return args.toArray(new String[0]);
137 }
138 case RunMode.PRODUCTION: {
139 List<String> args = getVMArgsForHeapConfig(zeroBaseInProdPhase, zeroShiftInProdPhase);
140 args.addAll(List.of("-XX:+UnlockDiagnosticVMOptions",
141 "-Xlog:aot=info", // we need this to parse CompressedOops settings
142 "-Xlog:aot+codecache+init=debug",
143 "-Xlog:aot+codecache+exit=debug"));
144 return args.toArray(new String[0]);
145 }
146 }
147 return new String[] {};
148 }
149
150 @Override
151 public String[] appCommandLine(RunMode runMode) {
152 return new String[] {
153 "JavacBenchApp", "10"
154 };
155 }
156
157 @Override
158 public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
159 if (runMode == RunMode.PRODUCTION) {
160 int aotCacheShift = -1, currentShift = -1;
161 long aotCacheBase = -1, currentBase = -1;
162 List<String> list = out.asLines();
163 /* We tried to have CompressedOops settings as per the test requirement,
164 * but it ultimately depends on OS and is not guaranteed that we have got the desired settings.
165 * So we parse the log output from the production run to get the real settings.
166 *
167 * Parse the following Xlog:cds output to get the values of CompressedOops::base and CompressedOops::shift
168 * used during the AOTCache assembly and production run:
169 *
170 * [0.022s][info][cds] CDS archive was created with max heap size = 1024M, and the following configuration:
171 * [0.022s][info][cds] narrow_klass_base at mapping start address, narrow_klass_pointer_bits = 32, narrow_klass_shift = 0
172 * [0.022s][info][cds] narrow_oop_mode = 1, narrow_oop_base = 0x0000000000000000, narrow_oop_shift = 3
173 * [0.022s][info][cds] The current max heap size = 31744M, G1HeapRegion::GrainBytes = 16777216
174 * [0.022s][info][cds] narrow_klass_base = 0x000007fc00000000, arrow_klass_pointer_bits = 32, narrow_klass_shift = 0
175 * [0.022s][info][cds] narrow_oop_mode = 3, narrow_oop_base = 0x0000000300000000, narrow_oop_shift = 3
176 * [0.022s][info][cds] heap range = [0x0000000301000000 - 0x0000000ac1000000]
177 */
178 Pattern p = Pattern.compile("narrow_oop_base = 0x([0-9a-fA-F]+), narrow_oop_shift = (\\d)");
179 for (int i = 0; i < list.size(); i++) {
180 String line = list.get(i);
181 if (line.indexOf("CDS archive was created with max heap size") != -1) {
182 // Parse AOT Cache CompressedOops settings
183 line = list.get(i+2);
184 Matcher m = p.matcher(line);
185 if (!m.find()) {
186 throw new RuntimeException("Pattern \"" + p + "\" not found in the output");
187 }
188 aotCacheBase = Long.valueOf(m.group(1), 16);
189 aotCacheShift = Integer.valueOf(m.group(2));
190 // Parse current CompressedOops settings
191 line = list.get(i+5);
192 m = p.matcher(line);
193 if (!m.find()) {
194 throw new RuntimeException("Pattern \"" + p + "\" not found in the output");
195 }
196 currentBase = Long.valueOf(m.group(1), 16);
197 currentShift = Integer.valueOf(m.group(2));
198 break;
199 }
200 }
201 if (aotCacheShift == -1 || currentShift == -1 || aotCacheBase == -1 || currentBase == -1) {
202 throw new RuntimeException("Failed to find CompressedOops settings");
203 }
204 if (aotCacheShift != currentShift) {
205 out.shouldContain("AOT Code Cache disabled: it was created with different CompressedOops::shift()");
206 } else if ((aotCacheBase == 0 || currentBase == 0) && (aotCacheBase != currentBase)) {
207 out.shouldContain("AOTStubCaching is disabled: incompatible CompressedOops::base()");
208 } else {
209 out.shouldMatch("Read \\d+ entries table at offset \\d+ from AOT Code Cache");
210 }
211 }
212 }
213 }
214 }