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