1 /*
   2  * Copyright (c) 2013, 2016, 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.whitebox;
  25 
  26 import jdk.test.lib.Platform;
  27 import sun.hotspot.WhiteBox;
  28 import sun.hotspot.code.NMethod;
  29 
  30 import java.lang.reflect.Executable;
  31 import java.util.Objects;
  32 import java.util.concurrent.Callable;
  33 import java.util.function.Function;
  34 
  35 /**
  36  * Abstract class for WhiteBox testing of JIT.
  37  * Depends on jdk.test.lib.Platform from testlibrary.
  38  *
  39  * @author igor.ignatyev@oracle.com
  40  */
  41 public abstract class CompilerWhiteBoxTest {
  42     /** {@code CompLevel::CompLevel_none} -- Interpreter */
  43     public static final int COMP_LEVEL_NONE = 0;
  44     /** {@code CompLevel::CompLevel_any}, {@code CompLevel::CompLevel_all} */
  45     public static final int COMP_LEVEL_ANY = -2;
  46     /** {@code CompLevel::CompLevel_aot} -- AOT */
  47     public static final int COMP_LEVEL_AOT = -1;
  48     /** {@code CompLevel::CompLevel_simple} -- C1 */
  49     public static final int COMP_LEVEL_SIMPLE = 1;
  50     /** {@code CompLevel::CompLevel_limited_profile} -- C1, invocation & backedge counters */
  51     public static final int COMP_LEVEL_LIMITED_PROFILE = 2;
  52     /** {@code CompLevel::CompLevel_full_profile} -- C1, invocation & backedge counters + mdo */
  53     public static final int COMP_LEVEL_FULL_PROFILE = 3;
  54     /** {@code CompLevel::CompLevel_full_optimization} -- C2 or Shark */
  55     public static final int COMP_LEVEL_FULL_OPTIMIZATION = 4;
  56     /** Maximal value for CompLevel */
  57     public static final int COMP_LEVEL_MAX = COMP_LEVEL_FULL_OPTIMIZATION;
  58 
  59     /** Instance of WhiteBox */
  60     protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
  61     /** Value of {@code -XX:CompileThreshold} */
  62     protected static final int COMPILE_THRESHOLD
  63             = Integer.parseInt(getVMOption("CompileThreshold", "10000"));
  64     /** Value of {@code -XX:BackgroundCompilation} */
  65     protected static final boolean BACKGROUND_COMPILATION
  66             = Boolean.valueOf(getVMOption("BackgroundCompilation", "true"));
  67     protected static final boolean USE_COUNTER_DECAY
  68             = Boolean.valueOf(getVMOption("UseCounterDecay", "true"));
  69     /** Value of {@code -XX:TieredCompilation} */
  70     protected static final boolean TIERED_COMPILATION
  71             = Boolean.valueOf(getVMOption("TieredCompilation", "false"));
  72     /** Value of {@code -XX:TieredStopAtLevel} */
  73     protected static final int TIERED_STOP_AT_LEVEL
  74             = Integer.parseInt(getVMOption("TieredStopAtLevel", "0"));
  75     /** Flag for verbose output, true if {@code -Dverbose} specified */
  76     protected static final boolean IS_VERBOSE
  77             = System.getProperty("verbose") != null;
  78     /** invocation count to trigger compilation */
  79     public static final int THRESHOLD;
  80     /** invocation count to trigger OSR compilation */
  81     protected static final long BACKEDGE_THRESHOLD;
  82 
  83     static {
  84         if (TIERED_COMPILATION) {
  85             BACKEDGE_THRESHOLD = THRESHOLD = 150000;
  86         } else {
  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 (Platform.isInt()) {
 171             throw new Error("TESTBUG: test can not be run in interpreter");
 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     protected final void checkNotCompiled(int compLevel) {
 201         if (WHITE_BOX.isMethodQueuedForCompilation(method)) {
 202             throw new RuntimeException(method + " must not be in queue");
 203         }
 204         if (WHITE_BOX.getMethodCompilationLevel(method, false) >= compLevel) {
 205             throw new RuntimeException(method + " comp_level must be >= maxCompLevel");
 206         }
 207         if (WHITE_BOX.getMethodCompilationLevel(method, true) >= compLevel) {
 208             throw new RuntimeException(method + " osr_comp_level must be >= maxCompLevel");
 209         }
 210     }
 211 
 212     /**
 213      * Checks, that {@linkplain #method} is not compiled.
 214      *
 215      * @throws RuntimeException if {@linkplain #method} is in compiler queue or
 216      *                          is compiled, or if {@linkplain #method} has zero
 217      *                          compilation level.
 218      */
 219     protected final void checkNotCompiled() {
 220         waitBackgroundCompilation();
 221         checkNotCompiled(true);
 222         checkNotCompiled(false);
 223     }
 224 
 225     /**
 226      * Checks, that {@linkplain #method} is not (OSR-)compiled.
 227      *
 228      * @param isOsr Check for OSR compilation if true
 229      * @throws RuntimeException if {@linkplain #method} is in compiler queue or
 230      *                          is compiled, or if {@linkplain #method} has zero
 231      *                          compilation level.
 232      */
 233     protected final void checkNotCompiled(boolean isOsr) {
 234         if (WHITE_BOX.isMethodQueuedForCompilation(method)) {
 235             throw new RuntimeException(method + " must not be in queue");
 236         }
 237         if (WHITE_BOX.isMethodCompiled(method, isOsr)) {
 238             throw new RuntimeException(method + " must not be " +
 239                                        (isOsr ? "osr_" : "") + "compiled");
 240         }
 241         if (WHITE_BOX.getMethodCompilationLevel(method, isOsr) != 0) {
 242             throw new RuntimeException(method + (isOsr ? " osr_" : " ") +
 243                                        "comp_level must be == 0");
 244         }
 245     }
 246 
 247     /**
 248      * Checks, that {@linkplain #method} is compiled.
 249      *
 250      * @throws RuntimeException if {@linkplain #method} isn't in compiler queue
 251      *                          and isn't compiled, or if {@linkplain #method}
 252      *                          has nonzero compilation level
 253      */
 254     protected final void checkCompiled() {
 255         final long start = System.currentTimeMillis();
 256         waitBackgroundCompilation();
 257         if (WHITE_BOX.isMethodQueuedForCompilation(method)) {
 258             System.err.printf("Warning: %s is still in queue after %dms%n",
 259                     method, System.currentTimeMillis() - start);
 260             return;
 261         }
 262         if (!WHITE_BOX.isMethodCompiled(method, testCase.isOsr())) {
 263             throw new RuntimeException(method + " must be "
 264                     + (testCase.isOsr() ? "osr_" : "") + "compiled");
 265         }
 266         if (WHITE_BOX.getMethodCompilationLevel(method, testCase.isOsr())
 267                 == 0) {
 268             throw new RuntimeException(method
 269                     + (testCase.isOsr() ? " osr_" : " ")
 270                     + "comp_level must be != 0");
 271         }
 272     }
 273 
 274     protected final void deoptimize() {
 275         WHITE_BOX.deoptimizeMethod(method, testCase.isOsr());
 276         if (testCase.isOsr()) {
 277             WHITE_BOX.deoptimizeMethod(method, false);
 278         }
 279     }
 280 
 281     protected final int getCompLevel() {
 282         NMethod nm = NMethod.get(method, testCase.isOsr());
 283         return nm == null ? COMP_LEVEL_NONE : nm.comp_level;
 284     }
 285 
 286     protected final boolean isCompilable() {
 287         return WHITE_BOX.isMethodCompilable(method, COMP_LEVEL_ANY,
 288                 testCase.isOsr());
 289     }
 290 
 291     protected final boolean isCompilable(int compLevel) {
 292         return WHITE_BOX
 293                 .isMethodCompilable(method, compLevel, testCase.isOsr());
 294     }
 295 
 296     protected final void makeNotCompilable() {
 297         WHITE_BOX.makeMethodNotCompilable(method, COMP_LEVEL_ANY,
 298                 testCase.isOsr());
 299     }
 300 
 301     protected final void makeNotCompilable(int compLevel) {
 302         WHITE_BOX.makeMethodNotCompilable(method, compLevel, testCase.isOsr());
 303     }
 304 
 305     /**
 306      * Waits for completion of background compilation of {@linkplain #method}.
 307      */
 308     protected final void waitBackgroundCompilation() {
 309         waitBackgroundCompilation(method);
 310     }
 311 
 312     /**
 313      * Waits for completion of background compilation of the given executable.
 314      *
 315      * @param executable Executable
 316      */
 317     public static final void waitBackgroundCompilation(Executable executable) {
 318         if (!BACKGROUND_COMPILATION) {
 319             return;
 320         }
 321         final Object obj = new Object();
 322         for (int i = 0; i < 100
 323                 && WHITE_BOX.isMethodQueuedForCompilation(executable); ++i) {
 324             synchronized (obj) {
 325                 try {
 326                     obj.wait(100);
 327                 } catch (InterruptedException e) {
 328                     Thread.currentThread().interrupt();
 329                 }
 330             }
 331         }
 332     }
 333 
 334     /**
 335      * Prints information about {@linkplain #method}.
 336      */
 337     protected final void printInfo() {
 338         System.out.printf("%n%s:%n", method);
 339         System.out.printf("\tcompilable:\t%b%n",
 340                 WHITE_BOX.isMethodCompilable(method, COMP_LEVEL_ANY, false));
 341         boolean isCompiled = WHITE_BOX.isMethodCompiled(method, false);
 342         System.out.printf("\tcompiled:\t%b%n", isCompiled);
 343         if (isCompiled) {
 344             System.out.printf("\tcompile_id:\t%d%n",
 345                     NMethod.get(method, false).compile_id);
 346         }
 347         System.out.printf("\tcomp_level:\t%d%n",
 348                 WHITE_BOX.getMethodCompilationLevel(method, false));
 349         System.out.printf("\tosr_compilable:\t%b%n",
 350                 WHITE_BOX.isMethodCompilable(method, COMP_LEVEL_ANY, true));
 351         isCompiled = WHITE_BOX.isMethodCompiled(method, true);
 352         System.out.printf("\tosr_compiled:\t%b%n", isCompiled);
 353         if (isCompiled) {
 354             System.out.printf("\tosr_compile_id:\t%d%n",
 355                     NMethod.get(method, true).compile_id);
 356         }
 357         System.out.printf("\tosr_comp_level:\t%d%n",
 358                 WHITE_BOX.getMethodCompilationLevel(method, true));
 359         System.out.printf("\tin_queue:\t%b%n",
 360                 WHITE_BOX.isMethodQueuedForCompilation(method));
 361         System.out.printf("compile_queues_size:\t%d%n%n",
 362                 WHITE_BOX.getCompileQueuesSize());
 363     }
 364 
 365     /**
 366      * Executes testing.
 367      */
 368     protected abstract void test() throws Exception;
 369 
 370     /**
 371      * Tries to trigger compilation of {@linkplain #method} by call
 372      * {@linkplain TestCase#getCallable()} enough times.
 373      *
 374      * @return accumulated result
 375      * @see #compile(int)
 376      */
 377     protected final int compile() throws Exception {
 378         if (USE_COUNTER_DECAY) {
 379             throw new Exception("Tests using compile method must turn off counter decay for reliability");
 380         }
 381         if (testCase.isOsr()) {
 382             return compile(1);
 383         } else {
 384             return compile(THRESHOLD);
 385         }
 386     }
 387 
 388     /**
 389      * Tries to trigger compilation of {@linkplain #method} by call
 390      * {@linkplain TestCase#getCallable()} specified times.
 391      *
 392      * @param count invocation count
 393      * @return accumulated result
 394      */
 395     protected final int compile(int count) {
 396         int result = 0;
 397         Integer tmp;
 398         for (int i = 0; i < count; ++i) {
 399             try {
 400                 tmp = testCase.getCallable().call();
 401             } catch (Exception e) {
 402                 tmp = null;
 403             }
 404             result += tmp == null ? 0 : tmp;
 405         }
 406         if (IS_VERBOSE) {
 407             System.out.println("method was invoked " + count + " times");
 408         }
 409         return result;
 410     }
 411 
 412     /**
 413      * Utility interface provides tested method and object to invoke it.
 414      */
 415     public interface TestCase {
 416         /** the name of test case */
 417         String name();
 418 
 419         /** tested method */
 420         Executable getExecutable();
 421 
 422         /** object to invoke {@linkplain #getExecutable()} */
 423         Callable<Integer> getCallable();
 424 
 425         /** flag for OSR test case */
 426         boolean isOsr();
 427     }
 428 
 429     /**
 430      * @return {@code true} if the current test case is OSR and the mode is
 431      *          Xcomp, otherwise {@code false}
 432      */
 433     protected boolean skipXcompOSR() {
 434         boolean result = testCase.isOsr() && Platform.isComp();
 435         if (result && IS_VERBOSE) {
 436             System.err.printf("Warning: %s is not applicable in %s%n",
 437                     testCase.name(), Platform.vmInfo);
 438         }
 439         return result;
 440     }
 441 
 442     /**
 443      * Skip the test for the specified value of Tiered Compilation
 444      * @param value of TieredCompilation the test should not run with
 445      * @return {@code true} if the test should be skipped,
 446      *         {@code false} otherwise
 447      */
 448     public static boolean skipOnTieredCompilation(boolean value) {
 449         if (value == CompilerWhiteBoxTest.TIERED_COMPILATION) {
 450             System.err.println("Test isn't applicable w/ "
 451                     + (value ? "enabled" : "disabled")
 452                     + "TieredCompilation. Skip test.");
 453             return true;
 454         }
 455         return false;
 456     }
 457 }
 458