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