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