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