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:-BackgroundCompilation") 184 .addSuffix("-XX:+RecordTraining"); 185 if ((runMode & RUN_BENCH) != 0) { 186 dynDumpOpts.setBenchmarkMode(true); 187 dynDumpOpts.addPrefix("-Xlog:cds=debug", "-Xlog:scc"); 188 } else { 189 dynDumpOpts.addPrefix("-Xlog:class+load", "-Xlog:cds=debug", 190 "-Xlog:cds+class=debug"); 191 } 192 // The main class name and arguments 193 dynDumpOpts 194 .addSuffix(mainClass) 195 .addSuffix(productionArg); 196 output = CDSTestUtils.runWithArchive(dynDumpOpts); 197 TestCommon.checkExecReturn(output, 0, true); 198 199 //====================================================================== 200 System.out.println("#" + testNumber + 201 ": (STEP 4 of 5) Run with dynamic archive and generate AOT code"); 202 String sharedCodeArchive = s + ".scc"; 203 CDSOptions aotOpts = (new CDSOptions()) 204 .addPrefix("-cp", appJar) 205 .setArchiveName(archiveName) 206 .setUseVersion(false) 207 .addSuffix("-XX:-BackgroundCompilation") 208 .addSuffix("-XX:+StoreCachedCode") 209 .addSuffix("-XX:CachedCodeFile=" + sharedCodeArchive) 210 .addSuffix("-XX:CachedCodeMaxSize=100M"); 211 212 if ((runMode & RUN_BENCH) != 0) { 213 aotOpts.setBenchmarkMode(true); 214 aotOpts.addPrefix("-Xlog:cds=debug", "-Xlog:scc"); 215 } else { 216 // Tell CDSTestUtils to not add the -XX:VerifyArchivedFields=1 flag, which seems to be causing a crash 217 // in the AOT code. 218 aotOpts.setBenchmarkMode(true); 219 220 aotOpts.addPrefix("-Xlog:class+load", "-Xlog:cds=debug", "-Xlog:scc+init,scc+exit"); 221 if (mainClass.equals("ConcatA")) { 222 // Hard-code the printing of loopA for now 223 dynDumpOpts 224 .addSuffix("-XX:CompileCommand=print,*::loopA") 225 .addSuffix("-XX:+PrintAssembly"); 226 } 227 } 228 // The main class name and arguments 229 aotOpts 230 .addSuffix(mainClass) 231 .addSuffix(productionArg); 232 output = CDSTestUtils.runWithArchive(aotOpts); 233 TestCommon.checkExecReturn(output, 0, true); 234 235 //====================================================================== 236 System.out.println("#" + testNumber + ": (STEP 5 of 5) Run with dynamic archive and AOT cache"); 237 CDSOptions runOpts = (new CDSOptions()) 238 .addPrefix("-cp", appJar) 239 .setArchiveName(dynamicArchiveName) 240 .addSuffix("-XX:+LoadCachedCode") 241 .addSuffix("-XX:CachedCodeFile=" + sharedCodeArchive) 242 .setUseVersion(false) 243 .addSuffix(mainClass) 244 .addSuffix(productionArg); 245 246 if ((runMode & RUN_BENCH) != 0) { 247 runOpts.setBenchmarkMode(true); 248 } else { 249 // Tell CDSTestUtils to not add the -XX:VerifyArchivedFields=1 flag, which seems to be causing a crash 250 // in the AOT code. 251 runOpts.setBenchmarkMode(true); 252 253 runOpts.addPrefix("-Xlog:class+load", "-Xlog:cds=debug", "-Xlog:scc+init"); 254 if (mainClass.equals("ConcatA")) { 255 // Hard-code the printing of loopA for now 256 dynDumpOpts 257 .addSuffix("-XX:CompileCommand=print,*::loopA") 258 .addSuffix("-XX:+PrintAssembly"); 259 } 260 } 261 262 output = CDSTestUtils.runWithArchive(runOpts); 263 TestCommon.checkExecReturn(output, 0, true); 264 } 265 } 266 267 static void checkExec(String expectedOutput) throws Exception { 268 checkExec(expectedOutput, /* lambdaFormsMustBeArchived*/ true); 269 } 270 271 static void checkExec(String expectedOutput, boolean lambdaFormsMustBeArchived) throws Exception { 272 if (output == null) { // test may be skipped 273 return; 274 } 275 if (expectedOutput != null) { 276 TestCommon.checkExecReturn(output, 0, true, "OUTPUT = " + expectedOutput); 277 } 278 if (lambdaFormsMustBeArchived) { 279 output.shouldMatch("LambdaForm[$]((MH)|(DMH))/0x[0-9]+ source: shared objects file"); 280 output.shouldNotMatch("LambdaForm[$]MH/0x[0-9]+ source: __JVM_LookupDefineClass__"); 281 output.shouldNotMatch("LambdaForm[$]DMH/0x[0-9]+ source: __JVM_LookupDefineClass__"); 282 } 283 } 284 285 static void shouldMatch(String pattern) throws Exception { 286 if (output != null) { // test may be skipped 287 output.shouldMatch(pattern); 288 } 289 } 290 291 static String runModeString(int runMode) { 292 String prefix = ""; 293 String s = ""; 294 if ((runMode & RUN_STATIC) != 0) { 295 s += "RUN_STATIC"; 296 prefix = " | "; 297 } 298 if ((runMode & RUN_AOT) != 0) { 299 s += prefix; 300 s += "RUN_AOT"; 301 prefix = " | "; 302 } 303 return s; 304 } 305 }