test/hotspot/jtreg/compiler/whitebox/CompilerWhiteBoxTest.java
changeset 47216 71c04702a3d5
parent 42650 1f304d0c888b
child 47687 fb290fd1f9d4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/compiler/whitebox/CompilerWhiteBoxTest.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,458 @@
+/*
+ * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package compiler.whitebox;
+
+import jdk.test.lib.Platform;
+import sun.hotspot.WhiteBox;
+import sun.hotspot.code.NMethod;
+
+import java.lang.reflect.Executable;
+import java.util.Objects;
+import java.util.concurrent.Callable;
+import java.util.function.Function;
+
+/**
+ * Abstract class for WhiteBox testing of JIT.
+ * Depends on jdk.test.lib.Platform from testlibrary.
+ *
+ * @author igor.ignatyev@oracle.com
+ */
+public abstract class CompilerWhiteBoxTest {
+    /** {@code CompLevel::CompLevel_none} -- Interpreter */
+    public static final int COMP_LEVEL_NONE = 0;
+    /** {@code CompLevel::CompLevel_any}, {@code CompLevel::CompLevel_all} */
+    public static final int COMP_LEVEL_ANY = -2;
+    /** {@code CompLevel::CompLevel_aot} -- AOT */
+    public static final int COMP_LEVEL_AOT = -1;
+    /** {@code CompLevel::CompLevel_simple} -- C1 */
+    public static final int COMP_LEVEL_SIMPLE = 1;
+    /** {@code CompLevel::CompLevel_limited_profile} -- C1, invocation & backedge counters */
+    public static final int COMP_LEVEL_LIMITED_PROFILE = 2;
+    /** {@code CompLevel::CompLevel_full_profile} -- C1, invocation & backedge counters + mdo */
+    public static final int COMP_LEVEL_FULL_PROFILE = 3;
+    /** {@code CompLevel::CompLevel_full_optimization} -- C2 or Shark */
+    public static final int COMP_LEVEL_FULL_OPTIMIZATION = 4;
+    /** Maximal value for CompLevel */
+    public static final int COMP_LEVEL_MAX = COMP_LEVEL_FULL_OPTIMIZATION;
+
+    /** Instance of WhiteBox */
+    protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+    /** Value of {@code -XX:CompileThreshold} */
+    protected static final int COMPILE_THRESHOLD
+            = Integer.parseInt(getVMOption("CompileThreshold", "10000"));
+    /** Value of {@code -XX:BackgroundCompilation} */
+    protected static final boolean BACKGROUND_COMPILATION
+            = Boolean.valueOf(getVMOption("BackgroundCompilation", "true"));
+    protected static final boolean USE_COUNTER_DECAY
+            = Boolean.valueOf(getVMOption("UseCounterDecay", "true"));
+    /** Value of {@code -XX:TieredCompilation} */
+    protected static final boolean TIERED_COMPILATION
+            = Boolean.valueOf(getVMOption("TieredCompilation", "false"));
+    /** Value of {@code -XX:TieredStopAtLevel} */
+    protected static final int TIERED_STOP_AT_LEVEL
+            = Integer.parseInt(getVMOption("TieredStopAtLevel", "0"));
+    /** Flag for verbose output, true if {@code -Dverbose} specified */
+    protected static final boolean IS_VERBOSE
+            = System.getProperty("verbose") != null;
+    /** invocation count to trigger compilation */
+    public static final int THRESHOLD;
+    /** invocation count to trigger OSR compilation */
+    protected static final long BACKEDGE_THRESHOLD;
+
+    static {
+        if (TIERED_COMPILATION) {
+            BACKEDGE_THRESHOLD = THRESHOLD = 150000;
+        } else {
+            THRESHOLD = COMPILE_THRESHOLD;
+            BACKEDGE_THRESHOLD = COMPILE_THRESHOLD * Long.parseLong(getVMOption(
+                    "OnStackReplacePercentage"));
+        }
+    }
+
+    /**
+     * Returns value of VM option.
+     *
+     * @param name option's name
+     * @return value of option or {@code null}, if option doesn't exist
+     * @throws NullPointerException if name is null
+     */
+    protected static String getVMOption(String name) {
+        Objects.requireNonNull(name);
+        return Objects.toString(WHITE_BOX.getVMFlag(name), null);
+    }
+
+    /**
+     * Returns value of VM option or default value.
+     *
+     * @param name         option's name
+     * @param defaultValue default value
+     * @return value of option or {@code defaultValue}, if option doesn't exist
+     * @throws NullPointerException if name is null
+     * @see #getVMOption(String)
+     */
+    protected static String getVMOption(String name, String defaultValue) {
+        String result = getVMOption(name);
+        return result == null ? defaultValue : result;
+    }
+
+    /** copy of is_c1_compile(int) from utilities/globalDefinitions.hpp */
+    protected static boolean isC1Compile(int compLevel) {
+        return (compLevel > COMP_LEVEL_NONE)
+                && (compLevel < COMP_LEVEL_FULL_OPTIMIZATION);
+    }
+
+    /** copy of is_c2_compile(int) from utilities/globalDefinitions.hpp */
+    protected static boolean isC2Compile(int compLevel) {
+        return compLevel == COMP_LEVEL_FULL_OPTIMIZATION;
+    }
+
+    protected static void main(
+            Function<TestCase, CompilerWhiteBoxTest> constructor,
+            String[] args) {
+        if (args.length == 0) {
+            for (TestCase test : SimpleTestCase.values()) {
+                constructor.apply(test).runTest();
+            }
+        } else {
+            for (String name : args) {
+                constructor.apply(SimpleTestCase.valueOf(name)).runTest();
+            }
+        }
+    }
+
+    /** tested method */
+    protected final Executable method;
+    protected final TestCase testCase;
+
+    /**
+     * Constructor.
+     *
+     * @param testCase object, that contains tested method and way to invoke it.
+     */
+    protected CompilerWhiteBoxTest(TestCase testCase) {
+        Objects.requireNonNull(testCase);
+        System.out.println("TEST CASE:" + testCase.name());
+        method = testCase.getExecutable();
+        this.testCase = testCase;
+    }
+
+    /**
+     * Template method for testing. Prints tested method's info before
+     * {@linkplain #test()} and after {@linkplain #test()} or on thrown
+     * exception.
+     *
+     * @throws RuntimeException if method {@linkplain #test()} throws any
+     *                          exception
+     * @see #test()
+     */
+    protected final void runTest() {
+        if (Platform.isInt()) {
+            throw new Error("TESTBUG: test can not be run in interpreter");
+        }
+        System.out.println("at test's start:");
+        printInfo();
+        try {
+            test();
+        } catch (Exception e) {
+            System.out.printf("on exception '%s':", e.getMessage());
+            printInfo();
+            e.printStackTrace();
+            if (e instanceof RuntimeException) {
+                throw (RuntimeException) e;
+            }
+            throw new RuntimeException(e);
+        }
+        System.out.println("at test's end:");
+        printInfo();
+    }
+
+    /**
+     * Checks, that {@linkplain #method} is not compiled at the given compilation
+     * level or above.
+     *
+     * @param compLevel
+     *
+     * @throws RuntimeException if {@linkplain #method} is in compiler queue or
+     *                          is compiled, or if {@linkplain #method} has zero
+     *                          compilation level.
+     */
+    protected final void checkNotCompiled(int compLevel) {
+        if (WHITE_BOX.isMethodQueuedForCompilation(method)) {
+            throw new RuntimeException(method + " must not be in queue");
+        }
+        if (WHITE_BOX.getMethodCompilationLevel(method, false) >= compLevel) {
+            throw new RuntimeException(method + " comp_level must be >= maxCompLevel");
+        }
+        if (WHITE_BOX.getMethodCompilationLevel(method, true) >= compLevel) {
+            throw new RuntimeException(method + " osr_comp_level must be >= maxCompLevel");
+        }
+    }
+
+    /**
+     * Checks, that {@linkplain #method} is not compiled.
+     *
+     * @throws RuntimeException if {@linkplain #method} is in compiler queue or
+     *                          is compiled, or if {@linkplain #method} has zero
+     *                          compilation level.
+     */
+    protected final void checkNotCompiled() {
+        waitBackgroundCompilation();
+        checkNotCompiled(true);
+        checkNotCompiled(false);
+    }
+
+    /**
+     * Checks, that {@linkplain #method} is not (OSR-)compiled.
+     *
+     * @param isOsr Check for OSR compilation if true
+     * @throws RuntimeException if {@linkplain #method} is in compiler queue or
+     *                          is compiled, or if {@linkplain #method} has zero
+     *                          compilation level.
+     */
+    protected final void checkNotCompiled(boolean isOsr) {
+        if (WHITE_BOX.isMethodQueuedForCompilation(method)) {
+            throw new RuntimeException(method + " must not be in queue");
+        }
+        if (WHITE_BOX.isMethodCompiled(method, isOsr)) {
+            throw new RuntimeException(method + " must not be " +
+                                       (isOsr ? "osr_" : "") + "compiled");
+        }
+        if (WHITE_BOX.getMethodCompilationLevel(method, isOsr) != 0) {
+            throw new RuntimeException(method + (isOsr ? " osr_" : " ") +
+                                       "comp_level must be == 0");
+        }
+    }
+
+    /**
+     * Checks, that {@linkplain #method} is compiled.
+     *
+     * @throws RuntimeException if {@linkplain #method} isn't in compiler queue
+     *                          and isn't compiled, or if {@linkplain #method}
+     *                          has nonzero compilation level
+     */
+    protected final void checkCompiled() {
+        final long start = System.currentTimeMillis();
+        waitBackgroundCompilation();
+        if (WHITE_BOX.isMethodQueuedForCompilation(method)) {
+            System.err.printf("Warning: %s is still in queue after %dms%n",
+                    method, System.currentTimeMillis() - start);
+            return;
+        }
+        if (!WHITE_BOX.isMethodCompiled(method, testCase.isOsr())) {
+            throw new RuntimeException(method + " must be "
+                    + (testCase.isOsr() ? "osr_" : "") + "compiled");
+        }
+        if (WHITE_BOX.getMethodCompilationLevel(method, testCase.isOsr())
+                == 0) {
+            throw new RuntimeException(method
+                    + (testCase.isOsr() ? " osr_" : " ")
+                    + "comp_level must be != 0");
+        }
+    }
+
+    protected final void deoptimize() {
+        WHITE_BOX.deoptimizeMethod(method, testCase.isOsr());
+        if (testCase.isOsr()) {
+            WHITE_BOX.deoptimizeMethod(method, false);
+        }
+    }
+
+    protected final int getCompLevel() {
+        NMethod nm = NMethod.get(method, testCase.isOsr());
+        return nm == null ? COMP_LEVEL_NONE : nm.comp_level;
+    }
+
+    protected final boolean isCompilable() {
+        return WHITE_BOX.isMethodCompilable(method, COMP_LEVEL_ANY,
+                testCase.isOsr());
+    }
+
+    protected final boolean isCompilable(int compLevel) {
+        return WHITE_BOX
+                .isMethodCompilable(method, compLevel, testCase.isOsr());
+    }
+
+    protected final void makeNotCompilable() {
+        WHITE_BOX.makeMethodNotCompilable(method, COMP_LEVEL_ANY,
+                testCase.isOsr());
+    }
+
+    protected final void makeNotCompilable(int compLevel) {
+        WHITE_BOX.makeMethodNotCompilable(method, compLevel, testCase.isOsr());
+    }
+
+    /**
+     * Waits for completion of background compilation of {@linkplain #method}.
+     */
+    protected final void waitBackgroundCompilation() {
+        waitBackgroundCompilation(method);
+    }
+
+    /**
+     * Waits for completion of background compilation of the given executable.
+     *
+     * @param executable Executable
+     */
+    public static final void waitBackgroundCompilation(Executable executable) {
+        if (!BACKGROUND_COMPILATION) {
+            return;
+        }
+        final Object obj = new Object();
+        for (int i = 0; i < 100
+                && WHITE_BOX.isMethodQueuedForCompilation(executable); ++i) {
+            synchronized (obj) {
+                try {
+                    obj.wait(100);
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                }
+            }
+        }
+    }
+
+    /**
+     * Prints information about {@linkplain #method}.
+     */
+    protected final void printInfo() {
+        System.out.printf("%n%s:%n", method);
+        System.out.printf("\tcompilable:\t%b%n",
+                WHITE_BOX.isMethodCompilable(method, COMP_LEVEL_ANY, false));
+        boolean isCompiled = WHITE_BOX.isMethodCompiled(method, false);
+        System.out.printf("\tcompiled:\t%b%n", isCompiled);
+        if (isCompiled) {
+            System.out.printf("\tcompile_id:\t%d%n",
+                    NMethod.get(method, false).compile_id);
+        }
+        System.out.printf("\tcomp_level:\t%d%n",
+                WHITE_BOX.getMethodCompilationLevel(method, false));
+        System.out.printf("\tosr_compilable:\t%b%n",
+                WHITE_BOX.isMethodCompilable(method, COMP_LEVEL_ANY, true));
+        isCompiled = WHITE_BOX.isMethodCompiled(method, true);
+        System.out.printf("\tosr_compiled:\t%b%n", isCompiled);
+        if (isCompiled) {
+            System.out.printf("\tosr_compile_id:\t%d%n",
+                    NMethod.get(method, true).compile_id);
+        }
+        System.out.printf("\tosr_comp_level:\t%d%n",
+                WHITE_BOX.getMethodCompilationLevel(method, true));
+        System.out.printf("\tin_queue:\t%b%n",
+                WHITE_BOX.isMethodQueuedForCompilation(method));
+        System.out.printf("compile_queues_size:\t%d%n%n",
+                WHITE_BOX.getCompileQueuesSize());
+    }
+
+    /**
+     * Executes testing.
+     */
+    protected abstract void test() throws Exception;
+
+    /**
+     * Tries to trigger compilation of {@linkplain #method} by call
+     * {@linkplain TestCase#getCallable()} enough times.
+     *
+     * @return accumulated result
+     * @see #compile(int)
+     */
+    protected final int compile() throws Exception {
+        if (USE_COUNTER_DECAY) {
+            throw new Exception("Tests using compile method must turn off counter decay for reliability");
+        }
+        if (testCase.isOsr()) {
+            return compile(1);
+        } else {
+            return compile(THRESHOLD);
+        }
+    }
+
+    /**
+     * Tries to trigger compilation of {@linkplain #method} by call
+     * {@linkplain TestCase#getCallable()} specified times.
+     *
+     * @param count invocation count
+     * @return accumulated result
+     */
+    protected final int compile(int count) {
+        int result = 0;
+        Integer tmp;
+        for (int i = 0; i < count; ++i) {
+            try {
+                tmp = testCase.getCallable().call();
+            } catch (Exception e) {
+                tmp = null;
+            }
+            result += tmp == null ? 0 : tmp;
+        }
+        if (IS_VERBOSE) {
+            System.out.println("method was invoked " + count + " times");
+        }
+        return result;
+    }
+
+    /**
+     * Utility interface provides tested method and object to invoke it.
+     */
+    public interface TestCase {
+        /** the name of test case */
+        String name();
+
+        /** tested method */
+        Executable getExecutable();
+
+        /** object to invoke {@linkplain #getExecutable()} */
+        Callable<Integer> getCallable();
+
+        /** flag for OSR test case */
+        boolean isOsr();
+    }
+
+    /**
+     * @return {@code true} if the current test case is OSR and the mode is
+     *          Xcomp, otherwise {@code false}
+     */
+    protected boolean skipXcompOSR() {
+        boolean result = testCase.isOsr() && Platform.isComp();
+        if (result && IS_VERBOSE) {
+            System.err.printf("Warning: %s is not applicable in %s%n",
+                    testCase.name(), Platform.vmInfo);
+        }
+        return result;
+    }
+
+    /**
+     * Skip the test for the specified value of Tiered Compilation
+     * @param value of TieredCompilation the test should not run with
+     * @return {@code true} if the test should be skipped,
+     *         {@code false} otherwise
+     */
+    public static boolean skipOnTieredCompilation(boolean value) {
+        if (value == CompilerWhiteBoxTest.TIERED_COMPILATION) {
+            System.err.println("Test isn't applicable w/ "
+                    + (value ? "enabled" : "disabled")
+                    + "TieredCompilation. Skip test.");
+            return true;
+        }
+        return false;
+    }
+}
+