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.test; 25 26 import compiler.lib.ir_framework.*; 27 import compiler.lib.ir_framework.Compiler; 28 import compiler.lib.ir_framework.shared.*; 29 import jdk.test.lib.Platform; 30 import jdk.test.lib.Utils; 31 import jdk.test.whitebox.WhiteBox; 32 33 import java.io.PrintWriter; 34 import java.io.StringWriter; 35 import java.lang.annotation.Annotation; 36 import java.lang.reflect.*; 37 import java.util.*; 38 import java.util.stream.Collectors; 39 import java.util.stream.Stream; 40 41 import static compiler.lib.ir_framework.shared.TestFrameworkSocket.PRINT_TIMES_TAG; 42 43 /** 44 * This class' main method is called from {@link TestFramework} and represents the so-called "test VM". The class is 45 * the heart of the framework and is responsible for executing all the specified tests in the test class. It uses the 46 * Whitebox API and reflection to achieve this task. 47 */ 48 public class TestVM { 49 private static final WhiteBox WHITE_BOX; 50 51 static { 52 try { 53 WHITE_BOX = WhiteBox.getWhiteBox(); 54 } catch (UnsatisfiedLinkError e) { 55 System.err.println(System.lineSeparator() + """ 56 ########################################################## 57 - Did you call a test-related interface method from 58 TestFramework in main() of your test? Make sure to 59 only call setup/run methods and no checks or 60 assertions from main() of your test! 61 - Are you rerunning the test VM (TestVM class) 62 directly after a JTreg run? Make sure to start it 63 from within JTwork/scratch and with the flag 64 -DReproduce=true! 65 ########################################################## 66 """); 67 throw e; 68 } 69 } 70 71 /** 72 * The default number of warm-up iterations used to warm up a {@link Test} annotated test method. 73 * Use {@code -DWarmup=XY} to specify a different default value. An individual warm-up can also be 74 * set by specifying a {@link Warmup} iteration for a test. 75 */ 76 public static final int WARMUP_ITERATIONS = Integer.parseInt(System.getProperty("Warmup", "2000")); 77 78 private static final boolean ALLOW_METHOD_NOT_COMPILABLE = Boolean.getBoolean("AllowNotCompilable"); 79 private static final boolean TIERED_COMPILATION = (Boolean)WHITE_BOX.getVMFlag("TieredCompilation"); 80 private static final CompLevel TIERED_COMPILATION_STOP_AT_LEVEL; 81 private static final boolean CLIENT_VM = Platform.isClient(); 82 83 static { 84 CompLevel level = CompLevel.forValue(((Long)WHITE_BOX.getVMFlag("TieredStopAtLevel")).intValue()); 85 if (CLIENT_VM && level == CompLevel.C2) { 86 // No C2 available, use C1 level without profiling. 87 level = CompLevel.C1_SIMPLE; 88 } 89 TIERED_COMPILATION_STOP_AT_LEVEL = level; 90 } 91 public static final boolean TEST_C1 = (TIERED_COMPILATION && TIERED_COMPILATION_STOP_AT_LEVEL.getValue() < CompLevel.C2.getValue()) || CLIENT_VM; 92 93 static final boolean XCOMP = Platform.isComp(); 94 static final boolean VERBOSE = Boolean.getBoolean("Verbose"); 95 private static final boolean PRINT_TIMES = Boolean.getBoolean("PrintTimes"); 96 public static final boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler"); 97 static final boolean EXCLUDE_RANDOM = Boolean.getBoolean("ExcludeRandom"); 98 private static final String TESTLIST = System.getProperty("Test", ""); 99 private static final String EXCLUDELIST = System.getProperty("Exclude", ""); 100 private static final boolean DUMP_REPLAY = Boolean.getBoolean("DumpReplay"); 101 private static final boolean GC_AFTER = Boolean.getBoolean("GCAfter"); 102 private static final boolean SHUFFLE_TESTS = Boolean.parseBoolean(System.getProperty("ShuffleTests", "true")); 103 // Use separate flag as VERIFY_IR could have been set by user but due to other flags it was disabled by flag VM. 104 private static final boolean PRINT_VALID_IR_RULES = Boolean.getBoolean("ShouldDoIRVerification"); 105 protected static final long PER_METHOD_TRAP_LIMIT = (Long)WHITE_BOX.getVMFlag("PerMethodTrapLimit"); 106 protected static final boolean PROFILE_INTERPRETER = (Boolean)WHITE_BOX.getVMFlag("ProfileInterpreter"); 107 protected static final boolean DEOPT_BARRIERS_ALOT = (Boolean)WHITE_BOX.getVMFlag("DeoptimizeNMethodBarriersALot"); 108 private static final boolean FLIP_C1_C2 = Boolean.getBoolean("FlipC1C2"); 109 private static final boolean IGNORE_COMPILER_CONTROLS = Boolean.getBoolean("IgnoreCompilerControls"); 110 111 private final HashMap<Method, DeclaredTest> declaredTests = new HashMap<>(); 112 private final List<AbstractTest> allTests = new ArrayList<>(); 113 private final HashMap<String, Method> testMethodMap = new HashMap<>(); 114 private final HashMap<String, Method> setupMethodMap = new HashMap<>(); 115 private final List<String> excludeList; 116 private final List<String> testList; 117 private Set<Class<?>> helperClasses = null; // Helper classes that contain framework annotations to be processed. 118 private final IREncodingPrinter irMatchRulePrinter; 119 private final Class<?> testClass; 120 private final Map<Executable, CompLevel> forceCompileMap = new HashMap<>(); 121 122 private TestVM(Class<?> testClass) { 123 TestRun.check(testClass != null, "Test class cannot be null"); 124 this.testClass = testClass; 125 this.testList = createTestFilterList(TESTLIST, testClass); 126 this.excludeList = createTestFilterList(EXCLUDELIST, testClass); 127 128 if (PRINT_VALID_IR_RULES) { 129 irMatchRulePrinter = new IREncodingPrinter(); 130 } else { 131 irMatchRulePrinter = null; 132 } 133 } 134 135 /** 136 * Parse "test1,test2,test3" into a list. 137 */ 138 private static List<String> createTestFilterList(String list, Class<?> testClass) { 139 List<String> filterList = null; 140 if (!list.isEmpty()) { 141 String classPrefix = testClass.getSimpleName() + "."; 142 filterList = new ArrayList<>(Arrays.asList(list.split(","))); 143 for (int i = filterList.size() - 1; i >= 0; i--) { 144 String test = filterList.get(i); 145 if (test.indexOf(".") > 0) { 146 if (test.startsWith(classPrefix)) { 147 test = test.substring(classPrefix.length()); 148 filterList.set(i, test); 149 } else { 150 filterList.remove(i); 151 } 152 } 153 } 154 } 155 return filterList; 156 } 157 158 /** 159 * Main entry point of the test VM. 160 */ 161 public static void main(String[] args) { 162 try { 163 String testClassName = args[0]; 164 System.out.println("TestVM main() called - about to run tests in class " + testClassName); 165 Class<?> testClass = getClassObject(testClassName, "test"); 166 167 TestVM framework = new TestVM(testClass); 168 framework.addHelperClasses(args); 169 framework.start(); 170 } finally { 171 TestFrameworkSocket.closeClientSocket(); 172 } 173 } 174 175 protected static Class<?> getClassObject(String className, String classType) { 176 try { 177 return Class.forName(className); 178 } catch (Exception e) { 179 throw new TestRunException("Could not find " + classType + " class", e); 180 } 181 } 182 183 /** 184 * Set up all helper classes and verify they are specified correctly. 185 */ 186 private void addHelperClasses(String[] args) { 187 Class<?>[] helperClassesList = getHelperClasses(args); 188 if (helperClassesList != null) { 189 TestRun.check(Arrays.stream(helperClassesList).noneMatch(Objects::isNull), "A Helper class cannot be null"); 190 this.helperClasses = new HashSet<>(); 191 192 for (Class<?> helperClass : helperClassesList) { 193 if (Arrays.stream(testClass.getDeclaredClasses()).anyMatch(c -> c == helperClass)) { 194 // Nested class of test class is automatically treated as helper class 195 TestFormat.failNoThrow("Nested " + helperClass + " inside test " + testClass + " is implicitly" 196 + " treated as helper class and does not need to be specified as such."); 197 continue; 198 } 199 TestRun.check(!this.helperClasses.contains(helperClass), "Cannot add the same class twice: " + helperClass); 200 this.helperClasses.add(helperClass); 201 } 202 } 203 } 204 205 private static Class<?>[] getHelperClasses(String[] args) { 206 if (args.length == 1) { 207 return null; 208 } 209 Class<?>[] helperClasses = new Class<?>[args.length - 1]; // First argument is test class 210 for (int i = 1; i < args.length; i++) { 211 String helperClassName = args[i]; 212 helperClasses[i - 1] = getClassObject(helperClassName, "helper"); 213 } 214 return helperClasses; 215 } 216 217 private void checkHelperClass(Class<?> clazz) { 218 checkAnnotationsInClass(clazz, "helper"); 219 for (Class<?> c : clazz.getDeclaredClasses()) { 220 checkAnnotationsInClass(c, "nested (and helper)"); 221 } 222 } 223 224 private void checkAnnotationsInClass(Class<?> c, String clazzType) { 225 Method[] methods = c.getDeclaredMethods(); 226 for (Method m : methods) { 227 TestFormat.checkNoThrow(getAnnotation(m, Test.class) == null, 228 "Cannot use @Test annotation in " + clazzType + " " + c + " at " + m); 229 TestFormat.checkNoThrow(getAnnotation(m, Run.class) == null, 230 "Cannot use @Run annotation in " + clazzType + " " + c + " at " + m); 231 TestFormat.checkNoThrow(getAnnotation(m, Check.class) == null, 232 "Cannot use @Check annotation in " + clazzType + " " + c + " at " + m); 233 TestFormat.checkNoThrow(getAnnotation(m, Setup.class) == null, 234 "Cannot use @Setup annotation in " + clazzType + " " + c + " at " + m); 235 } 236 } 237 238 /** 239 * Only called by internal tests testing the framework itself. Accessed by reflection. Not exposed to normal users. 240 */ 241 private static void runTestsOnSameVM(Class<?> testClass) { 242 if (testClass == null) { 243 StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); 244 testClass = walker.getCallerClass(); 245 } 246 TestVM framework = new TestVM(testClass); 247 framework.start(); 248 } 249 250 /** 251 * Once everything is initialized and set up, start collecting tests and executing them afterwards. 252 */ 253 private void start() { 254 setupTests(); 255 checkForcedCompilationsCompleted(); 256 runTests(); 257 } 258 259 private void setupTests() { 260 // TODO remove this once JDK-8273591 is fixed 261 if (!IGNORE_COMPILER_CONTROLS) { 262 for (Class<?> clazz : testClass.getDeclaredClasses()) { 263 checkAnnotationsInClass(clazz, "inner"); 264 } 265 } 266 if (DUMP_REPLAY) { 267 addReplay(); 268 } 269 270 // Collect the @Setup methods so we can reference them 271 // from the test methods 272 collectSetupMethods(); 273 274 // Make sure to first setup test methods and make them non-inlineable and only then process compile commands. 275 setupDeclaredTests(); 276 processControlAnnotations(testClass); 277 processHelperClasses(); 278 setupCheckedAndCustomRunTests(); 279 280 // All remaining tests are simple base tests without check or specific way to run them. 281 addBaseTests(); 282 if (PRINT_VALID_IR_RULES) { 283 irMatchRulePrinter.emit(); 284 VMInfoPrinter.emit(); 285 } 286 TestFormat.throwIfAnyFailures(); 287 declaredTests.clear(); 288 testMethodMap.clear(); 289 } 290 291 private void addBaseTests() { 292 declaredTests.forEach((m, test) -> { 293 if (test.getAttachedMethod() == null) { 294 try { 295 Arguments argumentsAnno = getAnnotation(m, Arguments.class); 296 TestFormat.check(argumentsAnno != null || m.getParameterCount() == 0, "Missing @Arguments annotation to define arguments of " + m); 297 BaseTest baseTest = new BaseTest(test, shouldExcludeTest(m.getName())); 298 allTests.add(baseTest); 299 if (PRINT_VALID_IR_RULES) { 300 irMatchRulePrinter.emitRuleEncoding(m, baseTest.isSkipped()); 301 } 302 } catch (TestFormatException e) { 303 // Failure logged. Continue and report later. 304 } 305 } 306 }); 307 } 308 309 /** 310 * Check if user wants to exclude this test by checking the -DTest and -DExclude lists. 311 */ 312 private boolean shouldExcludeTest(String testName) { 313 boolean hasTestList = testList != null; 314 boolean hasExcludeList = excludeList != null; 315 if (hasTestList) { 316 return !testList.contains(testName) || (hasExcludeList && excludeList.contains(testName)); 317 } else if (hasExcludeList) { 318 return excludeList.contains(testName); 319 } 320 return false; 321 } 322 323 /** 324 * Generate replay compilation files. 325 */ 326 private void addReplay() { 327 String directive = "[{ match: \"*.*\", DumpReplay: true }]"; 328 TestFramework.check(WHITE_BOX.addCompilerDirective(directive) == 1, "Failed to add DUMP_REPLAY directive"); 329 } 330 331 private void processControlAnnotations(Class<?> clazz) { 332 if (IGNORE_COMPILER_CONTROLS) { 333 return; 334 } 335 // Also apply compile commands to all inner classes of 'clazz'. 336 ArrayList<Class<?>> classes = new ArrayList<>(Arrays.asList(clazz.getDeclaredClasses())); 337 classes.add(clazz); 338 for (Class<?> c : classes) { 339 applyClassAnnotations(c); 340 List<Executable> executables = new ArrayList<>(Arrays.asList(c.getDeclaredMethods())); 341 Collections.addAll(executables, c.getDeclaredConstructors()); 342 for (Executable ex : executables) { 343 checkClassAnnotations(ex); 344 try { 345 applyIndependentCompilationCommands(ex); 346 } catch (TestFormatException e) { 347 // Failure logged. Continue and report later. 348 } 349 } 350 351 // Only force compilation now because above annotations affect inlining 352 for (Executable ex : executables) { 353 try { 354 applyForceCompileCommand(ex); 355 } catch (TestFormatException e) { 356 // Failure logged. Continue and report later. 357 } 358 } 359 } 360 } 361 362 private void applyClassAnnotations(Class<?> c) { 363 ForceCompileClassInitializer anno = getAnnotation(c, ForceCompileClassInitializer.class); 364 if (anno == null) { 365 return; 366 } 367 368 // Compile class initializer 369 CompLevel level = anno.value(); 370 if (level == CompLevel.SKIP || level == CompLevel.WAIT_FOR_COMPILATION) { 371 TestFormat.failNoThrow("Cannot define compilation level SKIP or WAIT_FOR_COMPILATION in " + 372 "@ForceCompileClassInitializer at " + c); 373 return; 374 } 375 level = restrictCompLevel(anno.value()); 376 if (level != CompLevel.SKIP) { 377 // Make sure class is initialized to avoid compilation bailout of <clinit> 378 getClassObject(c.getName(), "nested"); // calls Class.forName() to initialize 'c' 379 TestFormat.checkNoThrow(WHITE_BOX.enqueueInitializerForCompilation(c, level.getValue()), 380 "Failed to enqueue <clinit> of " + c + " for compilation. Did you specify " 381 + "@ForceCompileClassInitializer without providing a static class initialization? " 382 + "Make sure to provide any form of static initialization or remove the annotation. " 383 + "For debugging purposes, -DIgnoreCompilerControls=true can be used to temporarly " 384 + "ignore @ForceCompileClassInitializer annotations."); 385 } 386 } 387 388 private void checkClassAnnotations(Executable ex) { 389 TestFormat.checkNoThrow(getAnnotation(ex, ForceCompileClassInitializer.class) == null, 390 "@ForceCompileClassInitializer only allowed at classes but not at method " + ex); 391 } 392 393 /** 394 * Exclude a method from compilation with a compiler randomly. Return the compiler for which the method was made 395 * not compilable. 396 */ 397 public static Compiler excludeRandomly(Executable ex) { 398 Compiler compiler = switch (Utils.getRandomInstance().nextInt() % 3) { 399 case 1 -> Compiler.C1; 400 case 2 -> Compiler.C2; 401 default -> Compiler.ANY; 402 }; 403 WHITE_BOX.makeMethodNotCompilable(ex, compiler.getValue(), false); 404 WHITE_BOX.makeMethodNotCompilable(ex, compiler.getValue(), true); 405 System.out.println("Excluding from " + compiler.name() + " compilation: " + ex); 406 return compiler; 407 } 408 409 private void applyIndependentCompilationCommands(Executable ex) { 410 ForceInline forceInlineAnno = getAnnotation(ex, ForceInline.class); 411 DontInline dontInlineAnno = getAnnotation(ex, DontInline.class); 412 ForceCompile forceCompileAnno = getAnnotation(ex, ForceCompile.class); 413 DontCompile dontCompileAnno = getAnnotation(ex, DontCompile.class); 414 checkCompilationCommandAnnotations(ex, forceInlineAnno, dontInlineAnno, forceCompileAnno, dontCompileAnno); 415 // First handle inline annotations 416 if (dontInlineAnno != null) { 417 WHITE_BOX.testSetDontInlineMethod(ex, true); 418 } else if (forceInlineAnno != null) { 419 WHITE_BOX.testSetForceInlineMethod(ex, true); 420 } 421 if (dontCompileAnno != null) { 422 dontCompileWithCompiler(ex, dontCompileAnno.value()); 423 } 424 if (EXCLUDE_RANDOM && getAnnotation(ex, Test.class) == null && forceCompileAnno == null && dontCompileAnno == null) { 425 // Randomly exclude helper methods from compilation 426 if (Utils.getRandomInstance().nextBoolean()) { 427 excludeRandomly(ex); 428 } 429 } 430 } 431 432 private void checkCompilationCommandAnnotations(Executable ex, ForceInline forceInlineAnno, DontInline dontInlineAnno, ForceCompile forceCompileAnno, DontCompile dontCompileAnno) { 433 Test testAnno = getAnnotation(ex, Test.class); 434 Run runAnno = getAnnotation(ex, Run.class); 435 Check checkAnno = getAnnotation(ex, Check.class); 436 TestFormat.check((testAnno == null && runAnno == null && checkAnno == null) || Stream.of(forceCompileAnno, dontCompileAnno, forceInlineAnno, dontInlineAnno).noneMatch(Objects::nonNull), 437 "Cannot use explicit compile command annotations (@ForceInline, @DontInline, " + 438 "@ForceCompile or @DontCompile) together with @Test, @Check or @Run: " + ex + ". Use compLevel in @Test for fine tuning."); 439 if (Stream.of(forceInlineAnno, dontCompileAnno, dontInlineAnno).filter(Objects::nonNull).count() > 1) { 440 // Failure 441 TestFormat.check(dontCompileAnno == null || dontInlineAnno == null, 442 "@DontInline is implicitely done with @DontCompile annotation at " + ex); 443 TestFormat.fail("Cannot mix @ForceInline, @DontInline and @DontCompile at the same time at " + ex); 444 } 445 TestFormat.check(forceInlineAnno == null || dontInlineAnno == null, "Cannot have @ForceInline and @DontInline at the same time at " + ex); 446 if (forceCompileAnno != null && dontCompileAnno != null) { 447 CompLevel forceCompileLevel = forceCompileAnno.value(); 448 Compiler dontCompileCompiler = dontCompileAnno.value(); 449 TestFormat.check(dontCompileCompiler != Compiler.ANY, 450 "Cannot have @DontCompile(Compiler.ANY) and @ForceCompile at the same time at " + ex); 451 TestFormat.check(forceCompileLevel != CompLevel.ANY, 452 "Cannot have @ForceCompile(CompLevel.ANY) and @DontCompile at the same time at " + ex); 453 TestFormat.check(forceCompileLevel.isNotCompilationLevelOfCompiler(dontCompileCompiler), 454 "Overlapping compilation level and compiler with @ForceCompile and @DontCompile at " + ex); 455 } 456 } 457 458 /** 459 * Exlude the method from compilation and make sure it is not inlined. 460 */ 461 private void dontCompileAndDontInlineMethod(Method m) { 462 if (!IGNORE_COMPILER_CONTROLS) { 463 WHITE_BOX.makeMethodNotCompilable(m, CompLevel.ANY.getValue(), true); 464 WHITE_BOX.makeMethodNotCompilable(m, CompLevel.ANY.getValue(), false); 465 WHITE_BOX.testSetDontInlineMethod(m, true); 466 } 467 } 468 469 private void dontCompileWithCompiler(Executable ex, Compiler compiler) { 470 if (VERBOSE) { 471 System.out.println("dontCompileWithCompiler " + ex + " , compiler = " + compiler.name()); 472 } 473 WHITE_BOX.makeMethodNotCompilable(ex, compiler.getValue(), true); 474 WHITE_BOX.makeMethodNotCompilable(ex, compiler.getValue(), false); 475 if (compiler == Compiler.ANY) { 476 WHITE_BOX.testSetDontInlineMethod(ex, true); 477 } 478 } 479 480 private void applyForceCompileCommand(Executable ex) { 481 ForceCompile forceCompileAnno = getAnnotation(ex, ForceCompile.class); 482 if (forceCompileAnno != null) { 483 CompLevel compLevel = forceCompileAnno.value(); 484 TestFormat.check(compLevel != CompLevel.SKIP && compLevel != CompLevel.WAIT_FOR_COMPILATION, 485 "Cannot define compilation level SKIP or WAIT_FOR_COMPILATION in @ForceCompile at " + ex); 486 compLevel = restrictCompLevel(forceCompileAnno.value()); 487 if (FLIP_C1_C2) { 488 compLevel = compLevel.flipCompLevel(); 489 compLevel = restrictCompLevel(compLevel.flipCompLevel()); 490 } 491 if (EXCLUDE_RANDOM) { 492 compLevel = compLevel.excludeCompilationRandomly(ex); 493 } 494 if (compLevel != CompLevel.SKIP) { 495 enqueueForCompilation(ex, compLevel); 496 forceCompileMap.put(ex, compLevel); 497 } 498 } 499 } 500 501 static void enqueueForCompilation(Executable ex, CompLevel requestedCompLevel) { 502 if (TestVM.VERBOSE) { 503 System.out.println("enqueueForCompilation " + ex + ", level = " + requestedCompLevel); 504 } 505 CompLevel compLevel = restrictCompLevel(requestedCompLevel); 506 if (compLevel != CompLevel.SKIP) { 507 WHITE_BOX.enqueueMethodForCompilation(ex, compLevel.getValue()); 508 } else { 509 System.out.println("Skipped compilation on level " + requestedCompLevel + " due to VM flags not allowing it."); 510 } 511 } 512 513 514 /** 515 * Collect all @Setup annotated methods and add them to setupMethodMap, for convenience to reference later from 516 * tests with @Arguments(setup = "setupMethodName"). 517 */ 518 private void collectSetupMethods() { 519 for (Method m : testClass.getDeclaredMethods()) { 520 Setup setupAnnotation = getAnnotation(m, Setup.class); 521 if (setupAnnotation != null) { 522 addSetupMethod(m); 523 } 524 } 525 } 526 527 private void addSetupMethod(Method m) { 528 TestFormat.checkNoThrow(getAnnotation(m, Test.class) == null, 529 "@Setup method cannot have @Test annotation: " + m); 530 TestFormat.checkNoThrow(getAnnotation(m, Check.class) == null, 531 "@Setup method cannot have @Check annotation: " + m); 532 TestFormat.checkNoThrow(getAnnotation(m, Arguments.class) == null, 533 "@Setup method cannot have @Arguments annotation: " + m); 534 TestFormat.checkNoThrow(getAnnotation(m, Run.class) == null, 535 "@Setup method cannot have @Run annotation: " + m); 536 Method mOverloaded = setupMethodMap.put(m.getName(), m); 537 TestFormat.checkNoThrow(mOverloaded == null, 538 "@Setup method cannot be overloaded: " + mOverloaded + " with " + m); 539 m.setAccessible(true); 540 } 541 542 /** 543 * Setup @Test annotated method an add them to the declaredTests map to have a convenient way of accessing them 544 * once setting up a framework test (base checked, or custom run test). 545 */ 546 private void setupDeclaredTests() { 547 for (Method m : testClass.getDeclaredMethods()) { 548 Test testAnno = getAnnotation(m, Test.class); 549 try { 550 if (testAnno != null) { 551 addDeclaredTest(m); 552 } else { 553 TestFormat.checkNoThrow(!m.isAnnotationPresent(IR.class) && !m.isAnnotationPresent(IRs.class), 554 "Found @IR annotation on non-@Test method " + m); 555 TestFormat.checkNoThrow(!m.isAnnotationPresent(Warmup.class) || getAnnotation(m, Run.class) != null, 556 "Found @Warmup annotation on non-@Test or non-@Run method " + m); 557 } 558 } catch (TestFormatException e) { 559 // Failure logged. Continue and report later. 560 } 561 } 562 TestFormat.checkNoThrow(!declaredTests.isEmpty(), "Did not specify any @Test methods in " + testClass); 563 } 564 565 private void addDeclaredTest(Method m) { 566 Test testAnno = getAnnotation(m, Test.class); 567 checkTestAnnotations(m, testAnno); 568 Warmup warmup = getAnnotation(m, Warmup.class); 569 int warmupIterations = WARMUP_ITERATIONS; 570 if (warmup != null) { 571 warmupIterations = warmup.value(); 572 TestFormat.checkNoThrow(warmupIterations >= 0, "Cannot have negative value for @Warmup at " + m); 573 } 574 575 if (!IGNORE_COMPILER_CONTROLS) { 576 // Don't inline test methods by default. Do not apply this when -DIgnoreCompilerControls=true is set. 577 WHITE_BOX.testSetDontInlineMethod(m, true); 578 } 579 CompLevel compLevel = restrictCompLevel(testAnno.compLevel()); 580 if (FLIP_C1_C2) { 581 compLevel = compLevel.flipCompLevel(); 582 compLevel = restrictCompLevel(compLevel.flipCompLevel()); 583 } 584 if (EXCLUDE_RANDOM) { 585 compLevel = compLevel.excludeCompilationRandomly(m); 586 } 587 boolean allowNotCompilable = testAnno.allowNotCompilable() || ALLOW_METHOD_NOT_COMPILABLE; 588 ArgumentsProvider argumentsProvider = ArgumentsProviderBuilder.build(m, setupMethodMap); 589 DeclaredTest test = new DeclaredTest(m, argumentsProvider, compLevel, warmupIterations, allowNotCompilable); 590 declaredTests.put(m, test); 591 testMethodMap.put(m.getName(), m); 592 } 593 594 private void checkTestAnnotations(Method m, Test testAnno) { 595 TestFormat.check(!testMethodMap.containsKey(m.getName()), 596 "Cannot overload two @Test methods: " + m + ", " + testMethodMap.get(m.getName())); 597 TestFormat.check(testAnno != null, m + " must be a method with a @Test annotation"); 598 599 Check checkAnno = getAnnotation(m, Check.class); 600 Run runAnno = getAnnotation(m, Run.class); 601 TestFormat.check(checkAnno == null && runAnno == null, 602 m + " has invalid @Check or @Run annotation while @Test annotation is present."); 603 604 TestFormat.checkNoThrow(Arrays.stream(m.getParameterTypes()).noneMatch(AbstractInfo.class::isAssignableFrom), 605 "Cannot " + AbstractInfo.class + " or any of its subclasses as parameter type at " + 606 "@Test method " + m); 607 608 TestFormat.checkNoThrow(!AbstractInfo.class.isAssignableFrom(m.getReturnType()), 609 "Cannot " + AbstractInfo.class + " or any of its subclasses as return type at " + 610 "@Test method " + m); 611 } 612 613 614 /** 615 * Get the appropriate level as permitted by the test scenario and VM flags. 616 */ 617 private static CompLevel restrictCompLevel(CompLevel compLevel) { 618 if (!USE_COMPILER) { 619 return CompLevel.SKIP; 620 } 621 if (compLevel == CompLevel.ANY) { 622 // Use highest available compilation level by default (usually C2). 623 compLevel = TIERED_COMPILATION_STOP_AT_LEVEL; 624 } 625 if (TEST_C1 && compLevel == CompLevel.C2) { 626 return CompLevel.SKIP; 627 } 628 if ((!TIERED_COMPILATION && !CLIENT_VM) && compLevel.getValue() < CompLevel.C2.getValue()) { 629 return CompLevel.SKIP; 630 } 631 if ((TIERED_COMPILATION || CLIENT_VM) && compLevel.getValue() > TIERED_COMPILATION_STOP_AT_LEVEL.getValue()) { 632 return CompLevel.SKIP; 633 } 634 return compLevel; 635 } 636 637 /** 638 * Verify that the helper classes do not contain illegal framework annotations and then apply the actions as 639 * specified by the different helper class annotations. 640 */ 641 private void processHelperClasses() { 642 if (helperClasses != null) { 643 for (Class<?> helperClass : helperClasses) { 644 // Process the helper classes and apply the explicit compile commands 645 TestFormat.checkNoThrow(helperClass != testClass, 646 "Cannot specify test " + testClass + " as helper class, too."); 647 checkHelperClass(helperClass); 648 processControlAnnotations(helperClass); 649 } 650 } 651 } 652 653 /** 654 * First set up checked (with @Check) and custom run tests (with @Run). All remaining unmatched/unused @Test methods 655 * are treated as base tests and set up as such later. 656 */ 657 private void setupCheckedAndCustomRunTests() { 658 for (Method m : testClass.getDeclaredMethods()) { 659 Check checkAnno = getAnnotation(m, Check.class); 660 Run runAnno = getAnnotation(m, Run.class); 661 Arguments argumentsAnno = getAnnotation(m, Arguments.class); 662 try { 663 TestFormat.check(argumentsAnno == null || (checkAnno == null && runAnno == null), 664 "Cannot have @Argument annotation in combination with @Run or @Check at " + m); 665 if (checkAnno != null) { 666 addCheckedTest(m, checkAnno, runAnno); 667 } else if (runAnno != null) { 668 addCustomRunTest(m, runAnno); 669 } 670 } catch (TestFormatException e) { 671 // Failure logged. Continue and report later. 672 } 673 } 674 } 675 676 /** 677 * Set up a checked test by first verifying the correct format of the @Test and @Check method and then adding it 678 * to the allTests list which keeps track of all framework tests that are eventually executed. 679 */ 680 private void addCheckedTest(Method m, Check checkAnno, Run runAnno) { 681 Method testMethod = testMethodMap.get(checkAnno.test()); 682 DeclaredTest test = declaredTests.get(testMethod); 683 checkCheckedTest(m, checkAnno, runAnno, testMethod, test); 684 test.setAttachedMethod(m); 685 TestFormat.check(getAnnotation(testMethod, Arguments.class) != null || testMethod.getParameterCount() == 0, 686 "Missing @Arguments annotation to define arguments of " + testMethod + " required by " 687 + "checked test " + m); 688 CheckedTest.Parameter parameter = getCheckedTestParameter(m, testMethod); 689 dontCompileAndDontInlineMethod(m); 690 CheckedTest checkedTest = new CheckedTest(test, m, checkAnno, parameter, shouldExcludeTest(testMethod.getName())); 691 allTests.add(checkedTest); 692 if (PRINT_VALID_IR_RULES) { 693 // Only need to emit IR verification information if IR verification is actually performed. 694 irMatchRulePrinter.emitRuleEncoding(testMethod, checkedTest.isSkipped()); 695 } 696 } 697 698 private void checkCheckedTest(Method m, Check checkAnno, Run runAnno, Method testMethod, DeclaredTest test) { 699 TestFormat.check(runAnno == null, m + " has invalid @Run annotation while @Check annotation is present."); 700 TestFormat.check(testMethod != null, "Did not find associated test method \"" + m.getDeclaringClass().getName() 701 + "." + checkAnno.test() + "\" for @Check at " + m); 702 TestFormat.check(test != null, "Missing @Test annotation for associated test method " + testMethod + " for @Check at " + m); 703 Method attachedMethod = test.getAttachedMethod(); 704 TestFormat.check(attachedMethod == null, 705 "Cannot use @Test " + testMethod + " for more than one @Run or one @Check method. Found: " + m + ", " + attachedMethod); 706 } 707 708 /** 709 * Only allow parameters as specified in {@link Check}. 710 */ 711 private CheckedTest.Parameter getCheckedTestParameter(Method m, Method testMethod) { 712 boolean firstParameterTestInfo = m.getParameterCount() > 0 && m.getParameterTypes()[0].equals(TestInfo.class); 713 boolean secondParameterTestInfo = m.getParameterCount() > 1 && m.getParameterTypes()[1].equals(TestInfo.class); 714 CheckedTest.Parameter parameter = null; 715 Class<?> testReturnType = testMethod.getReturnType(); 716 switch (m.getParameterCount()) { 717 case 0 -> parameter = CheckedTest.Parameter.NONE; 718 case 1 -> { 719 TestFormat.checkNoThrow(firstParameterTestInfo || m.getParameterTypes()[0] == testReturnType, 720 "Single-parameter version of @Check method " + m + " must match return type of @Test " + testMethod); 721 parameter = firstParameterTestInfo ? CheckedTest.Parameter.TEST_INFO_ONLY : CheckedTest.Parameter.RETURN_ONLY; 722 } 723 case 2 -> { 724 TestFormat.checkNoThrow(m.getParameterTypes()[0] == testReturnType && secondParameterTestInfo, 725 "Two-parameter version of @Check method " + m + " must provide as first parameter the same" 726 + " return type as @Test method " + testMethod + " and as second parameter an object of " + TestInfo.class); 727 parameter = CheckedTest.Parameter.BOTH; 728 } 729 default -> TestFormat.failNoThrow("@Check method " + m + " must provide either a none, single or two-parameter variant."); 730 } 731 return parameter; 732 } 733 734 /** 735 * Set up a custom run test by first verifying the correct format of the @Test and @Run method and then adding it 736 * to the allTests list which keeps track of all framework tests that are eventually executed. 737 */ 738 private void addCustomRunTest(Method m, Run runAnno) { 739 checkRunMethod(m, runAnno); 740 List<DeclaredTest> tests = new ArrayList<>(); 741 boolean shouldExcludeTest = true; 742 for (String testName : runAnno.test()) { 743 try { 744 Method testMethod = testMethodMap.get(testName); 745 DeclaredTest test = declaredTests.get(testMethod); 746 checkCustomRunTest(m, testName, testMethod, test, runAnno.mode()); 747 test.setAttachedMethod(m); 748 tests.add(test); 749 // Only exclude custom run test if all test methods excluded 750 shouldExcludeTest &= shouldExcludeTest(testMethod.getName()); 751 } catch (TestFormatException e) { 752 // Logged, continue. 753 } 754 } 755 if (tests.isEmpty()) { 756 return; // There was a format violation. Return. 757 } 758 dontCompileAndDontInlineMethod(m); 759 CustomRunTest customRunTest = new CustomRunTest(m, getAnnotation(m, Warmup.class), runAnno, tests, shouldExcludeTest); 760 allTests.add(customRunTest); 761 if (PRINT_VALID_IR_RULES) { 762 tests.forEach(test -> irMatchRulePrinter.emitRuleEncoding(test.getTestMethod(), customRunTest.isSkipped())); 763 } 764 } 765 766 /** 767 * Only allow parameters as specified in {@link Run}. 768 */ 769 private void checkCustomRunTest(Method m, String testName, Method testMethod, DeclaredTest test, RunMode runMode) { 770 TestFormat.check(testMethod != null, "Did not find associated @Test method \"" + m.getDeclaringClass().getName() 771 + "." + testName + "\" specified in @Run at " + m); 772 TestFormat.check(test != null, 773 "Missing @Test annotation for associated test method " + testName + " for @Run at " + m); 774 Method attachedMethod = test.getAttachedMethod(); 775 TestFormat.check(attachedMethod == null, 776 "Cannot use @Test " + testMethod + " for more than one @Run/@Check method. Found: " 777 + m + ", " + attachedMethod); 778 Arguments argumentsAnno = getAnnotation(testMethod, Arguments.class); 779 TestFormat.check(argumentsAnno == null, 780 "Cannot use @Arguments at test method " + testMethod + " in combination with @Run method " + m); 781 Warmup warmupAnno = getAnnotation(testMethod, Warmup.class); 782 TestFormat.checkNoThrow(warmupAnno == null, 783 "Cannot set @Warmup at @Test method " + testMethod + " when used with its @Run method " 784 + m + ". Use @Warmup at @Run method instead."); 785 Test testAnno = getAnnotation(testMethod, Test.class); 786 TestFormat.checkNoThrow(runMode != RunMode.STANDALONE || testAnno.compLevel() == CompLevel.ANY, 787 "Setting explicit compilation level for @Test method " + testMethod + " has no effect " 788 + "when used with STANDALONE @Run method " + m); 789 } 790 791 private void checkRunMethod(Method m, Run runAnno) { 792 TestFormat.check(runAnno.test().length > 0, "@Run method " + m + " must specify at least one test method"); 793 TestFormat.checkNoThrow(m.getParameterCount() == 0 || (m.getParameterCount() == 1 && m.getParameterTypes()[0].equals(RunInfo.class)), 794 "@Run method " + m + " must specify either no parameter or exactly one " + RunInfo.class + " parameter."); 795 Warmup warmupAnno = getAnnotation(m, Warmup.class); 796 TestFormat.checkNoThrow(warmupAnno == null || runAnno.mode() != RunMode.STANDALONE, 797 "Cannot set @Warmup at @Run method " + m + " when used with RunMode.STANDALONE. The @Run method is only invoked once."); 798 } 799 800 private static <T extends Annotation> T getAnnotation(AnnotatedElement element, Class<T> c) { 801 T[] annos = element.getAnnotationsByType(c); 802 TestFormat.check(annos.length < 2, element + " has duplicated annotations"); 803 return Arrays.stream(annos).findFirst().orElse(null); 804 } 805 806 /** 807 * Ensure that all compilations that were enforced (added to compilation queue) by framework annotations are 808 * completed. Wait if necessary for a short amount of time for their completion. 809 */ 810 private void checkForcedCompilationsCompleted() { 811 if (forceCompileMap.isEmpty()) { 812 return; 813 } 814 final long started = System.currentTimeMillis(); 815 long elapsed; 816 do { 817 forceCompileMap.entrySet().removeIf(entry -> WHITE_BOX.getMethodCompilationLevel(entry.getKey()) == entry.getValue().getValue()); 818 if (forceCompileMap.isEmpty()) { 819 // All @ForceCompile methods are compiled at the requested level. 820 return; 821 } 822 // Retry again if not yet compiled. 823 forceCompileMap.forEach(TestVM::enqueueForCompilation); 824 elapsed = System.currentTimeMillis() - started; 825 } while (elapsed < 5000); 826 StringBuilder builder = new StringBuilder(); 827 forceCompileMap.forEach((key, value) -> builder.append("- ").append(key).append(" at CompLevel.").append(value) 828 .append(System.lineSeparator())); 829 throw new TestRunException("Could not force compile the following @ForceCompile methods:" 830 + System.lineSeparator() + builder.toString()); 831 } 832 833 /** 834 * Once all framework tests are collected, they are run in this method. 835 */ 836 private void runTests() { 837 TreeMap<Long, String> durations = (PRINT_TIMES || VERBOSE) ? new TreeMap<>() : null; 838 long startTime = System.nanoTime(); 839 List<AbstractTest> testList; 840 boolean testFilterPresent = testFilterPresent(); 841 if (testFilterPresent) { 842 // Only run the specified tests by the user filters -DTest and/or -DExclude. 843 testList = allTests.stream().filter(test -> !test.isSkipped()).collect(Collectors.toList()); 844 if (testList.isEmpty()) { 845 // Throw an exception to inform the user about an empty specified test set with -DTest and/or -DExclude 846 throw new NoTestsRunException(); 847 } 848 } else { 849 testList = allTests; 850 } 851 852 if (SHUFFLE_TESTS) { 853 // Execute tests in random order (execution sequence affects profiling). This is done by default. 854 Collections.shuffle(testList, Utils.getRandomInstance()); 855 } 856 StringBuilder builder = new StringBuilder(); 857 int failures = 0; 858 859 // Execute all tests and keep track of each exception that is thrown. These are then reported once all tests 860 // are executing. This prevents a premature exit without running all tests. 861 for (AbstractTest test : testList) { 862 if (VERBOSE) { 863 System.out.println("Run " + test.toString()); 864 } 865 if (testFilterPresent) { 866 TestFrameworkSocket.write("Run " + test.toString(), TestFrameworkSocket.TESTLIST_TAG, true); 867 } 868 try { 869 test.run(); 870 } catch (TestRunException e) { 871 StringWriter sw = new StringWriter(); 872 PrintWriter pw = new PrintWriter(sw); 873 e.printStackTrace(pw); 874 builder.append(test.toString()).append(":").append(System.lineSeparator()).append(sw.toString()) 875 .append(System.lineSeparator()).append(System.lineSeparator()); 876 failures++; 877 } 878 if (PRINT_TIMES || VERBOSE) { 879 long endTime = System.nanoTime(); 880 long duration = (endTime - startTime); 881 durations.put(duration, test.getName()); 882 if (VERBOSE) { 883 System.out.println("Done " + test.getName() + ": " + duration + " ns = " + (duration / 1000000) + " ms"); 884 } 885 } 886 if (GC_AFTER) { 887 System.out.println("doing GC"); 888 WHITE_BOX.fullGC(); 889 } 890 } 891 892 // Print execution times 893 if (VERBOSE || PRINT_TIMES) { 894 TestFrameworkSocket.write("Test execution times:", PRINT_TIMES_TAG, true); 895 for (Map.Entry<Long, String> entry : durations.entrySet()) { 896 TestFrameworkSocket.write(String.format("%-25s%15d ns%n", entry.getValue() + ":", entry.getKey()), 897 PRINT_TIMES_TAG, true); 898 } 899 } 900 901 if (failures > 0) { 902 // Finally, report all occurred exceptions in a nice format. 903 String msg = System.lineSeparator() + System.lineSeparator() + "Test Failures (" + failures + ")" 904 + System.lineSeparator() + "----------------" + "-".repeat(String.valueOf(failures).length()); 905 throw new TestRunException(msg + System.lineSeparator() + builder.toString()); 906 } 907 } 908 909 private boolean testFilterPresent() { 910 return testList != null || excludeList != null; 911 } 912 913 enum TriState { 914 Maybe, 915 Yes, 916 No 917 } 918 919 public static void compile(Method m, CompLevel compLevel) { 920 TestRun.check(compLevel != CompLevel.SKIP && compLevel != CompLevel.WAIT_FOR_COMPILATION, 921 "Invalid compilation request with level " + compLevel); 922 enqueueForCompilation(m, compLevel); 923 } 924 925 public static void deoptimize(Method m) { 926 WHITE_BOX.deoptimizeMethod(m); 927 } 928 929 public static boolean isCompiled(Method m) { 930 return compiledAtLevel(m, CompLevel.ANY) == TriState.Yes; 931 } 932 933 public static boolean isC1Compiled(Method m) { 934 return compiledByC1(m) == TriState.Yes; 935 } 936 937 public static boolean isC2Compiled(Method m) { 938 return compiledByC2(m) == TriState.Yes; 939 } 940 941 public static boolean isCompiledAtLevel(Method m, CompLevel compLevel) { 942 return compiledAtLevel(m, compLevel) == TriState.Yes; 943 } 944 945 public static void assertDeoptimizedByC1(Method m) { 946 if (isStableDeopt(m, CompLevel.C1_SIMPLE)) { 947 TestRun.check(compiledByC1(m) != TriState.Yes, m + " should have been deoptimized by C1"); 948 } 949 } 950 951 public static void assertDeoptimizedByC2(Method m) { 952 if (isStableDeopt(m, CompLevel.C2)) { 953 TestRun.check(compiledByC2(m) != TriState.Yes, m + " should have been deoptimized by C2"); 954 } 955 } 956 957 /** 958 * Some VM flags could make the deopt assertions unstable. 959 */ 960 public static boolean isStableDeopt(Method m, CompLevel level) { 961 return (USE_COMPILER && !XCOMP && !IGNORE_COMPILER_CONTROLS && !TEST_C1 && 962 PER_METHOD_TRAP_LIMIT != 0 && PROFILE_INTERPRETER && !DEOPT_BARRIERS_ALOT && 963 (!EXCLUDE_RANDOM || WHITE_BOX.isMethodCompilable(m, level.getValue(), false))); 964 } 965 966 public static void assertCompiledByC1(Method m) { 967 TestRun.check(compiledByC1(m) != TriState.No, m + " should have been C1 compiled"); 968 } 969 970 public static void assertCompiledByC2(Method m) { 971 TestRun.check(compiledByC2(m) != TriState.No, m + " should have been C2 compiled"); 972 } 973 974 public static void assertCompiledAtLevel(Method m, CompLevel level) { 975 TestRun.check(compiledAtLevel(m, level) != TriState.No, m + " should have been compiled at level " + level.name()); 976 } 977 978 public static void assertNotCompiled(Method m) { 979 TestRun.check(!isC1Compiled(m), m + " should not have been compiled by C1"); 980 TestRun.check(!isC2Compiled(m), m + " should not have been compiled by C2"); 981 } 982 983 public static void assertCompiled(Method m) { 984 TestRun.check(compiledByC1(m) != TriState.No || compiledByC2(m) != TriState.No, m + " should have been compiled"); 985 } 986 987 private static TriState compiledByC1(Method m) { 988 TriState triState = compiledAtLevel(m, CompLevel.C1_SIMPLE); 989 if (triState != TriState.No) { 990 return triState; 991 } 992 triState = compiledAtLevel(m, CompLevel.C1_LIMITED_PROFILE); 993 if (triState != TriState.No) { 994 return triState; 995 } 996 triState = compiledAtLevel(m, CompLevel.C1_FULL_PROFILE); 997 return triState; 998 } 999 1000 private static TriState compiledByC2(Method m) { 1001 return compiledAtLevel(m, CompLevel.C2); 1002 } 1003 1004 private static TriState compiledAtLevel(Method m, CompLevel level) { 1005 if (WHITE_BOX.isMethodCompiled(m, false)) { 1006 switch (level) { 1007 case C1_SIMPLE, C1_LIMITED_PROFILE, C1_FULL_PROFILE, C2 -> { 1008 if (WHITE_BOX.getMethodCompilationLevel(m, false) == level.getValue()) { 1009 return TriState.Yes; 1010 } 1011 } 1012 case ANY -> { 1013 return TriState.Yes; 1014 } 1015 default -> throw new TestRunException("compiledAtLevel() should not be called with " + level); 1016 } 1017 } 1018 if (!USE_COMPILER || XCOMP || TEST_C1 || IGNORE_COMPILER_CONTROLS || FLIP_C1_C2 || DEOPT_BARRIERS_ALOT || 1019 (EXCLUDE_RANDOM && !WHITE_BOX.isMethodCompilable(m, level.getValue(), false))) { 1020 return TriState.Maybe; 1021 } 1022 return TriState.No; 1023 } 1024 }