1 /* 2 * Copyright (c) 2021, 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 package compiler.lib.ir_framework; 25 26 import compiler.lib.ir_framework.driver.FlagVMProcess; 27 import compiler.lib.ir_framework.driver.TestVMException; 28 import compiler.lib.ir_framework.driver.TestVMProcess; 29 import compiler.lib.ir_framework.driver.irmatching.IRMatcher; 30 import compiler.lib.ir_framework.driver.irmatching.IRViolationException; 31 import compiler.lib.ir_framework.driver.irmatching.Matchable; 32 import compiler.lib.ir_framework.driver.irmatching.parser.TestClassParser; 33 import compiler.lib.ir_framework.shared.*; 34 import compiler.lib.ir_framework.test.TestVM; 35 import jdk.test.lib.Platform; 36 import jdk.test.lib.Utils; 37 import jdk.test.lib.helpers.ClassFileInstaller; 38 import jdk.test.whitebox.WhiteBox; 39 import jtreg.SkippedException; 40 41 import java.io.PrintWriter; 42 import java.io.StringWriter; 43 import java.lang.reflect.Method; 44 import java.net.MalformedURLException; 45 import java.net.URL; 46 import java.net.URLClassLoader; 47 import java.nio.file.Path; 48 import java.util.*; 49 import java.util.concurrent.atomic.AtomicInteger; 50 import java.util.stream.Collectors; 51 import java.util.stream.Stream; 52 53 /** 54 * This class represents the main entry point to the test framework whose main purpose is to perform regex-based checks on 55 * the C2 IR shape emitted by the VM flags {@code -XX:+PrintIdeal} and {@code -XX:+PrintOptoAssembly}. The framework can 56 * also be used for other non-IR matching (and non-compiler) tests by providing easy to use annotations for commonly used 57 * testing patterns and compiler control flags. 58 * <p> 59 * The framework offers various annotations to control how your test code should be invoked and being checked. There are 60 * three kinds of tests depending on how much control is needed over the test invocation: 61 * <b>Base tests</b> (see {@link Test}), <b>checked tests</b> (see {@link Check}), and <b>custom run tests</b> 62 * (see {@link Run}). Each type of test needs to define a unique <i>test method</i> that specifies a {@link Test @Test} 63 * annotation which represents the test code that is eventually executed by the test framework. More information about 64 * the usage and how to write different tests can be found in {@link Test}, {@link Check}, and {@link Run}. 65 * <p> 66 * Each test method can specify an arbitrary number of IR rules. This is done by using {@link IR @IR} annotations which 67 * can define regex strings that are matched on the output of {@code -XX:+PrintIdeal} and {@code -XX:+PrintOptoAssembly}. 68 * The matching is done after the test method was (optionally) warmed up and compiled. More information about the usage 69 * and how to write different IR rules can be found at {@link IR}. 70 * <p> 71 * This framework should be used with the following JTreg setup in your Test.java file in package <i>some.package</i>: 72 * <pre> 73 * {@literal @}library /test/lib 74 * {@literal @}run driver some.package.Test 75 * </pre> 76 * Note that even though the framework uses the Whitebox API internally, it is not required to build and enabel it in the 77 * JTreg test if the test itself is not utilizing any Whitebox features directly. 78 * <p> 79 * To specify additional flags, use {@link #runWithFlags(String...)}, {@link #addFlags(String...)}, or 80 * {@link #addScenarios(Scenario...)} where the scenarios can also be used to run different flag combinations 81 * (instead of specifying multiple JTreg {@code @run} entries). 82 * <p> 83 * After annotating your test code with the framework specific annotations, the framework needs to be invoked from the 84 * {@code main()} method of your JTreg test. There are two ways to do so. The first way is by calling the various 85 * {@code runXX()} methods of {@link TestFramework}. The second way, which gives more control, is to create a new 86 * {@code TestFramework} builder object on which {@link #start()} needs to be eventually called to start the testing. 87 * <p> 88 * The framework is called from the <i>driver VM</i> in which the JTreg test is initially run by specifying {@code 89 * @run driver} in the JTreg header. This strips all additionally specified JTreg VM and Javaoptions. 90 * The framework creates a new <i>flag VM</i> with all these flags added again in order to figure out which flags are 91 * required to run the tests specified in the test class (e.g. {@code -XX:+PrintIdeal} and {@code -XX:+PrintOptoAssembly} 92 * for IR matching). 93 * <p> 94 * After the flag VM terminates, it starts a new <i>test VM</i> which performs the execution of the specified 95 * tests in the test class as described in {@link Test}, {@link Check}, and {@link Run}. 96 * <p> 97 * In a last step, once the test VM has terminated without exceptions, IR matching is performed if there are any IR 98 * rules and if no VM flags disable it (e.g. not running with {@code -Xint}, see {@link IR} for more details). 99 * The IR regex matching is done on the output of {@code -XX:+PrintIdeal} and {@code -XX:+PrintOptoAssembly} by parsing 100 * the hotspot_pid file of the test VM. Failing IR rules are reported by throwing a {@link IRViolationException}. 101 * 102 * @see Test 103 * @see Check 104 * @see Run 105 * @see IR 106 */ 107 public class TestFramework { 108 /** 109 * JTreg can define additional VM (-Dtest.vm.opts) and Javaoptions (-Dtest.java.opts) flags. IR verification is only 110 * performed when all these additional JTreg flags (does not include additionally added framework and scenario flags 111 * by user code) are whitelisted. 112 * 113 * <p> 114 * A flag is whitelisted if it is a property flag (starting with -D), -ea, -esa, or if the flag name contains any of 115 * the entries of this list as a substring (partial match). 116 */ 117 public static final Set<String> JTREG_WHITELIST_FLAGS = new HashSet<>( 118 Arrays.asList( 119 // The following substrings are part of more than one VM flag 120 "RAM", 121 "Heap", 122 "Trace", 123 "Print", 124 "Verify", 125 "UseNewCode", 126 "Xmn", 127 "Xms", 128 "Xmx", 129 "Xss", 130 // The following substrings are only part of one VM flag (=exact match) 131 "CreateCoredumpOnCrash", 132 "IgnoreUnrecognizedVMOptions", 133 "UnlockDiagnosticVMOptions", 134 "UnlockExperimentalVMOptions", 135 "BackgroundCompilation", 136 "Xbatch", 137 "TieredCompilation", 138 "CompileThreshold", 139 "Xmixed", 140 "server", 141 "AlignVector", 142 "UseAVX", 143 "UseSSE", 144 "UseSVE", 145 "Xlog", 146 "LogCompilation", 147 "UseCompactObjectHeaders", 148 "UseFMA", 149 "AOTCache", 150 // Riscv 151 "UseRVV", 152 "UseZbb", 153 "UseZfh", 154 "UseZicond", 155 "UseZvbb" 156 ) 157 ); 158 159 public static final boolean VERBOSE = Boolean.getBoolean("Verbose"); 160 public static final boolean PRINT_RULE_MATCHING_TIME = Boolean.getBoolean("PrintRuleMatchingTime"); 161 public static final boolean TESTLIST = !System.getProperty("Test", "").isEmpty(); 162 public static final boolean EXCLUDELIST = !System.getProperty("Exclude", "").isEmpty(); 163 private static final boolean REPORT_STDOUT = Boolean.getBoolean("ReportStdout"); 164 // Only used for internal testing and should not be used for normal user testing. 165 166 private static final String RERUN_HINT = """ 167 ############################################################# 168 - To only run the failed tests use -DTest, -DExclude, 169 and/or -DScenarios. 170 - To also get the standard output of the test VM run with 171 -DReportStdout=true or for even more fine-grained logging 172 use -DVerbose=true. 173 ############################################################# 174 """ + System.lineSeparator(); 175 176 private boolean irVerificationPossible = Boolean.parseBoolean(System.getProperty("VerifyIR", "true")); 177 private boolean shouldVerifyIR; // Should we perform IR matching? 178 private static boolean toggleBool; 179 180 private final Class<?> testClass; 181 private Set<Class<?>> helperClasses; 182 private List<Scenario> scenarios; 183 private Set<Integer> scenarioIndices; 184 private List<String> flags; 185 private int defaultWarmup = -1; 186 private boolean testClassesOnBootClassPath; 187 private boolean isAllowNotCompilable = false; 188 189 /* 190 * Public interface methods 191 */ 192 193 /** 194 * Creates an instance acting as a builder to test the class from which this constructor was invoked from. 195 * Use this constructor if you want to use multiple run options (flags, helper classes, scenarios). 196 * Use the associated add methods ({@link #addFlags(String...)}, {@link #addScenarios(Scenario...)}, 197 * {@link #addHelperClasses(Class...)}) to set up everything and then start the testing by invoking {@link #start()}. 198 */ 199 public TestFramework() { 200 this(StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).getCallerClass()); 201 } 202 203 /** 204 * Creates an instance acting as a builder to test {@code testClass}. 205 * Use this constructor if you want to use multiple run options (flags, helper classes, scenarios). 206 * Use the associated add methods ({@link #addFlags(String...)}, {@link #addScenarios(Scenario...)}, 207 * {@link #addHelperClasses(Class...)}) to set up everything and then start the testing by invoking {@link #start()}. 208 * 209 * @param testClass the class to be tested by the framework. 210 * @see #TestFramework() 211 */ 212 public TestFramework(Class<?> testClass) { 213 TestRun.check(testClass != null, "Test class cannot be null"); 214 this.testClass = testClass; 215 if (VERBOSE) { 216 System.out.println("Test class: " + testClass); 217 } 218 } 219 220 /** 221 * Tests the class from which this method was invoked from. 222 */ 223 public static void run() { 224 StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); 225 run(walker.getCallerClass()); 226 } 227 228 /** 229 * Tests {@code testClass}. 230 * 231 * @param testClass the class to be tested by the framework. 232 * @see #run() 233 */ 234 public static void run(Class<?> testClass) { 235 TestFramework framework = new TestFramework(testClass); 236 framework.start(); 237 } 238 239 /** 240 * Tests the class from which this method was invoked from. The test VM is called with the specified {@code flags}. 241 * <ul> 242 * <li><p>The {@code flags} override any set VM or Javaoptions flags by JTreg by default.<p> 243 * Use {@code -DPreferCommandLineFlags=true} if you want to prefer the JTreg VM and Javaoptions flags over 244 * the specified {@code flags} of this method.</li> 245 * <li><p>If you want to run your entire JTreg test with additional flags, use this method.</li> 246 * <li><p>If you want to run your entire JTreg test with additional flags but for another test class then the one 247 * from which this method was called from, use {@link #addFlags(String...)}, use this method.</li> 248 * <li><p>If you want to run your JTreg test with multiple flag combinations, use 249 * {@link #addScenarios(Scenario...)}</li> 250 * </ul> 251 * 252 * @param flags VM flags to be used for the test VM. 253 */ 254 public static void runWithFlags(String... flags) { 255 StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); 256 TestFramework framework = new TestFramework(walker.getCallerClass()); 257 framework.addFlags(flags); 258 framework.start(); 259 } 260 261 /** 262 * Add VM flags to be used for the test VM. These flags override any VM or Javaoptions set by JTreg by default.<p> 263 * Use {@code -DPreferCommandLineFlags=true} if you want to prefer the VM or Javaoptions over the scenario flags. 264 * 265 * <p> 266 * The testing can be started by invoking {@link #start()} 267 * 268 * @param flags VM options to be applied to the test VM. 269 * @return the same framework instance. 270 */ 271 public TestFramework addFlags(String... flags) { 272 TestRun.check(flags != null && Arrays.stream(flags).noneMatch(Objects::isNull), "A flag cannot be null"); 273 if (this.flags == null) { 274 this.flags = new ArrayList<>(); 275 } 276 this.flags.addAll(Arrays.asList(flags)); 277 return this; 278 } 279 280 /** 281 * Add helper classes that can specify additional compile command annotations ({@link ForceCompile @ForceCompile}, 282 * {@link DontCompile @DontCompile}, {@link ForceInline @ForceInline}, {@link DontInline @DontInline}) to be applied 283 * while testing {@code testClass} (also see description of {@link TestFramework}). 284 * 285 * <p> 286 * Duplicates in {@code helperClasses} are ignored. If a class is used by the test class that does not specify any 287 * compile command annotations, you do not need to include it with this method. If no helper class specifies any 288 * compile commands, you do not need to call this method at all. 289 * 290 * <p> 291 * The testing can be started by invoking {@link #start()}. 292 * 293 * @param helperClasses helper classes containing compile command annotations ({@link ForceCompile}, 294 * {@link DontCompile}, {@link ForceInline}, {@link DontInline}) to be applied 295 * while testing {@code testClass} (also see description of {@link TestFramework}). 296 * @return the same framework instance. 297 */ 298 public TestFramework addHelperClasses(Class<?>... helperClasses) { 299 TestRun.check(helperClasses != null && Arrays.stream(helperClasses).noneMatch(Objects::isNull), 300 "A Helper class cannot be null"); 301 if (this.helperClasses == null) { 302 this.helperClasses = new HashSet<>(); 303 } 304 305 this.helperClasses.addAll(Arrays.asList(helperClasses)); 306 return this; 307 } 308 309 /** 310 * Add scenarios to be used for the test VM. A test VM is called for each scenario in {@code scenarios} by using the 311 * specified VM flags in the scenario. The scenario flags override any flags set by {@link #addFlags(String...)} 312 * and thus also override any VM or Javaoptions set by JTreg by default.<p> 313 * Use {@code -DPreferCommandLineFlags=true} if you want to prefer the VM and Javaoptions over the scenario flags. 314 * 315 * <p> 316 * The testing can be started by invoking {@link #start()} 317 * 318 * @param scenarios scenarios which specify specific flags for the test VM. 319 * @return the same framework instance. 320 */ 321 public TestFramework addScenarios(Scenario... scenarios) { 322 TestFormat.checkAndReport(scenarios != null && Arrays.stream(scenarios).noneMatch(Objects::isNull), 323 "A scenario cannot be null"); 324 if (this.scenarios == null) { 325 this.scenarios = new ArrayList<>(); 326 this.scenarioIndices = new HashSet<>(); 327 } 328 329 for (Scenario scenario : scenarios) { 330 int scenarioIndex = scenario.getIndex(); 331 TestFormat.checkNoThrow(scenarioIndices.add(scenarioIndex), 332 "Cannot define two scenarios with the same index " + scenarioIndex); 333 this.scenarios.add(scenario); 334 } 335 TestFormat.throwIfAnyFailures(); 336 return this; 337 } 338 339 /** 340 * Add the cross-product (cartesian product) of sets of flags as Scenarios. Unlike when when constructing 341 * scenarios directly a string can contain multiple flags separated with a space. This allows grouping 342 * flags that have to be specified togeher. Further, an empty string in a set stands in for "no flag". 343 * <p> 344 * Example: 345 * <pre> 346 * addCrossProductScenarios(Set.of("", "-Xint", "-Xbatch -XX:-TieredCompilation"), 347 * Set.of("-XX:+UseNewCode", "-XX:UseNewCode2")) 348 * </pre> 349 * produces the following Scenarios 350 * <pre> 351 * Scenario(0, "-XX:+UseNewCode") 352 * Scenario(1, "-XX:+UseNewCode2") 353 * Scenario(2, "-Xint", "-XX:+UseNewCode") 354 * Scenario(3, "-Xint", "-XX:+UseNewCode2") 355 * Scenario(4, "-Xbatch -XX:-TieredCompilation", "-XX:+UseNewCode") 356 * Scenario(5, "-Xbatch -XX:-TieredCompilation", "-XX:+UseNewCode2") 357 * </pre> 358 * 359 * @param sets sets of flags to generate the cross product for. 360 * @return the same framework instance. 361 */ 362 @SafeVarargs 363 final public TestFramework addCrossProductScenarios(Set<String>... flagSets) { 364 TestFormat.checkAndReport(flagSets != null && 365 Arrays.stream(flagSets).noneMatch(Objects::isNull) && 366 Arrays.stream(flagSets).flatMap(Set::stream).noneMatch(Objects::isNull), 367 "Flags must not be null"); 368 if (flagSets.length == 0) { 369 return this; 370 } 371 372 int initIdx = 0; 373 if (this.scenarioIndices != null && !this.scenarioIndices.isEmpty()) { 374 initIdx = this.scenarioIndices.stream().max(Comparator.comparingInt(Integer::intValue)).get() + 1; 375 } 376 AtomicInteger idx = new AtomicInteger(initIdx); 377 378 Stream<List<String>> crossProduct = Arrays.stream(flagSets) 379 .reduce( 380 Stream.of(Collections.<String>emptyList()), // Initialize Stream<List<String>> acc with a Stream containing an empty list of Strings. 381 (Stream<List<String>> acc, Set<String> set) -> 382 acc.flatMap(lAcc -> // For each List<String>> lAcc in acc... 383 set.stream().map(flag -> { // ...and each flag in the current set... 384 List<String> newList = new ArrayList<>(lAcc); // ...create a new list containing lAcc... 385 newList.add(flag); // ...and append the flag. 386 return newList; 387 }) // This results in one List<List<String>> for each lAcc... 388 ), // ...that get flattend into one big List<List<String>>. 389 (a, b) -> Stream.concat(a, b)); // combiner; if any reduction steps are executed in parallel, just concat two streams. 390 391 Scenario[] newScenarios = crossProduct 392 .map(flags -> new Scenario( // For each List<String> flags in crossProduct create a new Scenario. 393 idx.getAndIncrement(), 394 flags.stream() // Process flags 395 .map(s -> Set.of(s.split("[ ]"))) // Split muliple flags in the same string into separate strings. 396 .flatMap(Collection::stream) // Flatten the Stream<List<String>> into Stream<String>>. 397 .filter(s -> !s.isEmpty()) // Remove empty string flags. 398 .collect(Collectors.toList()) 399 .toArray(new String[0]))) 400 .collect(Collectors.toList()).toArray(new Scenario[0]); 401 return addScenarios(newScenarios); 402 } 403 404 /** 405 * Add test classes to boot classpath. This adds all classes found on path {@link jdk.test.lib.Utils#TEST_CLASSES} 406 * to the boot classpath with "-Xbootclasspath/a". This is useful when trying to run tests in a privileged mode. 407 */ 408 public TestFramework addTestClassesToBootClassPath() { 409 this.testClassesOnBootClassPath = true; 410 return this; 411 } 412 413 /** 414 * Start the testing of the implicitly (by {@link #TestFramework()}) or explicitly (by {@link #TestFramework(Class)}) 415 * set test class. 416 */ 417 public void start() { 418 if (shouldInstallWhiteBox()) { 419 installWhiteBox(); 420 } 421 checkCompatibleFlags(); 422 checkIRRuleCompilePhasesFormat(); 423 disableIRVerificationIfNotFeasible(); 424 425 if (scenarios == null) { 426 try { 427 start(null); 428 } catch (TestVMException e) { 429 System.err.println(System.lineSeparator() + e.getExceptionInfo() + RERUN_HINT); 430 throw e; 431 } catch (IRViolationException e) { 432 System.out.println(e.getCompilations()); 433 System.err.println(System.lineSeparator() + e.getExceptionInfo() + System.lineSeparator() + RERUN_HINT); 434 throw e; 435 } 436 } else { 437 startWithScenarios(); 438 } 439 } 440 441 private void checkIRRuleCompilePhasesFormat() { 442 for (Method method : testClass.getDeclaredMethods()) { 443 for (IR irAnno : method.getAnnotationsByType(IR.class)) { 444 TestFormat.checkNoThrow(irAnno.phase().length > 0, 445 "@IR rule " + irAnno + " must specify a non-empty list of compile " + 446 "phases \"phase\" at " + method); 447 } 448 } 449 TestFormat.throwIfAnyFailures(); 450 } 451 452 /** 453 * Try to load the Whitebox class from the user directory with a custom class loader. If the user has already built the 454 * Whitebox, we can load it. Otherwise, the framework needs to install it. 455 * 456 * @return true if the framework needs to install the Whitebox 457 */ 458 private boolean shouldInstallWhiteBox() { 459 try { 460 URL url = Path.of(System.getProperty("user.dir")).toUri().toURL(); 461 URLClassLoader userDirClassLoader = 462 URLClassLoader.newInstance(new URL[] {url}, TestFramework.class.getClassLoader().getParent()); 463 Class.forName(WhiteBox.class.getName(), false, userDirClassLoader); 464 } catch (MalformedURLException e) { 465 throw new TestFrameworkException("corrupted user.dir property", e); 466 } catch (ClassNotFoundException e) { 467 // We need to manually install the WhiteBox if we cannot load the WhiteBox class from the user directory. 468 // This happens when the user test does not explicitly install the WhiteBox as part of the test. 469 return true; 470 } 471 return false; 472 } 473 474 /** 475 * Set a new default warm-up (overriding the framework default of 2000 at 476 * {@link TestVM#WARMUP_ITERATIONS}) to be applied for all tests that do not specify an explicit 477 * warm-up with {@link Warmup @Warmup}. 478 * 479 * @param defaultWarmup a new non-negative default warm-up. 480 * @return the same framework instance. 481 */ 482 public TestFramework setDefaultWarmup(int defaultWarmup) { 483 TestFormat.checkAndReport(defaultWarmup >= 0, "Cannot specify a negative default warm-up"); 484 this.defaultWarmup = defaultWarmup; 485 return this; 486 } 487 488 /** 489 * In rare cases, methods may not be compilable because of a compilation bailout. By default, this leads to a 490 * test failure. However, if such cases are expected in multiple methods in a test class, this flag can be set to 491 * true, which allows any test to pass even if there is a compilation bailout. If only selected methods are prone 492 * to bail out, it is preferred to use {@link Test#allowNotCompilable()} instead for more fine-grained control. 493 * By setting this flag, any associated {@link IR} rule of a test is only executed if the test method was compiled, 494 * and else it is ignored silently. 495 */ 496 public TestFramework allowNotCompilable() { 497 this.isAllowNotCompilable = true; 498 return this; 499 } 500 501 /** 502 * Get the VM output of the test VM. Use {@code -DVerbose=true} to enable more debug information. If scenarios 503 * were run, use {@link Scenario#getTestVMOutput()}. 504 * 505 * @return the last test VM output. 506 */ 507 public static String getLastTestVMOutput() { 508 return TestVMProcess.getLastTestVMOutput(); 509 } 510 511 /* 512 * The following methods are only intended to be called from actual @Test methods and not from the main() method of 513 * a JTreg test. Calling these methods from main() results in a linking exception (Whitebox not yet loaded and enabled). 514 */ 515 516 /** 517 * Compile {@code m} at compilation level {@code compLevel}. {@code m} is first enqueued and might not be compiled, 518 * yet, upon returning from this method. 519 * 520 * @param m the method to be compiled. 521 * @param compLevel the (valid) compilation level at which the method should be compiled. 522 * @throws TestRunException if compilation level is {@link CompLevel#SKIP} or {@link CompLevel#WAIT_FOR_COMPILATION}. 523 */ 524 public static void compile(Method m, CompLevel compLevel) { 525 TestVM.compile(m, compLevel); 526 } 527 528 /** 529 * Deoptimize {@code m}. 530 * 531 * @param m the method to be deoptimized. 532 */ 533 public static void deoptimize(Method m) { 534 TestVM.deoptimize(m); 535 } 536 537 /** 538 * Returns a boolean indicating if {@code m} is compiled at any level. 539 * 540 * @param m the method to be checked. 541 * @return {@code true} if {@code m} is compiled at any level; 542 * {@code false} otherwise. 543 */ 544 public static boolean isCompiled(Method m) { 545 return TestVM.isCompiled(m); 546 } 547 548 /** 549 * Returns a boolean indicating if {@code m} is compiled with C1. 550 * 551 * @param m the method to be checked. 552 * @return {@code true} if {@code m} is compiled with C1; 553 * {@code false} otherwise. 554 */ 555 public static boolean isC1Compiled(Method m) { 556 return TestVM.isC1Compiled(m); 557 } 558 559 /** 560 * Returns a boolean indicating if {@code m} is compiled with C2. 561 * 562 * @param m the method to be checked. 563 * @return {@code true} if {@code m} is compiled with C2; 564 * {@code false} otherwise. 565 */ 566 public static boolean isC2Compiled(Method m) { 567 return TestVM.isC2Compiled(m); 568 } 569 570 /** 571 * Returns a boolean indicating if {@code m} is compiled at the specified {@code compLevel}. 572 * 573 * @param m the method to be checked. 574 * @param compLevel the compilation level. 575 * @return {@code true} if {@code m} is compiled at {@code compLevel}; 576 * {@code false} otherwise. 577 */ 578 public static boolean isCompiledAtLevel(Method m, CompLevel compLevel) { 579 return TestVM.isCompiledAtLevel(m, compLevel); 580 } 581 582 /** 583 * Checks if {@code m} is compiled at any level. 584 * 585 * @param m the method to be checked. 586 * @throws TestRunException if {@code m} is not compiled at any level. 587 */ 588 public static void assertCompiled(Method m) { 589 TestVM.assertCompiled(m); 590 } 591 592 /** 593 * Checks if {@code m} is not compiled at any level. 594 * 595 * @param m the method to be checked. 596 * @throws TestRunException if {@code m} is compiled at any level. 597 */ 598 public static void assertNotCompiled(Method m) { 599 TestVM.assertNotCompiled(m); 600 } 601 602 /** 603 * Verifies that {@code m} is compiled with C1. 604 * 605 * @param m the method to be verified. 606 * @throws TestRunException if {@code m} is not compiled with C1. 607 */ 608 public static void assertCompiledByC1(Method m) { 609 TestVM.assertCompiledByC1(m); 610 } 611 612 /** 613 * Verifies that {@code m} is compiled with C2. 614 * 615 * @param m the method to be checked. 616 * @throws TestRunException if {@code m} is not compiled with C2. 617 */ 618 public static void assertCompiledByC2(Method m) { 619 TestVM.assertCompiledByC2(m); 620 } 621 622 /** 623 * Verifies that {@code m} is compiled at the specified {@code compLevel}. 624 * 625 * @param m the method to be checked. 626 * @param compLevel the compilation level. 627 * @throws TestRunException if {@code m} is not compiled at {@code compLevel}. 628 */ 629 public static void assertCompiledAtLevel(Method m, CompLevel compLevel) { 630 TestVM.assertCompiledAtLevel(m, compLevel); 631 } 632 633 /** 634 * Verifies that {@code m} was deoptimized after being C1 compiled. 635 * 636 * @param m the method to be checked. 637 * @throws TestRunException if {@code m} is was not deoptimized after being C1 compiled. 638 */ 639 public static void assertDeoptimizedByC1(Method m) { 640 TestVM.assertDeoptimizedByC1(m); 641 } 642 643 /** 644 * Verifies that {@code m} was deoptimized after being C2 compiled. 645 * 646 * @param m the method to be checked. 647 * @throws TestRunException if {@code m} is was not deoptimized after being C2 compiled. 648 */ 649 public static void assertDeoptimizedByC2(Method m) { 650 TestVM.assertDeoptimizedByC2(m); 651 } 652 653 /** 654 * Returns a different boolean each time this method is invoked (switching between {@code false} and {@code true}). 655 * The very first invocation returns {@code false}. Note that this method could be used by different tests and 656 * thus the first invocation for a test could be {@code true} or {@code false} depending on how many times 657 * other tests have already invoked this method. 658 * 659 * @return an inverted boolean of the result of the last invocation of this method. 660 */ 661 public static boolean toggleBoolean() { 662 toggleBool = !toggleBool; 663 return toggleBool; 664 } 665 666 /* 667 * End of public interface methods 668 */ 669 670 /** 671 * Used to move Whitebox class to the right folder in the JTreg test 672 */ 673 private void installWhiteBox() { 674 try { 675 ClassFileInstaller.main(WhiteBox.class.getName()); 676 } catch (Exception e) { 677 throw new Error("failed to install whitebox classes", e); 678 } 679 } 680 681 /** 682 * Disable IR verification completely in certain cases. 683 */ 684 private void disableIRVerificationIfNotFeasible() { 685 if (!irVerificationPossible) { 686 return; 687 } 688 689 boolean debugTest = Platform.isDebugBuild(); 690 boolean intTest = !Platform.isInt(); 691 boolean compTest = !Platform.isComp(); 692 boolean irTest = hasIRAnnotations(); 693 // No IR verification is done if additional non-whitelisted JTreg VM or Javaoptions flag is specified. 694 List<String> nonWhiteListedFlags = anyNonWhitelistedJTregVMAndJavaOptsFlags(); 695 boolean nonWhiteListedTest = nonWhiteListedFlags.isEmpty(); 696 697 irVerificationPossible = debugTest && intTest && compTest && irTest && nonWhiteListedTest; 698 if (irVerificationPossible) { 699 return; 700 } 701 702 System.out.println("IR verification disabled due to the following reason(s):"); 703 if (!debugTest) { 704 System.out.println("- Not running a debug build (required for PrintIdeal and PrintOptoAssembly)"); 705 } 706 if (!intTest) { 707 System.out.println("- Running with -Xint (no compilations)"); 708 } 709 if (!compTest) { 710 System.out.println("- Running with -Xcomp (use warm-up of 0 instead)"); 711 } 712 if (!irTest) { 713 System.out.println("- Test " + testClass + " not specifying any @IR annotations"); 714 } 715 if (!nonWhiteListedTest) { 716 System.out.println("- Using non-whitelisted JTreg VM or Javaoptions flag(s):"); 717 nonWhiteListedFlags.forEach((f) -> System.out.println(" - " + f)); 718 } 719 720 System.out.println(""); 721 } 722 723 /** 724 * For scenarios: Run the tests with the scenario settings and collect all exceptions to be able to run all 725 * scenarios without prematurely throwing an exception. Format violations, however, are wrong for all scenarios 726 * and thus is reported immediately on the first scenario execution. 727 */ 728 private void startWithScenarios() { 729 Map<Scenario, Exception> exceptionMap = new TreeMap<>(Comparator.comparingInt(Scenario::getIndex)); 730 for (Scenario scenario : scenarios) { 731 try { 732 start(scenario); 733 } catch (TestFormatException e) { 734 // Test format violation is wrong for all the scenarios. Only report once. 735 throw e; 736 } catch (Exception e) { 737 exceptionMap.put(scenario, e); 738 } 739 } 740 if (!exceptionMap.isEmpty()) { 741 reportScenarioFailures(exceptionMap); 742 } 743 } 744 745 private void reportScenarioFailures(Map<Scenario, Exception> exceptionMap) { 746 String failedScenarios = "The following scenarios have failed: #" 747 + exceptionMap.keySet().stream() 748 .map(s -> String.valueOf(s.getIndex())) 749 .collect(Collectors.joining(", #")); 750 StringBuilder builder = new StringBuilder(failedScenarios); 751 builder.append(System.lineSeparator()).append(System.lineSeparator()); 752 for (Map.Entry<Scenario, Exception> entry : exceptionMap.entrySet()) { 753 Exception e = entry.getValue(); 754 Scenario scenario = entry.getKey(); 755 String errorMsg = ""; 756 if (scenario != null) { 757 errorMsg = getScenarioTitleAndFlags(scenario); 758 } 759 if (e instanceof IRViolationException irException) { 760 // For IR violations, only show the actual violations and not the (uninteresting) stack trace. 761 if (scenario != null) { 762 System.out.println("Scenario #" + scenario.getIndex()); 763 } 764 System.out.println(irException.getCompilations()); 765 builder.append(errorMsg).append(System.lineSeparator()).append(irException.getExceptionInfo()); 766 } else if (e instanceof TestVMException testVMException) { 767 builder.append(errorMsg).append(System.lineSeparator()).append(testVMException.getExceptionInfo()); 768 } else { 769 // Print stack trace otherwise 770 StringWriter errors = new StringWriter(); 771 e.printStackTrace(new PrintWriter(errors)); 772 builder.append(errors); 773 } 774 builder.append(System.lineSeparator()); 775 } 776 System.err.println(builder); 777 if (!VERBOSE && !REPORT_STDOUT && !TESTLIST && !EXCLUDELIST) { 778 // Provide a hint to the user how to get additional output/debugging information. 779 System.err.println(RERUN_HINT); 780 } 781 throw new TestRunException(failedScenarios + ". Please check stderr for more information."); 782 } 783 784 private static String getScenarioTitleAndFlags(Scenario scenario) { 785 StringBuilder builder = new StringBuilder(); 786 String title = "Scenario #" + scenario.getIndex(); 787 builder.append(title).append(System.lineSeparator()).append("=".repeat(title.length())) 788 .append(System.lineSeparator()); 789 builder.append("Scenario flags: [").append(String.join(", ", scenario.getFlags())).append("]") 790 .append(System.lineSeparator()); 791 return builder.toString(); 792 } 793 794 /** 795 * Execute a separate "flag" VM with White Box access to determine all test VM flags. The flag VM sends an encoding of 796 * all required flags for the test VM to the driver VM over a socket. Once the flag VM exits, this driver VM parses the 797 * test VM flags, which also determine if IR matching should be done, and then starts the test VM to execute all tests. 798 */ 799 private void start(Scenario scenario) { 800 if (scenario != null && !scenario.isEnabled()) { 801 System.out.println("Disabled scenario #" + scenario.getIndex() + "! This scenario is not present in set flag " + 802 "-DScenarios and is therefore not executed."); 803 return; 804 } 805 shouldVerifyIR = irVerificationPossible; 806 try { 807 // Use TestFramework flags and scenario flags for new VMs. 808 List<String> additionalFlags = new ArrayList<>(); 809 if (flags != null) { 810 additionalFlags.addAll(flags); 811 } 812 if (scenario != null) { 813 List<String> scenarioFlags = scenario.getFlags(); 814 String scenarioFlagsString = scenarioFlags.isEmpty() ? "" : " - [" + String.join(", ", scenarioFlags) + "]"; 815 System.out.println("Scenario #" + scenario.getIndex() + scenarioFlagsString + ":"); 816 additionalFlags.addAll(scenarioFlags); 817 } 818 String frameworkAndScenarioFlags = additionalFlags.isEmpty() ? 819 "" : " - [" + String.join(", ", additionalFlags) + "]"; 820 821 if (shouldVerifyIR) { 822 // Only need to use flag VM if an IR verification is possibly done. 823 System.out.println("Run Flag VM:"); 824 FlagVMProcess flagVMProcess = new FlagVMProcess(testClass, additionalFlags); 825 shouldVerifyIR = flagVMProcess.shouldVerifyIR(); 826 if (shouldVerifyIR) { 827 // Add more flags for the test VM which are required to do IR verification. 828 additionalFlags.addAll(flagVMProcess.getTestVMFlags()); 829 } // else: Flag VM found a reason to not do IR verification. 830 } else { 831 System.out.println("Skip Flag VM due to not performing IR verification."); 832 } 833 834 System.out.println("Run Test VM" + frameworkAndScenarioFlags + ":"); 835 runTestVM(additionalFlags); 836 } finally { 837 if (scenario != null) { 838 scenario.setTestVMOutput(TestVMProcess.getLastTestVMOutput()); 839 } 840 System.out.println(); 841 } 842 } 843 844 private boolean hasIRAnnotations() { 845 return Arrays.stream(testClass.getDeclaredMethods()).anyMatch(m -> m.getAnnotationsByType(IR.class).length > 0); 846 } 847 848 private void checkCompatibleFlags() { 849 for (String flag : Utils.getTestJavaOpts()) { 850 if (flag.contains("-agentpath")) { 851 throw new SkippedException("Can't run test with agent."); 852 } 853 } 854 } 855 856 private List<String> anyNonWhitelistedJTregVMAndJavaOptsFlags() { 857 List<String> flags = Arrays.stream(Utils.getTestJavaOpts()) 858 .map(s -> s.replaceFirst("-XX:[+|-]?|-(?=[^D|^e])", "")) 859 .collect(Collectors.toList()); 860 List<String> nonWhiteListedFlags = new ArrayList(); 861 for (String flag : flags) { 862 if (flag.contains("agentpath")) { 863 throw new SkippedException("Can't run test with -javaagent"); 864 } 865 // Property flags (prefix -D), -ea and -esa are whitelisted. 866 if (!flag.startsWith("-D") && !flag.startsWith("-e") && JTREG_WHITELIST_FLAGS.stream().noneMatch(flag::contains)) { 867 // Found VM flag that is not whitelisted 868 nonWhiteListedFlags.add(flag); 869 } 870 } 871 return nonWhiteListedFlags; 872 } 873 874 private void runTestVM(List<String> additionalFlags) { 875 TestVMProcess testVMProcess = new TestVMProcess(additionalFlags, testClass, helperClasses, defaultWarmup, 876 isAllowNotCompilable, testClassesOnBootClassPath); 877 if (shouldVerifyIR) { 878 try { 879 TestClassParser testClassParser = new TestClassParser(testClass, isAllowNotCompilable); 880 Matchable testClassMatchable = testClassParser.parse(testVMProcess.getHotspotPidFileName(), 881 testVMProcess.getIrEncoding()); 882 IRMatcher matcher = new IRMatcher(testClassMatchable); 883 matcher.match(); 884 } catch (IRViolationException e) { 885 e.addCommandLine(testVMProcess.getCommandLine()); 886 throw e; 887 } 888 } else { 889 System.out.println("IR verification disabled either due to no @IR annotations, through explicitly setting " + 890 "-DVerify=false, due to not running a debug build, using a non-whitelisted JTreg VM or " + 891 "Javaopts flag like -Xint, or running the test VM with other VM flags added by user code " + 892 "that make the IR verification impossible (e.g. -XX:-UseCompile, " + 893 "-XX:TieredStopAtLevel=[1,2,3], etc.)."); 894 } 895 } 896 897 public static void check(boolean test, String failureMessage) { 898 if (!test) { 899 throw new TestFrameworkException(failureMessage); 900 } 901 } 902 }