1 /* 2 * Copyright (c) 2013, 2014, 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 import sun.hotspot.WhiteBox; 25 import sun.hotspot.code.NMethod; 26 27 import java.lang.reflect.Constructor; 28 import java.lang.reflect.Executable; 29 import java.lang.reflect.Method; 30 import java.util.Objects; 31 import java.util.concurrent.Callable; 32 import java.util.function.Function; 33 34 /** 35 * Abstract class for WhiteBox testing of JIT. 36 * 37 * @author igor.ignatyev@oracle.com 38 */ 39 public abstract class CompilerWhiteBoxTest { 40 /** {@code CompLevel::CompLevel_none} -- Interpreter */ 41 protected static int COMP_LEVEL_NONE = 0; 42 /** {@code CompLevel::CompLevel_any}, {@code CompLevel::CompLevel_all} */ 43 protected static int COMP_LEVEL_ANY = -1; 44 /** {@code CompLevel::CompLevel_simple} -- C1 */ 45 protected static int COMP_LEVEL_SIMPLE = 1; 46 /** {@code CompLevel::CompLevel_limited_profile} -- C1, invocation & backedge counters */ 47 protected static int COMP_LEVEL_LIMITED_PROFILE = 2; 48 /** {@code CompLevel::CompLevel_full_profile} -- C1, invocation & backedge counters + mdo */ 49 protected static int COMP_LEVEL_FULL_PROFILE = 3; 50 /** {@code CompLevel::CompLevel_full_optimization} -- C2 or Shark */ 51 protected static int COMP_LEVEL_FULL_OPTIMIZATION = 4; 52 /** Maximal value for CompLevel */ 53 protected static int COMP_LEVEL_MAX = COMP_LEVEL_FULL_OPTIMIZATION; 54 55 /** Instance of WhiteBox */ 56 protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); 57 /** Value of {@code -XX:CompileThreshold} */ 58 protected static final int COMPILE_THRESHOLD 59 = Integer.parseInt(getVMOption("CompileThreshold", "10000")); 60 /** Value of {@code -XX:BackgroundCompilation} */ 61 protected static final boolean BACKGROUND_COMPILATION 62 = Boolean.valueOf(getVMOption("BackgroundCompilation", "true")); 63 /** Value of {@code -XX:TieredCompilation} */ 64 protected static final boolean TIERED_COMPILATION 65 = Boolean.valueOf(getVMOption("TieredCompilation", "false")); 66 /** Value of {@code -XX:TieredStopAtLevel} */ 67 protected static final int TIERED_STOP_AT_LEVEL 68 = Integer.parseInt(getVMOption("TieredStopAtLevel", "0")); 69 /** Flag for verbose output, true if {@code -Dverbose} specified */ 70 protected static final boolean IS_VERBOSE 71 = System.getProperty("verbose") != null; 72 /** invocation count to trigger compilation */ 73 protected static final int THRESHOLD; 74 /** invocation count to trigger OSR compilation */ 75 protected static final long BACKEDGE_THRESHOLD; 76 /** invocation count to warm up method before triggering OSR compilation */ 77 protected static final long OSR_WARMUP = 2000; 78 /** Value of {@code java.vm.info} (interpreted|mixed|comp mode) */ 79 protected static final String MODE = System.getProperty("java.vm.info"); 80 81 static { 82 if (TIERED_COMPILATION) { 83 BACKEDGE_THRESHOLD = THRESHOLD = 150000; 84 } else { 85 THRESHOLD = COMPILE_THRESHOLD; 86 BACKEDGE_THRESHOLD = COMPILE_THRESHOLD * Long.parseLong(getVMOption( 87 "OnStackReplacePercentage")); 88 } 89 } 90 91 /** 92 * Returns value of VM option. 93 * 94 * @param name option's name 95 * @return value of option or {@code null}, if option doesn't exist 96 * @throws NullPointerException if name is null 97 */ 98 protected static String getVMOption(String name) { 99 Objects.requireNonNull(name); 100 return Objects.toString(WHITE_BOX.getVMFlag(name), null); 101 } 102 103 /** 104 * Returns value of VM option or default value. 105 * 106 * @param name option's name 107 * @param defaultValue default value 108 * @return value of option or {@code defaultValue}, if option doesn't exist 109 * @throws NullPointerException if name is null 110 * @see #getVMOption(String) 111 */ 112 protected static String getVMOption(String name, String defaultValue) { 113 String result = getVMOption(name); 114 return result == null ? defaultValue : result; 115 } 116 117 /** copy of is_c1_compile(int) from utilities/globalDefinitions.hpp */ 118 protected static boolean isC1Compile(int compLevel) { 119 return (compLevel > COMP_LEVEL_NONE) 120 && (compLevel < COMP_LEVEL_FULL_OPTIMIZATION); 121 } 122 123 /** copy of is_c2_compile(int) from utilities/globalDefinitions.hpp */ 124 protected static boolean isC2Compile(int compLevel) { 125 return compLevel == COMP_LEVEL_FULL_OPTIMIZATION; 126 } 127 128 protected static void main( 129 Function<TestCase, CompilerWhiteBoxTest> constructor, 130 String[] args) { 131 if (args.length == 0) { 132 for (TestCase test : SimpleTestCase.values()) { 133 constructor.apply(test).runTest(); 134 } 135 } else { 136 for (String name : args) { 137 constructor.apply(SimpleTestCase.valueOf(name)).runTest(); 138 } 139 } 140 } 141 142 /** tested method */ 143 protected final Executable method; 144 protected final TestCase testCase; 145 146 /** 147 * Constructor. 148 * 149 * @param testCase object, that contains tested method and way to invoke it. 150 */ 151 protected CompilerWhiteBoxTest(TestCase testCase) { 152 Objects.requireNonNull(testCase); 153 System.out.println("TEST CASE:" + testCase.name()); 154 method = testCase.getExecutable(); 155 this.testCase = testCase; 156 } 157 158 /** 159 * Template method for testing. Prints tested method's info before 160 * {@linkplain #test()} and after {@linkplain #test()} or on thrown 161 * exception. 162 * 163 * @throws RuntimeException if method {@linkplain #test()} throws any 164 * exception 165 * @see #test() 166 */ 167 protected final void runTest() { 168 if (CompilerWhiteBoxTest.MODE.startsWith("interpreted ")) { 169 System.err.println( 170 "Warning: test is not applicable in interpreted mode"); 171 return; 172 } 173 System.out.println("at test's start:"); 174 printInfo(); 175 try { 176 test(); 177 } catch (Exception e) { 178 System.out.printf("on exception '%s':", e.getMessage()); 179 printInfo(); 180 e.printStackTrace(); 181 if (e instanceof RuntimeException) { 182 throw (RuntimeException) e; 183 } 184 throw new RuntimeException(e); 185 } 186 System.out.println("at test's end:"); 187 printInfo(); 188 } 189 190 /** 191 * Checks, that {@linkplain #method} is not compiled at the given compilation 192 * level or above. 193 * 194 * @param compLevel 195 * 196 * @throws RuntimeException if {@linkplain #method} is in compiler queue or 197 * is compiled, or if {@linkplain #method} has zero 198 * compilation level. 199 */ 200 201 protected final void checkNotCompiled(int compLevel) { 202 if (WHITE_BOX.isMethodQueuedForCompilation(method)) { 203 throw new RuntimeException(method + " must not be in queue"); 204 } 205 if (WHITE_BOX.getMethodCompilationLevel(method, false) >= compLevel) { 206 throw new RuntimeException(method + " comp_level must be >= maxCompLevel"); 207 } 208 if (WHITE_BOX.getMethodCompilationLevel(method, true) >= compLevel) { 209 throw new RuntimeException(method + " osr_comp_level must be >= maxCompLevel"); 210 } 211 } 212 213 /** 214 * Checks, that {@linkplain #method} is not compiled. 215 * 216 * @throws RuntimeException if {@linkplain #method} is in compiler queue or 217 * is compiled, or if {@linkplain #method} has zero 218 * compilation level. 219 */ 220 protected final void checkNotCompiled() { 221 if (WHITE_BOX.isMethodCompiled(method, false)) { 222 throw new RuntimeException(method + " must be not compiled"); 223 } 224 if (WHITE_BOX.getMethodCompilationLevel(method, false) != 0) { 225 throw new RuntimeException(method + " comp_level must be == 0"); 226 } 227 checkNotOsrCompiled(); 228 } 229 230 protected final void checkNotOsrCompiled() { 231 if (WHITE_BOX.isMethodQueuedForCompilation(method)) { 232 throw new RuntimeException(method + " must not be in queue"); 233 } 234 if (WHITE_BOX.isMethodCompiled(method, true)) { 235 throw new RuntimeException(method + " must be not osr_compiled"); 236 } 237 if (WHITE_BOX.getMethodCompilationLevel(method, true) != 0) { 238 throw new RuntimeException(method + " osr_comp_level must be == 0"); 239 } 240 } 241 242 /** 243 * Checks, that {@linkplain #method} is compiled. 244 * 245 * @throws RuntimeException if {@linkplain #method} isn't in compiler queue 246 * and isn't compiled, or if {@linkplain #method} 247 * has nonzero compilation level 248 */ 249 protected final void checkCompiled() { 250 final long start = System.currentTimeMillis(); 251 waitBackgroundCompilation(); 252 if (WHITE_BOX.isMethodQueuedForCompilation(method)) { 253 System.err.printf("Warning: %s is still in queue after %dms%n", 254 method, System.currentTimeMillis() - start); 255 return; 256 } 257 if (!WHITE_BOX.isMethodCompiled(method, testCase.isOsr())) { 258 throw new RuntimeException(method + " must be " 259 + (testCase.isOsr() ? "osr_" : "") + "compiled"); 260 } 261 if (WHITE_BOX.getMethodCompilationLevel(method, testCase.isOsr()) 262 == 0) { 263 throw new RuntimeException(method 264 + (testCase.isOsr() ? " osr_" : " ") 265 + "comp_level must be != 0"); 266 } 267 } 268 269 protected final void deoptimize() { 270 WHITE_BOX.deoptimizeMethod(method, testCase.isOsr()); 271 if (testCase.isOsr()) { 272 WHITE_BOX.deoptimizeMethod(method, false); 273 } 274 } 275 276 protected final int getCompLevel() { 277 NMethod nm = NMethod.get(method, testCase.isOsr()); 278 return nm == null ? COMP_LEVEL_NONE : nm.comp_level; 279 } 280 281 protected final boolean isCompilable() { 282 return WHITE_BOX.isMethodCompilable(method, COMP_LEVEL_ANY, 283 testCase.isOsr()); 284 } 285 286 protected final boolean isCompilable(int compLevel) { 287 return WHITE_BOX 288 .isMethodCompilable(method, compLevel, testCase.isOsr()); 289 } 290 291 protected final void makeNotCompilable() { 292 WHITE_BOX.makeMethodNotCompilable(method, COMP_LEVEL_ANY, 293 testCase.isOsr()); 294 } 295 296 protected final void makeNotCompilable(int compLevel) { 297 WHITE_BOX.makeMethodNotCompilable(method, compLevel, testCase.isOsr()); 298 } 299 300 /** 301 * Waits for completion of background compilation of {@linkplain #method}. 302 */ 303 protected final void waitBackgroundCompilation() { 304 waitBackgroundCompilation(method); 305 } 306 307 /** 308 * Waits for completion of background compilation of the given executable. 309 * 310 * @param executable Executable 311 */ 312 protected static final void waitBackgroundCompilation(Executable executable) { 313 if (!BACKGROUND_COMPILATION) { 314 return; 315 } 316 final Object obj = new Object(); 317 for (int i = 0; i < 10 318 && WHITE_BOX.isMethodQueuedForCompilation(executable); ++i) { 319 synchronized (obj) { 320 try { 321 obj.wait(1000); 322 } catch (InterruptedException e) { 323 Thread.currentThread().interrupt(); 324 } 325 } 326 } 327 } 328 329 /** 330 * Prints information about {@linkplain #method}. 331 */ 332 protected final void printInfo() { 333 System.out.printf("%n%s:%n", method); 334 System.out.printf("\tcompilable:\t%b%n", 335 WHITE_BOX.isMethodCompilable(method, COMP_LEVEL_ANY, false)); 336 System.out.printf("\tcompiled:\t%b%n", 337 WHITE_BOX.isMethodCompiled(method, false)); 338 System.out.printf("\tcomp_level:\t%d%n", 339 WHITE_BOX.getMethodCompilationLevel(method, false)); 340 System.out.printf("\tosr_compilable:\t%b%n", 341 WHITE_BOX.isMethodCompilable(method, COMP_LEVEL_ANY, true)); 342 System.out.printf("\tosr_compiled:\t%b%n", 343 WHITE_BOX.isMethodCompiled(method, true)); 344 System.out.printf("\tosr_comp_level:\t%d%n", 345 WHITE_BOX.getMethodCompilationLevel(method, true)); 346 System.out.printf("\tin_queue:\t%b%n", 347 WHITE_BOX.isMethodQueuedForCompilation(method)); 348 System.out.printf("compile_queues_size:\t%d%n%n", 349 WHITE_BOX.getCompileQueuesSize()); 350 } 351 352 /** 353 * Executes testing. 354 */ 355 protected abstract void test() throws Exception; 356 357 /** 358 * Tries to trigger compilation of {@linkplain #method} by call 359 * {@linkplain TestCase#getCallable()} enough times. 360 * 361 * @return accumulated result 362 * @see #compile(int) 363 */ 364 protected final int compile() { 365 if (testCase.isOsr()) { 366 return compile(1); 367 } else { 368 return compile(THRESHOLD); 369 } 370 } 371 372 /** 373 * Tries to trigger compilation of {@linkplain #method} by call 374 * {@linkplain TestCase#getCallable()} specified times. 375 * 376 * @param count invocation count 377 * @return accumulated result 378 */ 379 protected final int compile(int count) { 380 int result = 0; 381 Integer tmp; 382 for (int i = 0; i < count; ++i) { 383 try { 384 tmp = testCase.getCallable().call(); 385 } catch (Exception e) { 386 tmp = null; 387 } 388 result += tmp == null ? 0 : tmp; 389 } 390 if (IS_VERBOSE) { 391 System.out.println("method was invoked " + count + " times"); 392 } 393 return result; 394 } 395 396 /** 397 * Utility interface provides tested method and object to invoke it. 398 */ 399 public interface TestCase { 400 /** the name of test case */ 401 String name(); 402 403 /** tested method */ 404 Executable getExecutable(); 405 406 /** object to invoke {@linkplain #getExecutable()} */ 407 Callable<Integer> getCallable(); 408 409 /** flag for OSR test case */ 410 boolean isOsr(); 411 } 412 413 /** 414 * @return {@code true} if the current test case is OSR and the mode is 415 * Xcomp, otherwise {@code false} 416 */ 417 protected boolean skipXcompOSR() { 418 boolean result = testCase.isOsr() 419 && CompilerWhiteBoxTest.MODE.startsWith("compiled "); 420 if (result && IS_VERBOSE) { 421 System.err.printf("Warning: %s is not applicable in %s%n", 422 testCase.name(), CompilerWhiteBoxTest.MODE); 423 } 424 return result; 425 } 426 } 427 428 enum SimpleTestCase implements CompilerWhiteBoxTest.TestCase { 429 /** constructor test case */ 430 CONSTRUCTOR_TEST(Helper.CONSTRUCTOR, Helper.CONSTRUCTOR_CALLABLE, false), 431 /** method test case */ 432 METHOD_TEST(Helper.METHOD, Helper.METHOD_CALLABLE, false), 433 /** static method test case */ 434 STATIC_TEST(Helper.STATIC, Helper.STATIC_CALLABLE, false), 435 /** OSR constructor test case */ 436 OSR_CONSTRUCTOR_TEST(Helper.OSR_CONSTRUCTOR, 437 Helper.OSR_CONSTRUCTOR_CALLABLE, true), 438 /** OSR method test case */ 439 OSR_METHOD_TEST(Helper.OSR_METHOD, Helper.OSR_METHOD_CALLABLE, true), 440 /** OSR static method test case */ 441 OSR_STATIC_TEST(Helper.OSR_STATIC, Helper.OSR_STATIC_CALLABLE, true); 442 443 private final Executable executable; 444 private final Callable<Integer> callable; 445 private final boolean isOsr; 446 447 private SimpleTestCase(Executable executable, Callable<Integer> callable, 448 boolean isOsr) { 449 this.executable = executable; 450 this.callable = callable; 451 this.isOsr = isOsr; 452 } 453 454 @Override 455 public Executable getExecutable() { 456 return executable; 457 } 458 459 @Override 460 public Callable<Integer> getCallable() { 461 return callable; 462 } 463 464 @Override 465 public boolean isOsr() { 466 return isOsr; 467 } 468 469 private static class Helper { 470 471 private static final Callable<Integer> CONSTRUCTOR_CALLABLE 472 = new Callable<Integer>() { 473 @Override 474 public Integer call() throws Exception { 475 return new Helper(1337).hashCode(); 476 } 477 }; 478 479 private static final Callable<Integer> METHOD_CALLABLE 480 = new Callable<Integer>() { 481 private final Helper helper = new Helper(); 482 483 @Override 484 public Integer call() throws Exception { 485 return helper.method(); 486 } 487 }; 488 489 private static final Callable<Integer> STATIC_CALLABLE 490 = new Callable<Integer>() { 491 @Override 492 public Integer call() throws Exception { 493 return staticMethod(); 494 } 495 }; 496 497 private static final Callable<Integer> OSR_CONSTRUCTOR_CALLABLE 498 = new Callable<Integer>() { 499 @Override 500 public Integer call() throws Exception { 501 int result = warmup(OSR_CONSTRUCTOR); 502 return result + new Helper(null, CompilerWhiteBoxTest.BACKEDGE_THRESHOLD).hashCode(); 503 } 504 }; 505 506 private static final Callable<Integer> OSR_METHOD_CALLABLE 507 = new Callable<Integer>() { 508 private final Helper helper = new Helper(); 509 510 @Override 511 public Integer call() throws Exception { 512 int result = warmup(OSR_METHOD); 513 return result + helper.osrMethod(CompilerWhiteBoxTest.BACKEDGE_THRESHOLD); 514 } 515 }; 516 517 private static final Callable<Integer> OSR_STATIC_CALLABLE 518 = new Callable<Integer>() { 519 @Override 520 public Integer call() throws Exception { 521 int result = warmup(OSR_STATIC); 522 return result + osrStaticMethod(CompilerWhiteBoxTest.BACKEDGE_THRESHOLD); 523 } 524 }; 525 526 /** 527 * Deoptimizes all non-osr versions of the given executable after 528 * compilation finished. 529 * 530 * @param e Executable 531 * @throws Exception 532 */ 533 private static void waitAndDeoptimize(Executable e) throws Exception { 534 CompilerWhiteBoxTest.waitBackgroundCompilation(e); 535 if (WhiteBox.getWhiteBox().isMethodQueuedForCompilation(e)) { 536 throw new RuntimeException(e + " must not be in queue"); 537 } 538 // Deoptimize non-osr versions of executable 539 WhiteBox.getWhiteBox().deoptimizeMethod(e, false); 540 } 541 542 /** 543 * Executes the method multiple times to make sure we have 544 * enough profiling information before triggering an OSR 545 * compilation. Otherwise the C2 compiler may add uncommon traps. 546 * 547 * @param m Method to be executed 548 * @return Number of times the method was executed 549 * @throws Exception 550 */ 551 private static int warmup(Method m) throws Exception { 552 Helper helper = new Helper(); 553 int result = 0; 554 for (long i = 0; i < CompilerWhiteBoxTest.OSR_WARMUP; ++i) { 555 result += (int)m.invoke(helper, 1); 556 } 557 // Deoptimize non-osr versions 558 waitAndDeoptimize(m); 559 return result; 560 } 561 562 /** 563 * Executes the constructor multiple times to make sure we 564 * have enough profiling information before triggering an OSR 565 * compilation. Otherwise the C2 compiler may add uncommon traps. 566 * 567 * @param c Constructor to be executed 568 * @return Number of times the constructor was executed 569 * @throws Exception 570 */ 571 private static int warmup(Constructor c) throws Exception { 572 int result = 0; 573 for (long i = 0; i < CompilerWhiteBoxTest.OSR_WARMUP; ++i) { 574 result += c.newInstance(null, 1).hashCode(); 575 } 576 // Deoptimize non-osr versions 577 waitAndDeoptimize(c); 578 return result; 579 } 580 581 private static final Constructor CONSTRUCTOR; 582 private static final Constructor OSR_CONSTRUCTOR; 583 private static final Method METHOD; 584 private static final Method STATIC; 585 private static final Method OSR_METHOD; 586 private static final Method OSR_STATIC; 587 588 static { 589 try { 590 CONSTRUCTOR = Helper.class.getDeclaredConstructor(int.class); 591 } catch (NoSuchMethodException | SecurityException e) { 592 throw new RuntimeException( 593 "exception on getting method Helper.<init>(int)", e); 594 } 595 try { 596 OSR_CONSTRUCTOR = Helper.class.getDeclaredConstructor( 597 Object.class, long.class); 598 } catch (NoSuchMethodException | SecurityException e) { 599 throw new RuntimeException( 600 "exception on getting method Helper.<init>(Object, long)", e); 601 } 602 METHOD = getMethod("method"); 603 STATIC = getMethod("staticMethod"); 604 OSR_METHOD = getMethod("osrMethod", long.class); 605 OSR_STATIC = getMethod("osrStaticMethod", long.class); 606 } 607 608 private static Method getMethod(String name, Class<?>... parameterTypes) { 609 try { 610 return Helper.class.getDeclaredMethod(name, parameterTypes); 611 } catch (NoSuchMethodException | SecurityException e) { 612 throw new RuntimeException( 613 "exception on getting method Helper." + name, e); 614 } 615 } 616 617 private static int staticMethod() { 618 return 1138; 619 } 620 621 private int method() { 622 return 42; 623 } 624 625 private static int osrStaticMethod(long limit) { 626 int result = 0; 627 for (long i = 0; i < limit; ++i) { 628 result += staticMethod(); 629 } 630 return result; 631 } 632 633 private int osrMethod(long limit) { 634 int result = 0; 635 for (long i = 0; i < limit; ++i) { 636 result += method(); 637 } 638 return result; 639 } 640 641 private final int x; 642 643 // for method and OSR method test case 644 public Helper() { 645 x = 0; 646 } 647 648 // for OSR constructor test case 649 private Helper(Object o, long limit) { 650 int result = 0; 651 for (long i = 0; i < limit; ++i) { 652 result += method(); 653 } 654 x = result; 655 } 656 657 // for constructor test case 658 private Helper(int x) { 659 this.x = x; 660 } 661 662 @Override 663 public int hashCode() { 664 return x; 665 } 666 } 667 }