iignatyev@4592: /* iignatyev@4592: * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. iignatyev@4592: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. iignatyev@4592: * iignatyev@4592: * This code is free software; you can redistribute it and/or modify it iignatyev@4592: * under the terms of the GNU General Public License version 2 only, as iignatyev@4592: * published by the Free Software Foundation. iignatyev@4592: * iignatyev@4592: * This code is distributed in the hope that it will be useful, but WITHOUT iignatyev@4592: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or iignatyev@4592: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License iignatyev@4592: * version 2 for more details (a copy is included in the LICENSE file that iignatyev@4592: * accompanied this code). iignatyev@4592: * iignatyev@4592: * You should have received a copy of the GNU General Public License version iignatyev@4592: * 2 along with this work; if not, write to the Free Software Foundation, iignatyev@4592: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. iignatyev@4592: * iignatyev@4592: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA iignatyev@4592: * or visit www.oracle.com if you need additional information or have any iignatyev@4592: * questions. iignatyev@4592: */ iignatyev@4592: iignatyev@4951: import com.sun.management.HotSpotDiagnosticMXBean; iignatyev@4951: import com.sun.management.VMOption; iignatyev@4592: import sun.hotspot.WhiteBox; iignatyev@4592: import sun.management.ManagementFactoryHelper; iignatyev@4592: iignatyev@4951: import java.lang.reflect.Constructor; iignatyev@4951: import java.lang.reflect.Executable; iignatyev@4592: import java.lang.reflect.Method; iignatyev@4951: import java.util.Objects; iignatyev@4951: import java.util.concurrent.Callable; iignatyev@4592: iignatyev@4951: /** iignatyev@4951: * Abstract class for WhiteBox testing of JIT. iignatyev@4951: * iignatyev@4592: * @author igor.ignatyev@oracle.com iignatyev@4592: */ iignatyev@4592: public abstract class CompilerWhiteBoxTest { iignatyev@4951: /** {@code CompLevel::CompLevel_none} -- Interpreter */ iignatyev@4951: protected static int COMP_LEVEL_NONE = 0; iignatyev@4951: /** {@code CompLevel::CompLevel_any}, {@code CompLevel::CompLevel_all} */ iignatyev@4951: protected static int COMP_LEVEL_ANY = -1; iignatyev@5032: /** {@code CompLevel::CompLevel_simple} -- C1 */ iignatyev@5032: protected static int COMP_LEVEL_SIMPLE = 1; iignatyev@5032: /** {@code CompLevel::CompLevel_full_optimization} -- C2 or Shark */ iignatyev@5032: protected static int COMP_LEVEL_FULL_OPTIMIZATION = 4; iignatyev@5032: iignatyev@4951: /** Instance of WhiteBox */ iignatyev@4592: protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); iignatyev@4951: /** Value of {@code -XX:CompileThreshold} */ iignatyev@4592: protected static final int COMPILE_THRESHOLD iignatyev@4592: = Integer.parseInt(getVMOption("CompileThreshold", "10000")); iignatyev@4951: /** Value of {@code -XX:BackgroundCompilation} */ iignatyev@4766: protected static final boolean BACKGROUND_COMPILATION iignatyev@4766: = Boolean.valueOf(getVMOption("BackgroundCompilation", "true")); iignatyev@4951: /** Value of {@code -XX:TieredCompilation} */ iignatyev@4908: protected static final boolean TIERED_COMPILATION iignatyev@4908: = Boolean.valueOf(getVMOption("TieredCompilation", "false")); iignatyev@4951: /** Value of {@code -XX:TieredStopAtLevel} */ iignatyev@4951: protected static final int TIERED_STOP_AT_LEVEL iignatyev@4951: = Integer.parseInt(getVMOption("TieredStopAtLevel", "0")); iignatyev@4592: iignatyev@4951: /** iignatyev@4951: * Returns value of VM option. iignatyev@4951: * iignatyev@4951: * @param name option's name iignatyev@4951: * @return value of option or {@code null}, if option doesn't exist iignatyev@4951: * @throws NullPointerException if name is null iignatyev@4951: */ iignatyev@4951: protected static String getVMOption(String name) { iignatyev@4951: Objects.requireNonNull(name); iignatyev@4951: HotSpotDiagnosticMXBean diagnostic iignatyev@4951: = ManagementFactoryHelper.getDiagnosticMXBean(); iignatyev@4951: VMOption tmp; iignatyev@4592: try { iignatyev@4951: tmp = diagnostic.getVMOption(name); iignatyev@4951: } catch (IllegalArgumentException e) { iignatyev@4951: tmp = null; iignatyev@4592: } iignatyev@4951: return (tmp == null ? null : tmp.getValue()); iignatyev@4592: } iignatyev@4592: iignatyev@4951: /** iignatyev@4951: * Returns value of VM option or default value. iignatyev@4951: * iignatyev@4951: * @param name option's name iignatyev@4951: * @param defaultValue default value iignatyev@4951: * @return value of option or {@code defaultValue}, if option doesn't exist iignatyev@4951: * @throws NullPointerException if name is null iignatyev@4951: * @see #getVMOption(String) iignatyev@4951: */ iignatyev@4766: protected static String getVMOption(String name, String defaultValue) { iignatyev@4766: String result = getVMOption(name); iignatyev@4592: return result == null ? defaultValue : result; iignatyev@4592: } iignatyev@4592: iignatyev@5032: /** copy of is_c1_compile(int) from utilities/globalDefinitions.hpp */ iignatyev@5032: protected static boolean isC1Compile(int compLevel) { iignatyev@5032: return (compLevel > COMP_LEVEL_NONE) iignatyev@5032: && (compLevel < COMP_LEVEL_FULL_OPTIMIZATION); iignatyev@5032: } iignatyev@5032: iignatyev@5032: /** copy of is_c2_compile(int) from utilities/globalDefinitions.hpp */ iignatyev@5032: protected static boolean isC2Compile(int compLevel) { iignatyev@5032: return compLevel == COMP_LEVEL_FULL_OPTIMIZATION; iignatyev@5032: } iignatyev@5032: iignatyev@4951: /** tested method */ iignatyev@4951: protected final Executable method; iignatyev@4951: private final Callable callable; iignatyev@4951: iignatyev@4951: /** iignatyev@4951: * Constructor. iignatyev@4951: * iignatyev@4951: * @param testCase object, that contains tested method and way to invoke it. iignatyev@4951: */ iignatyev@4951: protected CompilerWhiteBoxTest(TestCase testCase) { iignatyev@4951: Objects.requireNonNull(testCase); iignatyev@4951: System.out.println("TEST CASE:" + testCase.name()); iignatyev@4951: method = testCase.executable; iignatyev@4951: callable = testCase.callable; iignatyev@4951: } iignatyev@4951: iignatyev@4951: /** iignatyev@4951: * Template method for testing. Prints tested method's info before iignatyev@4951: * {@linkplain #test()} and after {@linkplain #test()} or on thrown iignatyev@4951: * exception. iignatyev@4951: * iignatyev@4951: * @throws RuntimeException if method {@linkplain #test()} throws any iignatyev@4951: * exception iignatyev@4951: * @see #test() iignatyev@4951: */ iignatyev@4951: protected final void runTest() { iignatyev@4592: if (ManagementFactoryHelper.getCompilationMXBean() == null) { iignatyev@4592: System.err.println( iignatyev@4592: "Warning: test is not applicable in interpreted mode"); iignatyev@4592: return; iignatyev@4592: } iignatyev@4592: System.out.println("at test's start:"); iignatyev@4951: printInfo(); iignatyev@4592: try { iignatyev@4592: test(); iignatyev@4592: } catch (Exception e) { iignatyev@4592: System.out.printf("on exception '%s':", e.getMessage()); iignatyev@4951: printInfo(); iignatyev@4766: e.printStackTrace(); iignatyev@4951: if (e instanceof RuntimeException) { iignatyev@4951: throw (RuntimeException) e; iignatyev@4951: } iignatyev@4592: throw new RuntimeException(e); iignatyev@4592: } iignatyev@4592: System.out.println("at test's end:"); iignatyev@4951: printInfo(); iignatyev@4592: } iignatyev@4592: iignatyev@4951: /** iignatyev@4951: * Checks, that {@linkplain #method} is not compiled. iignatyev@4951: * iignatyev@4951: * @throws RuntimeException if {@linkplain #method} is in compiler queue or iignatyev@4951: * is compiled, or if {@linkplain #method} has zero iignatyev@4951: * compilation level. iignatyev@4951: */ iignatyev@4951: protected final void checkNotCompiled() { iignatyev@4908: if (WHITE_BOX.isMethodQueuedForCompilation(method)) { iignatyev@4908: throw new RuntimeException(method + " must not be in queue"); iignatyev@4908: } iignatyev@4592: if (WHITE_BOX.isMethodCompiled(method)) { iignatyev@4592: throw new RuntimeException(method + " must be not compiled"); iignatyev@4592: } iignatyev@4592: if (WHITE_BOX.getMethodCompilationLevel(method) != 0) { iignatyev@4592: throw new RuntimeException(method + " comp_level must be == 0"); iignatyev@4592: } iignatyev@4592: } iignatyev@4592: iignatyev@4951: /** iignatyev@4951: * Checks, that {@linkplain #method} is compiled. iignatyev@4951: * iignatyev@4951: * @throws RuntimeException if {@linkplain #method} isn't in compiler queue iignatyev@4951: * and isn't compiled, or if {@linkplain #method} iignatyev@4951: * has nonzero compilation level iignatyev@4951: */ iignatyev@4951: protected final void checkCompiled() { iignatyev@4592: final long start = System.currentTimeMillis(); iignatyev@4951: waitBackgroundCompilation(); iignatyev@4592: if (WHITE_BOX.isMethodQueuedForCompilation(method)) { iignatyev@4592: System.err.printf("Warning: %s is still in queue after %dms%n", iignatyev@4592: method, System.currentTimeMillis() - start); iignatyev@4592: return; iignatyev@4592: } iignatyev@4592: if (!WHITE_BOX.isMethodCompiled(method)) { iignatyev@4592: throw new RuntimeException(method + " must be compiled"); iignatyev@4592: } iignatyev@4592: if (WHITE_BOX.getMethodCompilationLevel(method) == 0) { iignatyev@4592: throw new RuntimeException(method + " comp_level must be != 0"); iignatyev@4592: } iignatyev@4592: } iignatyev@4592: iignatyev@4951: /** iignatyev@4951: * Waits for completion of background compilation of {@linkplain #method}. iignatyev@4951: */ iignatyev@4951: protected final void waitBackgroundCompilation() { iignatyev@4766: if (!BACKGROUND_COMPILATION) { iignatyev@4766: return; iignatyev@4766: } iignatyev@4592: final Object obj = new Object(); iignatyev@4951: for (int i = 0; i < 10 iignatyev@4951: && WHITE_BOX.isMethodQueuedForCompilation(method); ++i) { iignatyev@4951: synchronized (obj) { iignatyev@4951: try { iignatyev@4951: obj.wait(1000); iignatyev@4951: } catch (InterruptedException e) { iignatyev@4951: Thread.currentThread().interrupt(); iignatyev@4592: } iignatyev@4592: } iignatyev@4592: } iignatyev@4592: } iignatyev@4592: iignatyev@4951: /** iignatyev@4951: * Prints information about {@linkplain #method}. iignatyev@4951: */ iignatyev@4951: protected final void printInfo() { iignatyev@4592: System.out.printf("%n%s:%n", method); iignatyev@4592: System.out.printf("\tcompilable:\t%b%n", iignatyev@4592: WHITE_BOX.isMethodCompilable(method)); iignatyev@4592: System.out.printf("\tcompiled:\t%b%n", iignatyev@4592: WHITE_BOX.isMethodCompiled(method)); iignatyev@4592: System.out.printf("\tcomp_level:\t%d%n", iignatyev@4592: WHITE_BOX.getMethodCompilationLevel(method)); iignatyev@4592: System.out.printf("\tin_queue:\t%b%n", iignatyev@4592: WHITE_BOX.isMethodQueuedForCompilation(method)); iignatyev@4592: System.out.printf("compile_queues_size:\t%d%n%n", iignatyev@4592: WHITE_BOX.getCompileQueuesSize()); iignatyev@4592: } iignatyev@4592: iignatyev@4951: /** iignatyev@4951: * Executes testing. iignatyev@4951: */ iignatyev@4592: protected abstract void test() throws Exception; iignatyev@4592: iignatyev@4951: /** iignatyev@4951: * Tries to trigger compilation of {@linkplain #method} by call iignatyev@4951: * {@linkplain #callable} enough times. iignatyev@4951: * iignatyev@4951: * @return accumulated result iignatyev@4951: * @see #compile(int) iignatyev@4951: */ iignatyev@4592: protected final int compile() { iignatyev@4908: return compile(Math.max(COMPILE_THRESHOLD, 150000)); iignatyev@4908: } iignatyev@4908: iignatyev@4951: /** iignatyev@4951: * Tries to trigger compilation of {@linkplain #method} by call iignatyev@4951: * {@linkplain #callable} specified times. iignatyev@4951: * iignatyev@4951: * @param count invocation count iignatyev@4951: * @return accumulated result iignatyev@4951: */ iignatyev@4908: protected final int compile(int count) { iignatyev@4592: int result = 0; iignatyev@4951: Integer tmp; iignatyev@4766: for (int i = 0; i < count; ++i) { iignatyev@4951: try { iignatyev@4951: tmp = callable.call(); iignatyev@4951: } catch (Exception e) { iignatyev@4951: tmp = null; iignatyev@4951: } iignatyev@4951: result += tmp == null ? 0 : tmp; iignatyev@4592: } iignatyev@4766: System.out.println("method was invoked " + count + " times"); iignatyev@4592: return result; iignatyev@4592: } iignatyev@4951: } iignatyev@4592: iignatyev@4951: /** iignatyev@4951: * Utility structure containing tested method and object to invoke it. iignatyev@4951: */ iignatyev@4951: enum TestCase { iignatyev@4951: /** constructor test case */ iignatyev@4951: CONSTRUCTOR_TEST(Helper.CONSTRUCTOR, Helper.CONSTRUCTOR_CALLABLE), iignatyev@4951: /** method test case */ iignatyev@4951: METOD_TEST(Helper.METHOD, Helper.METHOD_CALLABLE), iignatyev@4951: /** static method test case */ iignatyev@4951: STATIC_TEST(Helper.STATIC, Helper.STATIC_CALLABLE); iignatyev@4951: iignatyev@4951: /** tested method */ iignatyev@4951: final Executable executable; iignatyev@4951: /** object to invoke {@linkplain #executable} */ iignatyev@4951: final Callable callable; iignatyev@4951: iignatyev@4951: private TestCase(Executable executable, Callable callable) { iignatyev@4951: this.executable = executable; iignatyev@4951: this.callable = callable; iignatyev@4951: } iignatyev@4951: iignatyev@4951: private static class Helper { iignatyev@4951: private static final Callable CONSTRUCTOR_CALLABLE iignatyev@4951: = new Callable() { iignatyev@4951: @Override iignatyev@4951: public Integer call() throws Exception { iignatyev@4951: return new Helper(1337).hashCode(); iignatyev@4951: } iignatyev@4951: }; iignatyev@4951: iignatyev@4951: private static final Callable METHOD_CALLABLE iignatyev@4951: = new Callable() { iignatyev@4951: private final Helper helper = new Helper(); iignatyev@4951: iignatyev@4951: @Override iignatyev@4951: public Integer call() throws Exception { iignatyev@4951: return helper.method(); iignatyev@4951: } iignatyev@4951: }; iignatyev@4951: iignatyev@4951: private static final Callable STATIC_CALLABLE iignatyev@4951: = new Callable() { iignatyev@4951: @Override iignatyev@4951: public Integer call() throws Exception { iignatyev@4951: return staticMethod(); iignatyev@4951: } iignatyev@4951: }; iignatyev@4951: iignatyev@4951: private static final Constructor CONSTRUCTOR; iignatyev@4951: private static final Method METHOD; iignatyev@4951: private static final Method STATIC; iignatyev@4951: iignatyev@4951: static { iignatyev@4951: try { iignatyev@4951: CONSTRUCTOR = Helper.class.getDeclaredConstructor(int.class); iignatyev@4951: } catch (NoSuchMethodException | SecurityException e) { iignatyev@4951: throw new RuntimeException( iignatyev@4951: "exception on getting method Helper.(int)", e); iignatyev@4951: } iignatyev@4951: try { iignatyev@4951: METHOD = Helper.class.getDeclaredMethod("method"); iignatyev@4951: } catch (NoSuchMethodException | SecurityException e) { iignatyev@4951: throw new RuntimeException( iignatyev@4951: "exception on getting method Helper.method()", e); iignatyev@4951: } iignatyev@4951: try { iignatyev@4951: STATIC = Helper.class.getDeclaredMethod("staticMethod"); iignatyev@4951: } catch (NoSuchMethodException | SecurityException e) { iignatyev@4951: throw new RuntimeException( iignatyev@4951: "exception on getting method Helper.staticMethod()", e); iignatyev@4951: } iignatyev@4951: } iignatyev@4951: iignatyev@4951: private static int staticMethod() { iignatyev@4951: return 1138; iignatyev@4951: } iignatyev@4951: iignatyev@4951: private int method() { iignatyev@4951: return 42; iignatyev@4951: } iignatyev@4951: iignatyev@4951: private final int x; iignatyev@4951: iignatyev@4951: public Helper() { iignatyev@4951: x = 0; iignatyev@4951: } iignatyev@4951: iignatyev@4951: private Helper(int x) { iignatyev@4951: this.x = x; iignatyev@4951: } iignatyev@4951: iignatyev@4951: @Override iignatyev@4951: public int hashCode() { iignatyev@4951: return x; iignatyev@4951: } iignatyev@4592: } iignatyev@4592: }