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