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:+PreloadSharedClasses", 144 "-XX:+ArchiveInvokeDynamic", 145 "-XX:+ArchiveDynamicProxies", 146 "-XX:+ArchiveReflectionData", 147 "-cp", appJar, 148 "-Xlog:cds+heap", 149 "-Xlog:cds+resolve=trace", 150 "-Xlog:cds,cds+class=debug") 151 .setArchiveName(archiveName); 152 staticDumpOutput = CDSTestUtils.createArchiveAndCheck(opts); 153 TestCommon.checkExecReturn(staticDumpOutput, 0, true); 154 155 if ((runMode & RUN_STATIC) != 0) { 156 // Run with static archive 157 System.out.println("#" + testNumber + ": Run with static archive (no AOT)"); 158 CDSOptions runOpts = (new CDSOptions()) 159 .addPrefix("-cp", appJar) 160 .setArchiveName(archiveName) 161 .setUseVersion(false) 162 .addSuffix(mainClass) 163 .addSuffix(productionArg); 164 if ((runMode & RUN_BENCH) != 0) { 165 runOpts.setBenchmarkMode(true); 166 } else { 167 runOpts.addPrefix("-Xlog:class+load", "-Xlog:cds=debug", "-Xlog:cds+heap=debug", 168 "-Xlog:methodhandles"); 169 } 170 output = CDSTestUtils.runWithArchive(runOpts); 171 TestCommon.checkExecReturn(output, 0, true); 172 } 173 174 if ((runMode & RUN_AOT) != 0) { 175 System.out.println("#" + testNumber + 176 ": (STEP 3 of 5) Run with static archive and dump profile in dynamic archive (With Training Data Replay)"); 177 String dynamicArchiveName = s + "-dyn.jsa"; 178 CDSOptions dynDumpOpts = (new CDSOptions()) 179 .addPrefix("-cp", appJar) 180 .setArchiveName(archiveName) 181 .setUseVersion(false) 182 .addSuffix("-XX:ArchiveClassesAtExit=" + dynamicArchiveName) 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:+StoreCachedCode") 207 .addSuffix("-XX:CachedCodeFile=" + sharedCodeArchive) 208 .addSuffix("-XX:CachedCodeMaxSize=100M"); 209 210 if ((runMode & RUN_BENCH) != 0) { 211 aotOpts.setBenchmarkMode(true); 212 aotOpts.addPrefix("-Xlog:cds=debug", "-Xlog:scc"); 213 } else { 214 // Tell CDSTestUtils to not add the -XX:VerifyArchivedFields=1 flag, which seems to be causing a crash 215 // in the AOT code. 216 aotOpts.setBenchmarkMode(true); 217 218 aotOpts.addPrefix("-Xlog:class+load", "-Xlog:cds=debug", "-Xlog:scc"); 219 if (mainClass.equals("ConcatA")) { 220 // Hard-code the printing of loopA for now 221 dynDumpOpts 222 .addSuffix("-XX:CompileCommand=print,*::loopA") 223 .addSuffix("-XX:+PrintAssembly"); 224 } 225 } 226 // The main class name and arguments 227 aotOpts 228 .addSuffix(mainClass) 229 .addSuffix(productionArg); 230 output = CDSTestUtils.runWithArchive(aotOpts); 231 TestCommon.checkExecReturn(output, 0, true); 232 233 //====================================================================== 234 System.out.println("#" + testNumber + ": (STEP 5 of 5) Run with dynamic archive and AOT cache"); 235 CDSOptions runOpts = (new CDSOptions()) 236 .addPrefix("-cp", appJar) 237 .setArchiveName(dynamicArchiveName) 238 .addSuffix("-XX:+LoadCachedCode") 239 .addSuffix("-XX:CachedCodeFile=" + sharedCodeArchive) 240 .setUseVersion(false) 241 .addSuffix(mainClass) 242 .addSuffix(productionArg); 243 244 if ((runMode & RUN_BENCH) != 0) { 245 runOpts.setBenchmarkMode(true); 246 } else { 247 // Tell CDSTestUtils to not add the -XX:VerifyArchivedFields=1 flag, which seems to be causing a crash 248 // in the AOT code. 249 runOpts.setBenchmarkMode(true); 250 251 runOpts.addPrefix("-Xlog:class+load", "-Xlog:cds=debug"); 252 if (mainClass.equals("ConcatA")) { 253 // Hard-code the printing of loopA for now 254 dynDumpOpts 255 .addSuffix("-XX:CompileCommand=print,*::loopA") 256 .addSuffix("-XX:+PrintAssembly"); 257 } 258 } 259 260 output = CDSTestUtils.runWithArchive(runOpts); 261 TestCommon.checkExecReturn(output, 0, true); 262 } 263 } 264 265 static void checkExec(String expectedOutput) throws Exception { 266 checkExec(expectedOutput, /* lambdaFormsMustBeArchived*/ true); 267 } 268 269 static void checkExec(String expectedOutput, boolean lambdaFormsMustBeArchived) throws Exception { 270 if (output == null) { // test may be skipped 271 return; 272 } 273 if (expectedOutput != null) { 274 TestCommon.checkExecReturn(output, 0, true, "OUTPUT = " + expectedOutput); 275 } 276 if (lambdaFormsMustBeArchived) { 277 output.shouldMatch("LambdaForm[$]((MH)|(DMH))/0x[0-9]+ source: shared objects file"); 278 output.shouldNotMatch("LambdaForm[$]MH/0x[0-9]+ source: __JVM_LookupDefineClass__"); 279 output.shouldNotMatch("LambdaForm[$]DMH/0x[0-9]+ source: __JVM_LookupDefineClass__"); 280 } 281 } 282 283 static void shouldMatch(String pattern) throws Exception { 284 if (output != null) { // test may be skipped 285 output.shouldMatch(pattern); 286 } 287 } 288 289 static String runModeString(int runMode) { 290 String prefix = ""; 291 String s = ""; 292 if ((runMode & RUN_STATIC) != 0) { 293 s += "RUN_STATIC"; 294 prefix = " | "; 295 } 296 if ((runMode & RUN_AOT) != 0) { 297 s += prefix; 298 s += "RUN_AOT"; 299 prefix = " | "; 300 } 301 return s; 302 } 303 }