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