1 /*
  2  * Copyright (c) 2013, 2018, 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 /**
 25  * These tests are explicitly testing the profiling behavior of the
 26  * interpreter. C1-based profiling differs slightly and when -Xcomp
 27  * is present, profiles will be created by C1 compiled code, not the
 28  * interpreter.
 29  *
 30  * @test
 31  * @requires vm.jvmci
 32  * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel > 1)
 33  * @requires vm.compMode != "Xcomp"
 34  * @requires vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel > 1
 35  * @modules jdk.internal.vm.ci/jdk.vm.ci.meta
 36  *          jdk.internal.vm.ci/jdk.vm.ci.runtime
 37  * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:-UseJVMCICompiler -Xbootclasspath/a:. compiler.jvmci.meta.ProfilingInfoTest
 38  */
 39 package compiler.jvmci.meta;
 40 
 41 import java.io.Serializable;
 42 import java.lang.reflect.Constructor;
 43 import java.lang.reflect.Executable;
 44 import java.lang.reflect.InvocationTargetException;
 45 import java.lang.reflect.Method;
 46 
 47 import org.junit.Assert;
 48 import org.junit.Assume;
 49 import org.junit.Test;
 50 
 51 import jdk.vm.ci.meta.JavaTypeProfile;
 52 import jdk.vm.ci.meta.MetaAccessProvider;
 53 import jdk.vm.ci.meta.ProfilingInfo;
 54 import jdk.vm.ci.meta.ResolvedJavaMethod;
 55 import jdk.vm.ci.meta.ResolvedJavaType;
 56 import jdk.vm.ci.meta.TriState;
 57 import jdk.vm.ci.runtime.JVMCI;
 58 
 59 /**
 60  * Tests profiling information provided by the runtime.
 61  * <p>
 62  * NOTE: These tests are actually not very robust. The problem is that only partial profiling
 63  * information may be gathered for any given method. For example, HotSpot's advanced compilation
 64  * policy can decide to only gather partial profiles in a first level compilation (see
 65  * AdvancedThresholdPolicy::common(...) in advancedThresholdPolicy.cpp). Because of this,
 66  * occasionally tests for {@link ProfilingInfo#getNullSeen(int)} can fail since HotSpot only sets
 67  * the null_seen bit when doing full profiling.
 68  */
 69 public class ProfilingInfoTest {
 70 
 71     private static final int N = 10;
 72     private static final double DELTA = 1d / Integer.MAX_VALUE;
 73 
 74     @Test
 75     public void testBranchTakenProbability() {
 76         ProfilingInfo info = profile("branchProbabilitySnippet", 0);
 77         Assert.assertEquals(0.0, info.getBranchTakenProbability(1), DELTA);
 78         Assert.assertEquals(N, info.getExecutionCount(1));
 79         Assert.assertEquals(-1.0, info.getBranchTakenProbability(8), DELTA);
 80         Assert.assertEquals(0, info.getExecutionCount(8));
 81 
 82         info = profile("branchProbabilitySnippet", 1);
 83         Assert.assertEquals(1.0, info.getBranchTakenProbability(1), DELTA);
 84         Assert.assertEquals(N, info.getExecutionCount(1));
 85         Assert.assertEquals(0.0, info.getBranchTakenProbability(8), DELTA);
 86         Assert.assertEquals(N, info.getExecutionCount(8));
 87 
 88         info = profile("branchProbabilitySnippet", 2);
 89         Assert.assertEquals(1.0, info.getBranchTakenProbability(1), DELTA);
 90         Assert.assertEquals(N, info.getExecutionCount(1));
 91         Assert.assertEquals(1.0, info.getBranchTakenProbability(8), DELTA);
 92         Assert.assertEquals(N, info.getExecutionCount(8));
 93 
 94         continueProfiling(3 * N, "branchProbabilitySnippet", 0);
 95         Assert.assertEquals(0.25, info.getBranchTakenProbability(1), DELTA);
 96         Assert.assertEquals(4 * N, info.getExecutionCount(1));
 97         Assert.assertEquals(1.0, info.getBranchTakenProbability(8), DELTA);
 98         Assert.assertEquals(N, info.getExecutionCount(8));
 99 
100         resetProfile("branchProbabilitySnippet");
101         Assert.assertEquals(-1.0, info.getBranchTakenProbability(1), DELTA);
102         Assert.assertEquals(0, info.getExecutionCount(1));
103         Assert.assertEquals(-1.0, info.getBranchTakenProbability(8), DELTA);
104         Assert.assertEquals(0, info.getExecutionCount(8));
105     }
106 
107     public static int branchProbabilitySnippet(int value) {
108         if (value == 0) {
109             return -1;
110         } else if (value == 1) {
111             return -2;
112         } else {
113             return -3;
114         }
115     }
116 
117     @Test
118     public void testSwitchProbabilities() {
119         ProfilingInfo info = profile("switchProbabilitySnippet", 0);
120         Assert.assertArrayEquals(new double[]{1.0, 0.0, 0.0}, info.getSwitchProbabilities(1), DELTA);
121 
122         info = profile("switchProbabilitySnippet", 1);
123         Assert.assertArrayEquals(new double[]{0.0, 1.0, 0.0}, info.getSwitchProbabilities(1), DELTA);
124 
125         info = profile("switchProbabilitySnippet", 2);
126         Assert.assertArrayEquals(new double[]{0.0, 0.0, 1.0}, info.getSwitchProbabilities(1), DELTA);
127 
128         resetProfile("switchProbabilitySnippet");
129         Assert.assertNull(info.getSwitchProbabilities(1));
130     }
131 
132     public static int switchProbabilitySnippet(int value) {
133         switch (value) {
134             case 0:
135                 return -1;
136             case 1:
137                 return -2;
138             default:
139                 return -3;
140         }
141     }
142 
143     @Test
144     public void testProfileInvokeVirtual() {
145         testTypeProfile("invokeVirtualSnippet", 1);
146     }
147 
148     public static int invokeVirtualSnippet(Object obj) {
149         return obj.hashCode();
150     }
151 
152     @Test
153     public void testTypeProfileInvokeInterface() {
154         testTypeProfile("invokeInterfaceSnippet", 1);
155     }
156 
157     public static int invokeInterfaceSnippet(CharSequence a) {
158         return a.length();
159     }
160 
161     @Test
162     public void testTypeProfileCheckCast() {
163         testTypeProfile("checkCastSnippet", 1);
164     }
165 
166     public static Serializable checkCastSnippet(Object obj) {
167         try {
168             return (Serializable) obj;
169         } catch (ClassCastException e) {
170             return null;
171         }
172     }
173 
174     @Test
175     public void testTypeProfileInstanceOf() {
176         testTypeProfile("instanceOfSnippet", 1);
177     }
178 
179     public static boolean instanceOfSnippet(Object obj) {
180         return obj instanceof Serializable;
181     }
182 
183     private void testTypeProfile(String testSnippet, int bci) {
184         MetaAccessProvider metaAccess = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess();
185         ResolvedJavaType stringType = metaAccess.lookupJavaType(String.class);
186         ResolvedJavaType stringBuilderType = metaAccess.lookupJavaType(StringBuilder.class);
187 
188         ProfilingInfo info = profile(testSnippet, "ABC");
189         JavaTypeProfile typeProfile = info.getTypeProfile(bci);
190         Assert.assertEquals(0.0, typeProfile.getNotRecordedProbability(), DELTA);
191         Assert.assertEquals(1, typeProfile.getTypes().length);
192         Assert.assertEquals(stringType, typeProfile.getTypes()[0].getType());
193         Assert.assertEquals(1.0, typeProfile.getTypes()[0].getProbability(), DELTA);
194 
195         continueProfiling(testSnippet, new StringBuilder());
196         typeProfile = info.getTypeProfile(bci);
197         Assert.assertEquals(0.0, typeProfile.getNotRecordedProbability(), DELTA);
198         Assert.assertEquals(2, typeProfile.getTypes().length);
199         Assert.assertEquals(stringType, typeProfile.getTypes()[0].getType());
200         Assert.assertEquals(stringBuilderType, typeProfile.getTypes()[1].getType());
201         Assert.assertEquals(0.5, typeProfile.getTypes()[0].getProbability(), DELTA);
202         Assert.assertEquals(0.5, typeProfile.getTypes()[1].getProbability(), DELTA);
203 
204         resetProfile(testSnippet);
205         typeProfile = info.getTypeProfile(bci);
206         Assert.assertNull(typeProfile);
207     }
208 
209     public ProfilingInfoTest() {
210     }
211 
212     @Test
213     public void testExceptionSeen() {
214         // NullPointerException
215         ProfilingInfo info = profile("nullPointerExceptionSnippet", 5);
216         Assert.assertEquals(TriState.FALSE, info.getExceptionSeen(1));
217 
218         info = profile("nullPointerExceptionSnippet", (Object) null);
219         Assert.assertEquals(TriState.TRUE, info.getExceptionSeen(1));
220 
221         resetProfile("nullPointerExceptionSnippet");
222         Assert.assertEquals(TriState.FALSE, info.getExceptionSeen(1));
223 
224         // ArrayOutOfBoundsException
225         info = profile("arrayIndexOutOfBoundsExceptionSnippet", new int[1]);
226         Assert.assertEquals(TriState.FALSE, info.getExceptionSeen(2));
227 
228         info = profile("arrayIndexOutOfBoundsExceptionSnippet", new int[0]);
229         Assert.assertEquals(TriState.TRUE, info.getExceptionSeen(2));
230 
231         resetProfile("arrayIndexOutOfBoundsExceptionSnippet");
232         Assert.assertEquals(TriState.FALSE, info.getExceptionSeen(2));
233 
234         // CheckCastException
235         info = profile("checkCastExceptionSnippet", "ABC");
236         Assert.assertEquals(TriState.FALSE, info.getExceptionSeen(1));
237 
238         info = profile("checkCastExceptionSnippet", 5);
239         Assert.assertEquals(TriState.TRUE, info.getExceptionSeen(1));
240 
241         resetProfile("checkCastExceptionSnippet");
242         Assert.assertEquals(TriState.FALSE, info.getExceptionSeen(1));
243 
244         // Invoke with exception
245         info = profile("invokeWithExceptionSnippet", false);
246         Assert.assertEquals(TriState.FALSE, info.getExceptionSeen(1));
247 
248         info = profile("invokeWithExceptionSnippet", true);
249         Assert.assertEquals(TriState.TRUE, info.getExceptionSeen(1));
250 
251         resetProfile("invokeWithExceptionSnippet");
252         Assert.assertEquals(TriState.FALSE, info.getExceptionSeen(1));
253     }
254 
255     public static int nullPointerExceptionSnippet(Object obj) {
256         try {
257             return obj.hashCode();
258         } catch (NullPointerException e) {
259             return 1;
260         }
261     }
262 
263     public static int arrayIndexOutOfBoundsExceptionSnippet(int[] array) {
264         try {
265             return array[0];
266         } catch (ArrayIndexOutOfBoundsException e) {
267             return 1;
268         }
269     }
270 
271     public static int checkCastExceptionSnippet(Object obj) {
272         try {
273             return ((String) obj).length();
274         } catch (ClassCastException e) {
275             return 1;
276         }
277     }
278 
279     public static int invokeWithExceptionSnippet(boolean doThrow) {
280         try {
281             return throwException(doThrow);
282         } catch (IllegalArgumentException e) {
283             return 1;
284         }
285     }
286 
287     private static int throwException(boolean doThrow) {
288         if (doThrow) {
289             throw new IllegalArgumentException();
290         } else {
291             return 1;
292         }
293     }
294 
295     @Test
296     public void testNullSeen() {
297         testNullSeen("instanceOfSnippet");
298         testNullSeen("checkCastSnippet");
299     }
300 
301     private void testNullSeen(String snippet) {
302         ProfilingInfo info = profile(snippet, 1);
303         Assert.assertEquals(TriState.FALSE, info.getNullSeen(1));
304 
305         continueProfiling(snippet, "ABC");
306         Assert.assertEquals(TriState.FALSE, info.getNullSeen(1));
307 
308         continueProfiling(snippet, new Object());
309         Assert.assertEquals(TriState.FALSE, info.getNullSeen(1));
310 
311         if (TriState.TRUE == info.getNullSeen(1)) {
312             // See the javadoc comment for ProfilingInfoTest.
313             continueProfiling(snippet, (Object) null);
314             Assert.assertEquals(TriState.TRUE, info.getNullSeen(1));
315 
316             continueProfiling(snippet, 0.0);
317             Assert.assertEquals(TriState.TRUE, info.getNullSeen(1));
318 
319             continueProfiling(snippet, new Object());
320             Assert.assertEquals(TriState.TRUE, info.getNullSeen(1));
321         }
322 
323         resetProfile(snippet);
324         Assert.assertEquals(TriState.FALSE, info.getNullSeen(1));
325     }
326 
327     private ProfilingInfo profile(String methodName, Object... args) {
328         return profile(true, N, methodName, args);
329     }
330 
331     private void continueProfiling(String methodName, Object... args) {
332         profile(false, N, methodName, args);
333     }
334 
335     private void continueProfiling(int executions, String methodName, Object... args) {
336         profile(false, executions, methodName, args);
337     }
338 
339     private ProfilingInfo profile(boolean resetProfile, int executions, String methodName, Object... args) {
340         MetaAccessProvider metaAccess = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess();
341         Method method = getMethod(methodName);
342         ResolvedJavaMethod javaMethod = metaAccess.lookupJavaMethod(method);
343         Assert.assertTrue(javaMethod.isStatic());
344         if (resetProfile) {
345             javaMethod.reprofile();
346         }
347 
348         for (int i = 0; i < executions; ++i) {
349             try {
350                 method.invoke(null, args);
351             } catch (Throwable e) {
352                 Assert.fail("method should not throw an exception: " + e.toString());
353             }
354         }
355 
356         ProfilingInfo info = javaMethod.getProfilingInfo();
357         // The execution counts are low so force maturity
358         info.setMature();
359         return info;
360     }
361 
362     static Method getMethod(String methodName) {
363         for (Method method : ProfilingInfoTest.class.getDeclaredMethods()) {
364             if (method.getName().equals(methodName)) {
365                 return method;
366             }
367         }
368         throw new IllegalArgumentException();
369     }
370 
371     private void resetProfile(String methodName) {
372         MetaAccessProvider metaAccess = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess();
373         ResolvedJavaMethod javaMethod = metaAccess.lookupJavaMethod(getMethod(methodName));
374         javaMethod.reprofile();
375     }
376 }