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