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