1 /* 2 * Copyright (c) 2013, 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 import com.sun.management.HotSpotDiagnosticMXBean; 25 import com.sun.management.VMOption; 26 import sun.hotspot.WhiteBox; 27 import sun.management.ManagementFactoryHelper; 28 29 import java.lang.reflect.Constructor; 30 import java.lang.reflect.Executable; 31 import java.lang.reflect.Method; 32 import java.util.Objects; 33 import java.util.concurrent.Callable; 34 35 /** 36 * Abstract class for WhiteBox testing of JIT. 37 * 38 * @author igor.ignatyev@oracle.com 39 */ 40 public abstract class CompilerWhiteBoxTest { 41 /** {@code CompLevel::CompLevel_none} -- Interpreter */ 42 protected static int COMP_LEVEL_NONE = 0; 43 /** {@code CompLevel::CompLevel_any}, {@code CompLevel::CompLevel_all} */ 44 protected static int COMP_LEVEL_ANY = -1; 45 /** {@code CompLevel::CompLevel_simple} -- C1 */ 46 protected static int COMP_LEVEL_SIMPLE = 1; 47 /** {@code CompLevel::CompLevel_full_optimization} -- C2 or Shark */ 48 protected static int COMP_LEVEL_FULL_OPTIMIZATION = 4; 49 50 /** Instance of WhiteBox */ 51 protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); 52 /** Value of {@code -XX:CompileThreshold} */ 53 protected static final int COMPILE_THRESHOLD 54 = Integer.parseInt(getVMOption("CompileThreshold", "10000")); 55 /** Value of {@code -XX:BackgroundCompilation} */ 56 protected static final boolean BACKGROUND_COMPILATION 57 = Boolean.valueOf(getVMOption("BackgroundCompilation", "true")); 58 /** Value of {@code -XX:TieredCompilation} */ 59 protected static final boolean TIERED_COMPILATION 60 = Boolean.valueOf(getVMOption("TieredCompilation", "false")); 61 /** Value of {@code -XX:TieredStopAtLevel} */ 62 protected static final int TIERED_STOP_AT_LEVEL 63 = Integer.parseInt(getVMOption("TieredStopAtLevel", "0")); 64 65 /** 66 * Returns value of VM option. 67 * 68 * @param name option's name 69 * @return value of option or {@code null}, if option doesn't exist 70 * @throws NullPointerException if name is null 71 */ 72 protected static String getVMOption(String name) { 73 Objects.requireNonNull(name); 74 HotSpotDiagnosticMXBean diagnostic 75 = ManagementFactoryHelper.getDiagnosticMXBean(); 76 VMOption tmp; 77 try { 78 tmp = diagnostic.getVMOption(name); 79 } catch (IllegalArgumentException e) { 80 tmp = null; 81 } 82 return (tmp == null ? null : tmp.getValue()); 83 } 84 85 /** 86 * Returns value of VM option or default value. 87 * 88 * @param name option's name 89 * @param defaultValue default value 90 * @return value of option or {@code defaultValue}, if option doesn't exist 91 * @throws NullPointerException if name is null 92 * @see #getVMOption(String) 93 */ 94 protected static String getVMOption(String name, String defaultValue) { 95 String result = getVMOption(name); 96 return result == null ? defaultValue : result; 97 } 98 99 /** copy of is_c1_compile(int) from utilities/globalDefinitions.hpp */ 100 protected static boolean isC1Compile(int compLevel) { 101 return (compLevel > COMP_LEVEL_NONE) 102 && (compLevel < COMP_LEVEL_FULL_OPTIMIZATION); 103 } 104 105 /** copy of is_c2_compile(int) from utilities/globalDefinitions.hpp */ 106 protected static boolean isC2Compile(int compLevel) { 107 return compLevel == COMP_LEVEL_FULL_OPTIMIZATION; 108 } 109 110 /** tested method */ 111 protected final Executable method; 112 private final Callable<Integer> callable; 113 114 /** 115 * Constructor. 116 * 117 * @param testCase object, that contains tested method and way to invoke it. 118 */ 119 protected CompilerWhiteBoxTest(TestCase testCase) { 120 Objects.requireNonNull(testCase); 121 System.out.println("TEST CASE:" + testCase.name()); 122 method = testCase.executable; 123 callable = testCase.callable; 124 } 125 126 /** 127 * Template method for testing. Prints tested method's info before 128 * {@linkplain #test()} and after {@linkplain #test()} or on thrown 129 * exception. 130 * 131 * @throws RuntimeException if method {@linkplain #test()} throws any 132 * exception 133 * @see #test() 134 */ 135 protected final void runTest() { 136 if (ManagementFactoryHelper.getCompilationMXBean() == null) { 137 System.err.println( 138 "Warning: test is not applicable in interpreted mode"); 139 return; 140 } 141 System.out.println("at test's start:"); 142 printInfo(); 143 try { 144 test(); 145 } catch (Exception e) { 146 System.out.printf("on exception '%s':", e.getMessage()); 147 printInfo(); 148 e.printStackTrace(); 149 if (e instanceof RuntimeException) { 150 throw (RuntimeException) e; 151 } 152 throw new RuntimeException(e); 153 } 154 System.out.println("at test's end:"); 155 printInfo(); 156 } 157 158 /** 159 * Checks, that {@linkplain #method} is not compiled. 160 * 161 * @throws RuntimeException if {@linkplain #method} is in compiler queue or 162 * is compiled, or if {@linkplain #method} has zero 163 * compilation level. 164 */ 165 protected final void checkNotCompiled() { 166 if (WHITE_BOX.isMethodQueuedForCompilation(method)) { 167 throw new RuntimeException(method + " must not be in queue"); 168 } 169 if (WHITE_BOX.isMethodCompiled(method)) { 170 throw new RuntimeException(method + " must be not compiled"); 171 } 172 if (WHITE_BOX.getMethodCompilationLevel(method) != 0) { 173 throw new RuntimeException(method + " comp_level must be == 0"); 174 } 175 } 176 177 /** 178 * Checks, that {@linkplain #method} is compiled. 179 * 180 * @throws RuntimeException if {@linkplain #method} isn't in compiler queue 181 * and isn't compiled, or if {@linkplain #method} 182 * has nonzero compilation level 183 */ 184 protected final void checkCompiled() { 185 final long start = System.currentTimeMillis(); 186 waitBackgroundCompilation(); 187 if (WHITE_BOX.isMethodQueuedForCompilation(method)) { 188 System.err.printf("Warning: %s is still in queue after %dms%n", 189 method, System.currentTimeMillis() - start); 190 return; 191 } 192 if (!WHITE_BOX.isMethodCompiled(method)) { 193 throw new RuntimeException(method + " must be compiled"); 194 } 195 if (WHITE_BOX.getMethodCompilationLevel(method) == 0) { 196 throw new RuntimeException(method + " comp_level must be != 0"); 197 } 198 } 199 200 /** 201 * Waits for completion of background compilation of {@linkplain #method}. 202 */ 203 protected final void waitBackgroundCompilation() { 204 if (!BACKGROUND_COMPILATION) { 205 return; 206 } 207 final Object obj = new Object(); 208 for (int i = 0; i < 10 209 && WHITE_BOX.isMethodQueuedForCompilation(method); ++i) { 210 synchronized (obj) { 211 try { 212 obj.wait(1000); 213 } catch (InterruptedException e) { 214 Thread.currentThread().interrupt(); 215 } 216 } 217 } 218 } 219 220 /** 221 * Prints information about {@linkplain #method}. 222 */ 223 protected final void printInfo() { 224 System.out.printf("%n%s:%n", method); 225 System.out.printf("\tcompilable:\t%b%n", 226 WHITE_BOX.isMethodCompilable(method)); 227 System.out.printf("\tcompiled:\t%b%n", 228 WHITE_BOX.isMethodCompiled(method)); 229 System.out.printf("\tcomp_level:\t%d%n", 230 WHITE_BOX.getMethodCompilationLevel(method)); 231 System.out.printf("\tin_queue:\t%b%n", 232 WHITE_BOX.isMethodQueuedForCompilation(method)); 233 System.out.printf("compile_queues_size:\t%d%n%n", 234 WHITE_BOX.getCompileQueuesSize()); 235 } 236 237 /** 238 * Executes testing. 239 */ 240 protected abstract void test() throws Exception; 241 242 /** 243 * Tries to trigger compilation of {@linkplain #method} by call 244 * {@linkplain #callable} enough times. 245 * 246 * @return accumulated result 247 * @see #compile(int) 248 */ 249 protected final int compile() { 250 return compile(Math.max(COMPILE_THRESHOLD, 150000)); 251 } 252 253 /** 254 * Tries to trigger compilation of {@linkplain #method} by call 255 * {@linkplain #callable} specified times. 256 * 257 * @param count invocation count 258 * @return accumulated result 259 */ 260 protected final int compile(int count) { 261 int result = 0; 262 Integer tmp; 263 for (int i = 0; i < count; ++i) { 264 try { 265 tmp = callable.call(); 266 } catch (Exception e) { 267 tmp = null; 268 } 269 result += tmp == null ? 0 : tmp; 270 } 271 System.out.println("method was invoked " + count + " times"); 272 return result; 273 } 274 } 275 276 /** 277 * Utility structure containing tested method and object to invoke it. 278 */ 279 enum TestCase { 280 /** constructor test case */ 281 CONSTRUCTOR_TEST(Helper.CONSTRUCTOR, Helper.CONSTRUCTOR_CALLABLE), 282 /** method test case */ 283 METOD_TEST(Helper.METHOD, Helper.METHOD_CALLABLE), 284 /** static method test case */ 285 STATIC_TEST(Helper.STATIC, Helper.STATIC_CALLABLE); 286 287 /** tested method */ 288 final Executable executable; 289 /** object to invoke {@linkplain #executable} */ 290 final Callable<Integer> callable; 291 292 private TestCase(Executable executable, Callable<Integer> callable) { 293 this.executable = executable; 294 this.callable = callable; 295 } 296 297 private static class Helper { 298 private static final Callable<Integer> CONSTRUCTOR_CALLABLE 299 = new Callable<Integer>() { 300 @Override 301 public Integer call() throws Exception { 302 return new Helper(1337).hashCode(); 303 } 304 }; 305 306 private static final Callable<Integer> METHOD_CALLABLE 307 = new Callable<Integer>() { 308 private final Helper helper = new Helper(); 309 310 @Override 311 public Integer call() throws Exception { 312 return helper.method(); 313 } 314 }; 315 316 private static final Callable<Integer> STATIC_CALLABLE 317 = new Callable<Integer>() { 318 @Override 319 public Integer call() throws Exception { 320 return staticMethod(); 321 } 322 }; 323 324 private static final Constructor CONSTRUCTOR; 325 private static final Method METHOD; 326 private static final Method STATIC; 327 328 static { 329 try { 330 CONSTRUCTOR = Helper.class.getDeclaredConstructor(int.class); 331 } catch (NoSuchMethodException | SecurityException e) { 332 throw new RuntimeException( 333 "exception on getting method Helper.<init>(int)", e); 334 } 335 try { 336 METHOD = Helper.class.getDeclaredMethod("method"); 337 } catch (NoSuchMethodException | SecurityException e) { 338 throw new RuntimeException( 339 "exception on getting method Helper.method()", e); 340 } 341 try { 342 STATIC = Helper.class.getDeclaredMethod("staticMethod"); 343 } catch (NoSuchMethodException | SecurityException e) { 344 throw new RuntimeException( 345 "exception on getting method Helper.staticMethod()", e); 346 } 347 } 348 349 private static int staticMethod() { 350 return 1138; 351 } 352 353 private int method() { 354 return 42; 355 } 356 357 private final int x; 358 359 public Helper() { 360 x = 0; 361 } 362 363 private Helper(int x) { 364 this.x = x; 365 } 366 367 @Override 368 public int hashCode() { 369 return x; 370 } 371 } 372 }