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 }