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