1 /*
   2  * Copyright (c) 2014, 2015, 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 /**
  25  * @test LevelTransitionTest
  26  * @library /testlibrary /test/lib /
  27  * @modules java.base/jdk.internal.misc
  28  *          java.management
  29  * @ignore 8067651
  30  * @build TransitionsTestExecutor LevelTransitionTest
  31  * @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission
  32  * @run main/othervm/timeout=240 -Xmixed -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
  33  *                   -XX:+WhiteBoxAPI -XX:+TieredCompilation
  34  *                   -XX:CompileCommand=compileonly,compiler.whitebox.SimpleTestCaseHelper::*
  35  *                   -XX:CompileCommand=compileonly,ExtendedTestCase$CompileMethodHolder::*
  36  *                   TransitionsTestExecutor LevelTransitionTest
  37  * @summary Test the correctness of compilation level transitions for different methods
  38  */
  39 
  40 import java.lang.reflect.Executable;
  41 import java.lang.reflect.Method;
  42 import java.util.Objects;
  43 import java.util.concurrent.Callable;
  44 import compiler.whitebox.CompilerWhiteBoxTest;
  45 import compiler.whitebox.SimpleTestCase;
  46 
  47 public class LevelTransitionTest extends TieredLevelsTest {
  48     /** Shows if method was profiled by being executed on levels 2 or 3 */
  49     protected boolean isMethodProfiled;
  50     private int transitionCount;
  51 
  52     public static void main(String[] args) throws Throwable {
  53         assert (!CompilerWhiteBoxTest.skipOnTieredCompilation(false));
  54 
  55         CompilerWhiteBoxTest.main(LevelTransitionTest::new, args);
  56         // run extended test cases
  57         for (TestCase testCase : ExtendedTestCase.values()) {
  58             new LevelTransitionTest(testCase).runTest();
  59         }
  60     }
  61 
  62     protected LevelTransitionTest(TestCase testCase) {
  63         super(testCase);
  64         isMethodProfiled = testCase.isOsr(); // OSR methods were already profiled by warmup
  65         transitionCount = 0;
  66     }
  67 
  68     @Override
  69     protected void test() throws Exception {
  70         checkTransitions();
  71         deoptimize();
  72         printInfo();
  73         if (testCase.isOsr()) {
  74             // deoptimization makes the following transitions be unstable
  75             // methods go to level 3 before 4 because of uncommon_trap and reprofile
  76             return;
  77         }
  78         checkTransitions();
  79     }
  80 
  81     /**
  82      * Makes and verifies transitions between compilation levels
  83      */
  84     protected void checkTransitions() throws Exception {
  85         checkNotCompiled();
  86         boolean finish = false;
  87         while (!finish) {
  88             System.out.printf("Level transition #%d%n", ++transitionCount);
  89             int newLevel;
  90             int current = getCompLevel();
  91             int expected = getNextLevel(current);
  92             if (current == expected) {
  93                 // if we are on expected level, just execute it more
  94                 // to ensure that the level won't change
  95                 System.out.printf("Method %s is already on expected level %d%n", method, expected);
  96                 compile();
  97                 newLevel = getCompLevel();
  98                 finish = true;
  99             } else {
 100                 newLevel = changeCompLevel();
 101                 finish = false;
 102             }
 103             System.out.printf("Method %s is compiled on level %d. Expected level is %d%n", method, newLevel, expected);
 104             checkLevel(expected, newLevel);
 105             printInfo();
 106         };
 107     }
 108 
 109     /**
 110      * Gets next expected level for the test case on each transition.
 111      *
 112      * @param currentLevel a level the test case is compiled on
 113      * @return expected compilation level
 114      */
 115     protected int getNextLevel(int currentLevel) {
 116         int nextLevel = currentLevel;
 117         switch (currentLevel) {
 118             case CompilerWhiteBoxTest.COMP_LEVEL_NONE:
 119                 nextLevel = isMethodProfiled ? CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION
 120                         : CompilerWhiteBoxTest.COMP_LEVEL_FULL_PROFILE;
 121                 break;
 122             case CompilerWhiteBoxTest.COMP_LEVEL_LIMITED_PROFILE:
 123             case CompilerWhiteBoxTest.COMP_LEVEL_FULL_PROFILE:
 124                 nextLevel = CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION;
 125                 isMethodProfiled = true;
 126                 break;
 127         }
 128         nextLevel = isTrivial() ? CompilerWhiteBoxTest.COMP_LEVEL_SIMPLE : nextLevel;
 129         return Math.min(nextLevel, CompilerWhiteBoxTest.TIERED_STOP_AT_LEVEL);
 130     }
 131 
 132     /**
 133      * Determines if tested method should be handled as trivial
 134      *
 135      * @return {@code true} for trivial methods, {@code false} otherwise
 136      */
 137     protected boolean isTrivial() {
 138         return testCase == ExtendedTestCase.ACCESSOR_TEST
 139                 || testCase == SimpleTestCase.METHOD_TEST
 140                 || testCase == SimpleTestCase.STATIC_TEST
 141                 || (testCase == ExtendedTestCase.TRIVIAL_CODE_TEST && isMethodProfiled);
 142     }
 143 
 144     /**
 145      * Invokes {@linkplain #method} until its compilation level is changed.
 146      * Note that if the level won't change, it will be an endless loop
 147      *
 148      * @return compilation level the {@linkplain #method} was compiled on
 149      */
 150     protected int changeCompLevel() {
 151         int currentLevel = getCompLevel();
 152         int newLevel = currentLevel;
 153         int result = 0;
 154         while (currentLevel == newLevel) {
 155             result = compile(1);
 156             if (WHITE_BOX.isMethodCompiled(method, testCase.isOsr())) {
 157                 newLevel = getCompLevel();
 158             }
 159         }
 160         return newLevel;
 161     }
 162 
 163     protected static class Helper {
 164         /**
 165          * Gets method from a specified class using its name
 166          *
 167          * @param aClass type method belongs to
 168          * @param name   the name of the method
 169          * @return {@link Method} that represents corresponding class method
 170          */
 171         public static Method getMethod(Class<?> aClass, String name) {
 172             Method method;
 173             try {
 174                 method = aClass.getDeclaredMethod(name);
 175             } catch (NoSuchMethodException e) {
 176                 throw new Error("TESTBUG: Unable to get method " + name, e);
 177             }
 178             return method;
 179         }
 180 
 181         /**
 182          * Gets {@link Callable} that invokes given method from the given object
 183          *
 184          * @param object the object the specified method is invoked from
 185          * @param name   the name of the method
 186          */
 187         public static Callable<Integer> getCallable(Object object, String name) {
 188             Method method = getMethod(object.getClass(), name);
 189             return () -> {
 190                 try {
 191                     return Objects.hashCode(method.invoke(object));
 192                 } catch (ReflectiveOperationException e) {
 193                     throw new Error("TESTBUG: Invocation failure", e);
 194                 }
 195             };
 196         }
 197     }
 198 }
 199 
 200 enum ExtendedTestCase implements CompilerWhiteBoxTest.TestCase {
 201     ACCESSOR_TEST("accessor"),
 202     NONTRIVIAL_METHOD_TEST("nonTrivialMethod"),
 203     TRIVIAL_CODE_TEST("trivialCode");
 204 
 205     private final Executable executable;
 206     private final Callable<Integer> callable;
 207 
 208     @Override
 209     public Executable getExecutable() {
 210         return executable;
 211     }
 212 
 213     @Override
 214     public Callable<Integer> getCallable() {
 215         return callable;
 216     }
 217 
 218     @Override
 219     public boolean isOsr() {
 220         return false;
 221     }
 222 
 223     private ExtendedTestCase(String methodName) {
 224         this.executable = LevelTransitionTest.Helper.getMethod(CompileMethodHolder.class, methodName);
 225         this.callable = LevelTransitionTest.Helper.getCallable(new CompileMethodHolder(), methodName);
 226     }
 227 
 228     private static class CompileMethodHolder {
 229         private final int iter = 10;
 230         private int field = 42;
 231 
 232         /** Non-trivial method for threshold policy: contains loops */
 233         public int nonTrivialMethod() {
 234             int acc = 0;
 235             for (int i = 0; i < iter; i++) {
 236                 acc += i;
 237             }
 238             return acc;
 239         }
 240 
 241         /** Field accessor method */
 242         public int accessor() {
 243             return field;
 244         }
 245 
 246         /** Method considered as trivial by amount of code */
 247         public int trivialCode() {
 248             int var = 0xBAAD_C0DE;
 249             var *= field;
 250             return var;
 251         }
 252     }
 253 }