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