001/*
002 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.compiler.test;
024
025import java.io.*;
026
027import jdk.internal.jvmci.meta.*;
028
029import org.junit.*;
030
031/**
032 * Tests profiling information provided by the runtime.
033 * <p>
034 * NOTE: These tests are actually not very robust. The problem is that only partial profiling
035 * information may be gathered for any given method. For example, HotSpot's advanced compilation
036 * policy can decide to only gather partial profiles in a first level compilation (see
037 * AdvancedThresholdPolicy::common(...) in advancedThresholdPolicy.cpp). Because of this,
038 * occasionally tests for {@link ProfilingInfo#getNullSeen(int)} can fail since HotSpot only set's
039 * the null_seen bit when doing full profiling.
040 */
041public class ProfilingInfoTest extends GraalCompilerTest {
042
043    private static final int N = 10;
044    private static final double DELTA = 1d / Integer.MAX_VALUE;
045
046    @Test
047    public void testBranchTakenProbability() {
048        ProfilingInfo info = profile("branchProbabilitySnippet", 0);
049        Assert.assertEquals(0.0, info.getBranchTakenProbability(1), DELTA);
050        Assert.assertEquals(N, info.getExecutionCount(1));
051        Assert.assertEquals(-1.0, info.getBranchTakenProbability(8), DELTA);
052        Assert.assertEquals(0, info.getExecutionCount(8));
053
054        info = profile("branchProbabilitySnippet", 1);
055        Assert.assertEquals(1.0, info.getBranchTakenProbability(1), DELTA);
056        Assert.assertEquals(N, info.getExecutionCount(1));
057        Assert.assertEquals(0.0, info.getBranchTakenProbability(8), DELTA);
058        Assert.assertEquals(N, info.getExecutionCount(8));
059
060        info = profile("branchProbabilitySnippet", 2);
061        Assert.assertEquals(1.0, info.getBranchTakenProbability(1), DELTA);
062        Assert.assertEquals(N, info.getExecutionCount(1));
063        Assert.assertEquals(1.0, info.getBranchTakenProbability(8), DELTA);
064        Assert.assertEquals(N, info.getExecutionCount(8));
065
066        continueProfiling(3 * N, "branchProbabilitySnippet", 0);
067        Assert.assertEquals(0.25, info.getBranchTakenProbability(1), DELTA);
068        Assert.assertEquals(4 * N, info.getExecutionCount(1));
069        Assert.assertEquals(1.0, info.getBranchTakenProbability(8), DELTA);
070        Assert.assertEquals(N, info.getExecutionCount(8));
071
072        resetProfile("branchProbabilitySnippet");
073        Assert.assertEquals(-1.0, info.getBranchTakenProbability(1), DELTA);
074        Assert.assertEquals(0, info.getExecutionCount(1));
075        Assert.assertEquals(-1.0, info.getBranchTakenProbability(8), DELTA);
076        Assert.assertEquals(0, info.getExecutionCount(8));
077    }
078
079    public static int branchProbabilitySnippet(int value) {
080        if (value == 0) {
081            return -1;
082        } else if (value == 1) {
083            return -2;
084        } else {
085            return -3;
086        }
087    }
088
089    @Test
090    public void testSwitchProbabilities() {
091        ProfilingInfo info = profile("switchProbabilitySnippet", 0);
092        Assert.assertArrayEquals(new double[]{1.0, 0.0, 0.0}, info.getSwitchProbabilities(1), DELTA);
093
094        info = profile("switchProbabilitySnippet", 1);
095        Assert.assertArrayEquals(new double[]{0.0, 1.0, 0.0}, info.getSwitchProbabilities(1), DELTA);
096
097        info = profile("switchProbabilitySnippet", 2);
098        Assert.assertArrayEquals(new double[]{0.0, 0.0, 1.0}, info.getSwitchProbabilities(1), DELTA);
099
100        resetProfile("switchProbabilitySnippet");
101        Assert.assertNull(info.getSwitchProbabilities(1));
102    }
103
104    public static int switchProbabilitySnippet(int value) {
105        switch (value) {
106            case 0:
107                return -1;
108            case 1:
109                return -2;
110            default:
111                return -3;
112        }
113    }
114
115    @Test
116    public void testProfileInvokeVirtual() {
117        testTypeProfile("invokeVirtualSnippet", 1);
118    }
119
120    public static int invokeVirtualSnippet(Object obj) {
121        return obj.hashCode();
122    }
123
124    @Test
125    public void testTypeProfileInvokeInterface() {
126        testTypeProfile("invokeInterfaceSnippet", 1);
127    }
128
129    public static int invokeInterfaceSnippet(CharSequence a) {
130        return a.length();
131    }
132
133    @Test
134    public void testTypeProfileCheckCast() {
135        testTypeProfile("checkCastSnippet", 1);
136    }
137
138    public static Serializable checkCastSnippet(Object obj) {
139        try {
140            return (Serializable) obj;
141        } catch (ClassCastException e) {
142            return null;
143        }
144    }
145
146    @Test
147    public void testTypeProfileInstanceOf() {
148        testTypeProfile("instanceOfSnippet", 1);
149    }
150
151    public static boolean instanceOfSnippet(Object obj) {
152        return obj instanceof Serializable;
153    }
154
155    private void testTypeProfile(String testSnippet, int bci) {
156        ResolvedJavaType stringType = getMetaAccess().lookupJavaType(String.class);
157        ResolvedJavaType stringBuilderType = getMetaAccess().lookupJavaType(StringBuilder.class);
158
159        ProfilingInfo info = profile(testSnippet, "ABC");
160        JavaTypeProfile typeProfile = info.getTypeProfile(bci);
161        Assert.assertEquals(0.0, typeProfile.getNotRecordedProbability(), DELTA);
162        Assert.assertEquals(1, typeProfile.getTypes().length);
163        Assert.assertEquals(stringType, typeProfile.getTypes()[0].getType());
164        Assert.assertEquals(1.0, typeProfile.getTypes()[0].getProbability(), DELTA);
165
166        continueProfiling(testSnippet, new StringBuilder());
167        typeProfile = info.getTypeProfile(bci);
168        Assert.assertEquals(0.0, typeProfile.getNotRecordedProbability(), DELTA);
169        Assert.assertEquals(2, typeProfile.getTypes().length);
170        Assert.assertEquals(stringType, typeProfile.getTypes()[0].getType());
171        Assert.assertEquals(stringBuilderType, typeProfile.getTypes()[1].getType());
172        Assert.assertEquals(0.5, typeProfile.getTypes()[0].getProbability(), DELTA);
173        Assert.assertEquals(0.5, typeProfile.getTypes()[1].getProbability(), DELTA);
174
175        resetProfile(testSnippet);
176        typeProfile = info.getTypeProfile(bci);
177        Assert.assertNull(typeProfile);
178    }
179
180    @Test
181    public void testExceptionSeen() {
182        // NullPointerException
183        ProfilingInfo info = profile("nullPointerExceptionSnippet", 5);
184        Assert.assertEquals(TriState.FALSE, info.getExceptionSeen(1));
185
186        info = profile("nullPointerExceptionSnippet", (Object) null);
187        Assert.assertEquals(TriState.TRUE, info.getExceptionSeen(1));
188
189        resetProfile("nullPointerExceptionSnippet");
190        Assert.assertEquals(TriState.FALSE, info.getExceptionSeen(1));
191
192        // ArrayOutOfBoundsException
193        info = profile("arrayIndexOutOfBoundsExceptionSnippet", new int[1]);
194        Assert.assertEquals(TriState.FALSE, info.getExceptionSeen(2));
195
196        info = profile("arrayIndexOutOfBoundsExceptionSnippet", new int[0]);
197        Assert.assertEquals(TriState.TRUE, info.getExceptionSeen(2));
198
199        resetProfile("arrayIndexOutOfBoundsExceptionSnippet");
200        Assert.assertEquals(TriState.FALSE, info.getExceptionSeen(2));
201
202        // CheckCastException
203        info = profile("checkCastExceptionSnippet", "ABC");
204        Assert.assertEquals(TriState.FALSE, info.getExceptionSeen(1));
205
206        info = profile("checkCastExceptionSnippet", 5);
207        Assert.assertEquals(TriState.TRUE, info.getExceptionSeen(1));
208
209        resetProfile("checkCastExceptionSnippet");
210        Assert.assertEquals(TriState.FALSE, info.getExceptionSeen(1));
211
212        // Invoke with exception
213        info = profile("invokeWithExceptionSnippet", false);
214        Assert.assertEquals(TriState.FALSE, info.getExceptionSeen(1));
215
216        info = profile("invokeWithExceptionSnippet", true);
217        Assert.assertEquals(TriState.TRUE, info.getExceptionSeen(1));
218
219        resetProfile("invokeWithExceptionSnippet");
220        Assert.assertEquals(TriState.FALSE, info.getExceptionSeen(1));
221    }
222
223    public static int nullPointerExceptionSnippet(Object obj) {
224        try {
225            return obj.hashCode();
226        } catch (NullPointerException e) {
227            return 1;
228        }
229    }
230
231    public static int arrayIndexOutOfBoundsExceptionSnippet(int[] array) {
232        try {
233            return array[0];
234        } catch (ArrayIndexOutOfBoundsException e) {
235            return 1;
236        }
237    }
238
239    public static int checkCastExceptionSnippet(Object obj) {
240        try {
241            return ((String) obj).length();
242        } catch (ClassCastException e) {
243            return 1;
244        }
245    }
246
247    public static int invokeWithExceptionSnippet(boolean doThrow) {
248        try {
249            return throwException(doThrow);
250        } catch (IllegalArgumentException e) {
251            return 1;
252        }
253    }
254
255    private static int throwException(boolean doThrow) {
256        if (doThrow) {
257            throw new IllegalArgumentException();
258        } else {
259            return 1;
260        }
261    }
262
263    @Test
264    public void testNullSeen() {
265        testNullSeen("instanceOfSnippet");
266        testNullSeen("checkCastSnippet");
267    }
268
269    private void testNullSeen(String snippet) {
270        ProfilingInfo info = profile(snippet, 1);
271        Assert.assertEquals(TriState.FALSE, info.getNullSeen(1));
272
273        continueProfiling(snippet, "ABC");
274        Assert.assertEquals(TriState.FALSE, info.getNullSeen(1));
275
276        continueProfiling(snippet, new Object());
277        Assert.assertEquals(TriState.FALSE, info.getNullSeen(1));
278
279        if (TriState.TRUE == info.getNullSeen(1)) {
280            // See the javadoc comment for ProfilingInfoTest.
281            continueProfiling(snippet, (Object) null);
282            Assert.assertEquals(TriState.TRUE, info.getNullSeen(1));
283
284            continueProfiling(snippet, 0.0);
285            Assert.assertEquals(TriState.TRUE, info.getNullSeen(1));
286
287            continueProfiling(snippet, new Object());
288            Assert.assertEquals(TriState.TRUE, info.getNullSeen(1));
289        }
290
291        resetProfile(snippet);
292        Assert.assertEquals(TriState.FALSE, info.getNullSeen(1));
293    }
294
295    private ProfilingInfo profile(String methodName, Object... args) {
296        return profile(true, N, methodName, args);
297    }
298
299    private void continueProfiling(String methodName, Object... args) {
300        profile(false, N, methodName, args);
301    }
302
303    private void continueProfiling(int executions, String methodName, Object... args) {
304        profile(false, executions, methodName, args);
305    }
306
307    private ProfilingInfo profile(boolean resetProfile, int executions, String methodName, Object... args) {
308        ResolvedJavaMethod javaMethod = getResolvedJavaMethod(methodName);
309        Assert.assertTrue(javaMethod.isStatic());
310        if (resetProfile) {
311            javaMethod.reprofile();
312        }
313
314        for (int i = 0; i < executions; ++i) {
315            try {
316                invoke(javaMethod, null, args);
317            } catch (Throwable e) {
318                Assert.fail("method should not throw an exception: " + e.toString());
319            }
320        }
321
322        ProfilingInfo info = javaMethod.getProfilingInfo();
323        // The execution counts are low so force maturity
324        info.setMature();
325        return info;
326    }
327
328    private void resetProfile(String methodName) {
329        ResolvedJavaMethod javaMethod = getResolvedJavaMethod(methodName);
330        javaMethod.reprofile();
331    }
332}