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