1 /* 2 * Copyright (c) 2023, 2024, 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 import java.io.FileWriter; 26 import java.nio.file.Path; 27 import java.nio.file.Paths; 28 import java.util.regex.Matcher; 29 import java.util.regex.Pattern; 30 31 import jdk.test.lib.cds.CDSOptions; 32 import jdk.test.lib.cds.CDSTestUtils; 33 import jdk.test.lib.helpers.ClassFileInstaller; 34 import jdk.test.lib.process.OutputAnalyzer; 35 36 37 public class IndyTestBase { 38 static Pattern testOnlyPattern = null; // matches testNumber 39 static Pattern testSkipPattern = null; // matches testNumber 40 static Pattern forceSkipPattern = null; // Force some tests to be disabled during development. 41 static int testNumber = 0; 42 static String appJar; 43 static OutputAnalyzer output = null; // after test() returns, this is the output of the final run 44 static OutputAnalyzer staticDumpOutput = null; 45 static String jtregTestCase = "testcase"; 46 47 // bits for runMode 48 static final int RUN_STATIC = 1; // Run with static archive only (no AOT) 49 static final int RUN_AOT = 2; // Run with AOT (which uses dynamic archive) 50 static final int RUN_BENCH = 4; // Run in benchmark mode (no logs) 51 52 static void setup(String appJar) throws Exception { 53 setup(null, appJar); 54 } 55 56 static void setup(String forceSkip, String appJar) throws Exception { 57 String testName = System.getProperty("test.name"); 58 if (testName != null) { 59 Path p = Paths.get(testName); 60 String file = p.getFileName().toString(); 61 int i = file.indexOf("."); 62 jtregTestCase = file.substring(0, i); 63 } 64 IndyTestBase.appJar = appJar; 65 String testOnly = System.getProperty(jtregTestCase + ".test.only"); 66 if (testOnly != null) { 67 testOnlyPattern = Pattern.compile("^(" + testOnly + ")$"); 68 } 69 String testSkip = System.getProperty(jtregTestCase + ".test.skip"); 70 if (testSkip != null) { 71 testSkipPattern = Pattern.compile("^(" + testSkip + ")$"); 72 } 73 74 if (forceSkip != null) { 75 forceSkipPattern = Pattern.compile("^(" + forceSkip + ")$"); 76 } 77 } 78 79 static boolean shouldTest(String s) { 80 if (testOnlyPattern != null) { 81 Matcher matcher = testOnlyPattern.matcher(s); 82 if (!matcher.find()) { 83 return false; 84 } 85 } 86 if (testSkipPattern != null) { 87 Matcher matcher = testSkipPattern.matcher(s); 88 if (matcher.find()) { 89 return false; 90 } 91 } 92 93 return true; 94 } 95 96 // Run the test program with the same arg for both training run and production run 97 static void test(String testNote, String mainClass, String arg) throws Exception { 98 test(testNote, mainClass, arg, arg, RUN_STATIC); 99 } 100 101 static void test(String testNote, String mainClass, String trainingArg, String productionArg) throws Exception { 102 test(testNote, mainClass, trainingArg, productionArg, RUN_STATIC); 103 } 104 105 static void test(String testNote, String mainClass, String trainingArg, String productionArg, int runMode) throws Exception { 106 output = null; 107 testNumber ++; 108 String skipBy = null; 109 110 if (forceSkipPattern != null) { 111 Matcher matcher = forceSkipPattern.matcher(testNote); 112 if (matcher.find()) { 113 skipBy = " ***** (hard coded) Skipped by test note"; 114 } 115 } 116 if (skipBy == null && !shouldTest("" + testNumber)) { 117 skipBy = " ***** Skipped by test number"; 118 } 119 120 if (skipBy != null) { 121 System.out.println(" Test : #" + testNumber + ", " + testNote + skipBy); 122 return; 123 } 124 125 System.out.println("=================================================================="); 126 System.out.println(" Test : #" + testNumber + ", " + testNote); 127 System.out.println(" trainingArg : " + trainingArg); 128 System.out.println("productionArg : " + productionArg); 129 System.out.println(" runMode : " + runModeString(runMode)); 130 System.out.println("vvvv==========================================================vvvv"); 131 String s = jtregTestCase + "-" + testNumber; 132 String classList = s + ".classlist"; 133 String archiveName = s + ".jsa"; 134 135 // Create classlist 136 System.out.println("#" + testNumber + ": Create classlist"); 137 CDSTestUtils.dumpClassList(classList, "-cp", appJar, mainClass, trainingArg); 138 139 // Dump static archive 140 System.out.println("#" + testNumber + ": Dump static archive"); 141 CDSOptions opts = (new CDSOptions()) 142 .addPrefix("-XX:SharedClassListFile=" + classList, 143 "-XX:+AOTClassLinking", // by default enables AOTInvokeDynamicLinking 144 "-XX:+ArchiveDynamicProxies", 145 "-XX:+ArchiveReflectionData", 146 "-cp", appJar, 147 "-Xlog:cds+heap", 148 "-Xlog:cds+resolve=trace", 149 "-Xlog:cds,cds+class=debug") 150 .setArchiveName(archiveName); 151 staticDumpOutput = CDSTestUtils.createArchiveAndCheck(opts); 152 TestCommon.checkExecReturn(staticDumpOutput, 0, true); 153 154 if ((runMode & RUN_STATIC) != 0) { 155 // Run with static archive 156 System.out.println("#" + testNumber + ": Run with static archive (no AOT)"); 157 CDSOptions runOpts = (new CDSOptions()) 158 .addPrefix("-cp", appJar) 159 .setArchiveName(archiveName) 160 .setUseVersion(false) 161 .addSuffix(mainClass) 162 .addSuffix(productionArg); 163 if ((runMode & RUN_BENCH) != 0) { 164 runOpts.setBenchmarkMode(true); 165 } else { 166 runOpts.addPrefix("-Xlog:class+load", "-Xlog:cds=debug", "-Xlog:cds+heap=debug", 167 "-Xlog:methodhandles"); 168 } 169 output = CDSTestUtils.runWithArchive(runOpts); 170 TestCommon.checkExecReturn(output, 0, true); 171 } 172 173 if ((runMode & RUN_AOT) != 0) { 174 System.out.println("#" + testNumber + 175 ": (STEP 3 of 5) Run with static archive and dump profile in dynamic archive (With Training Data Replay)"); 176 String dynamicArchiveName = s + "-dyn.jsa"; 177 CDSOptions dynDumpOpts = (new CDSOptions()) 178 .addPrefix("-cp", appJar) 179 .setArchiveName(archiveName) 180 .setUseVersion(false) 181 .addSuffix("-XX:ArchiveClassesAtExit=" + dynamicArchiveName) 182 .addSuffix("-XX:-BackgroundCompilation") 183 .addSuffix("-XX:+RecordTraining"); 184 if ((runMode & RUN_BENCH) != 0) { 185 dynDumpOpts.setBenchmarkMode(true); 186 dynDumpOpts.addPrefix("-Xlog:cds=debug", "-Xlog:scc"); 187 } else { 188 dynDumpOpts.addPrefix("-Xlog:class+load", "-Xlog:cds=debug", 189 "-Xlog:cds+class=debug"); 190 } 191 // The main class name and arguments 192 dynDumpOpts 193 .addSuffix(mainClass) 194 .addSuffix(productionArg); 195 output = CDSTestUtils.runWithArchive(dynDumpOpts); 196 TestCommon.checkExecReturn(output, 0, true); 197 198 //====================================================================== 199 System.out.println("#" + testNumber + 200 ": (STEP 4 of 5) Run with dynamic archive and generate AOT code"); 201 String sharedCodeArchive = s + ".scc"; 202 CDSOptions aotOpts = (new CDSOptions()) 203 .addPrefix("-cp", appJar) 204 .setArchiveName(archiveName) 205 .setUseVersion(false) 206 .addSuffix("-XX:-BackgroundCompilation") 207 .addSuffix("-XX:+StoreCachedCode") 208 .addSuffix("-XX:CachedCodeFile=" + sharedCodeArchive) 209 .addSuffix("-XX:CachedCodeMaxSize=100M"); 210 211 if ((runMode & RUN_BENCH) != 0) { 212 aotOpts.setBenchmarkMode(true); 213 aotOpts.addPrefix("-Xlog:cds=debug", "-Xlog:scc"); 214 } else { 215 aotOpts.addPrefix("-Xlog:class+load", "-Xlog:cds=debug", "-Xlog:scc+init,scc+exit"); 216 if (mainClass.equals("ConcatA")) { 217 // Hard-code the printing of loopA for now 218 dynDumpOpts 219 .addSuffix("-XX:CompileCommand=print,*::loopA") 220 .addSuffix("-XX:+PrintAssembly"); 221 } 222 } 223 // The main class name and arguments 224 aotOpts 225 .addSuffix(mainClass) 226 .addSuffix(productionArg); 227 output = CDSTestUtils.runWithArchive(aotOpts); 228 TestCommon.checkExecReturn(output, 0, true); 229 230 //====================================================================== 231 System.out.println("#" + testNumber + ": (STEP 5 of 5) Run with dynamic archive and AOT cache"); 232 CDSOptions runOpts = (new CDSOptions()) 233 .addPrefix("-cp", appJar) 234 .setArchiveName(dynamicArchiveName) 235 .addSuffix("-XX:+LoadCachedCode") 236 .addSuffix("-XX:CachedCodeFile=" + sharedCodeArchive) 237 .setUseVersion(false) 238 .addSuffix(mainClass) 239 .addSuffix(productionArg); 240 241 if ((runMode & RUN_BENCH) != 0) { 242 runOpts.setBenchmarkMode(true); 243 } else { 244 // Tell CDSTestUtils to not add the -XX:VerifyArchivedFields=1 flag, which seems to be causing a crash 245 // in the AOT code. 246 runOpts.setBenchmarkMode(true); 247 248 runOpts.addPrefix("-Xlog:class+load", "-Xlog:cds=debug", "-Xlog:scc+init"); 249 if (mainClass.equals("ConcatA")) { 250 // Hard-code the printing of loopA for now 251 dynDumpOpts 252 .addSuffix("-XX:CompileCommand=print,*::loopA") 253 .addSuffix("-XX:+PrintAssembly"); 254 } 255 } 256 257 output = CDSTestUtils.runWithArchive(runOpts); 258 TestCommon.checkExecReturn(output, 0, true); 259 } 260 } 261 262 static void checkExec(String expectedOutput) throws Exception { 263 checkExec(expectedOutput, /* lambdaFormsMustBeArchived*/ true); 264 } 265 266 static void checkExec(String expectedOutput, boolean lambdaFormsMustBeArchived) throws Exception { 267 if (output == null) { // test may be skipped 268 return; 269 } 270 if (expectedOutput != null) { 271 TestCommon.checkExecReturn(output, 0, true, "OUTPUT = " + expectedOutput); 272 } 273 if (lambdaFormsMustBeArchived) { 274 output.shouldMatch("LambdaForm[$]((MH)|(DMH))/0x[0-9]+ source: shared objects file"); 275 output.shouldNotMatch("LambdaForm[$]MH/0x[0-9]+ source: __JVM_LookupDefineClass__"); 276 output.shouldNotMatch("LambdaForm[$]DMH/0x[0-9]+ source: __JVM_LookupDefineClass__"); 277 } 278 } 279 280 static void shouldMatch(String pattern) throws Exception { 281 if (output != null) { // test may be skipped 282 output.shouldMatch(pattern); 283 } 284 } 285 286 static String runModeString(int runMode) { 287 String prefix = ""; 288 String s = ""; 289 if ((runMode & RUN_STATIC) != 0) { 290 s += "RUN_STATIC"; 291 prefix = " | "; 292 } 293 if ((runMode & RUN_AOT) != 0) { 294 s += prefix; 295 s += "RUN_AOT"; 296 prefix = " | "; 297 } 298 return s; 299 } 300 }