1 /* 2 * Copyright Amazon.com Inc. 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 * @test 26 * @bug 8275908 27 * @summary Record null_check traps for calls and array_check traps in the interpreter 28 * 29 * @requires vm.compiler2.enabled & vm.compMode != "Xcomp" 30 * @requires vm.opt.DeoptimizeALot != true 31 * 32 * @library /test/lib 33 * @build jdk.test.whitebox.WhiteBox 34 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox 35 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 36 * -Xbatch -XX:-UseOnStackReplacement -XX:-TieredCompilation 37 * -XX:CompileCommand=compileonly,compiler.exceptions.OptimizeImplicitExceptions::throwImplicitException 38 * compiler.exceptions.OptimizeImplicitExceptions 39 */ 40 41 package compiler.exceptions; 42 43 import java.lang.reflect.Method; 44 import java.util.HashMap; 45 46 import jdk.test.lib.Asserts; 47 import jdk.test.whitebox.WhiteBox; 48 49 public class OptimizeImplicitExceptions { 50 // ImplicitException represents the various implicit (aka. 'built-in') exceptions 51 // which can be thrown implicitely by the JVM when executing bytecodes. 52 public enum ImplicitException { 53 // NullPointerException during field access 54 NULL_POINTER_EXCEPTION("null_check"), 55 // NullPointerException during invoke 56 INVOKE_NULL_POINTER_EXCEPTION("null_check"), 57 ARITHMETIC_EXCEPTION("div0_check"), 58 ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION("range_check"), 59 ARRAY_STORE_EXCEPTION("array_check"), 60 CLASS_CAST_EXCEPTION("class_check"); 61 private final String reason; 62 ImplicitException(String reason) { 63 this.reason = reason; 64 } 65 public String getReason() { 66 return reason; 67 } 68 } 69 // TestMode represents a specific combination of the OmitStackTraceInFastThrow command line options. 70 // They will be set up in 'setFlags(TestMode testMode)' before a new test run starts. 71 public enum TestMode { 72 OMIT_STACKTRACES_IN_FASTTHROW, 73 STACKTRACES_IN_FASTTHROW 74 } 75 76 private static final WhiteBox WB = WhiteBox.getWhiteBox(); 77 // The number of deoptimizations after which a method will be made not-entrant 78 private static final int PerBytecodeTrapLimit = WB.getIntxVMFlag("PerBytecodeTrapLimit").intValue(); 79 // The number of interpreter invocations after which a decompiled method will be re-compiled. 80 private static final int Tier0InvokeNotifyFreq = (int)Math.pow(2, WB.getIntxVMFlag("Tier0InvokeNotifyFreqLog")); 81 // The following variables are used to track the value of the global deopt counters between the various test phases. 82 private static int oldDeoptCount = 0; 83 private static HashMap<String, Integer> oldDeoptCountReason = new HashMap<String, Integer>(ImplicitException.values().length); 84 // The following two objects are declared statically to simplify the test method. 85 private static String[] string_a = new String[1]; 86 private static final Object o = new Object(); 87 88 // This is the main test method. It will repeatedly called with the same ImplicitException 'type' to 89 // JIT-compile it, deoptimized it, re-compile it again and do various checks on the way. 90 // This process will be repeated then for each kind of ImplicitException 'type'. 91 public static Object throwImplicitException(ImplicitException type, Object[] object_a) { 92 switch (type) { 93 case NULL_POINTER_EXCEPTION: { 94 return object_a.length; 95 } 96 case INVOKE_NULL_POINTER_EXCEPTION: { 97 return object_a.hashCode(); 98 } 99 case ARITHMETIC_EXCEPTION: { 100 return ((42 / (object_a.length - 1)) > 2) ? null : object_a[0]; 101 } 102 case ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION: { 103 return object_a[5]; 104 } 105 case ARRAY_STORE_EXCEPTION: { 106 return (object_a[0] = o); 107 } 108 case CLASS_CAST_EXCEPTION: { 109 return (ImplicitException[])object_a; 110 } 111 } 112 return null; 113 } 114 115 // Completely unload (i.e. make "not-entrant"->free) a JIT-compiled 116 // version of a method and clear the method's profiling counters. 117 private static void unloadAndClean(Method m) { 118 WB.deoptimizeMethod(m); // Makes the nmethod "not entrant". 119 System.gc(); 120 WB.clearMethodState(m); 121 } 122 123 // Set '-XX' flags according to 'TestMode' 124 private static void setFlags(TestMode testMode) { 125 if (testMode == TestMode.OMIT_STACKTRACES_IN_FASTTHROW) { 126 WB.setBooleanVMFlag("OmitStackTraceInFastThrow", true); 127 } else { 128 WB.setBooleanVMFlag("OmitStackTraceInFastThrow", false); 129 } 130 131 System.out.println("=========================================================="); 132 System.out.println("testMode=" + testMode + 133 " OmitStackTraceInFastThrow=" + WB.getBooleanVMFlag("OmitStackTraceInFastThrow")); 134 System.out.println("=========================================================="); 135 } 136 137 private static void printCounters(TestMode testMode, ImplicitException impExcp, Method throwImplicitException_m, int invocations) { 138 System.out.println("testMode=" + testMode + " exception=" + impExcp + " invocations=" + invocations + "\n" + 139 "decompilecount=" + WB.getMethodDecompileCount(throwImplicitException_m) + " " + 140 "trapCount=" + WB.getMethodTrapCount(throwImplicitException_m) + " " + 141 "trapCount(" + impExcp.getReason() + ")=" + 142 WB.getMethodTrapCount(throwImplicitException_m, impExcp.getReason()) + " " + 143 "globalDeoptCount=" + WB.getDeoptCount() + " " + 144 "globalDeoptCount(" + impExcp.getReason() + ")=" + WB.getDeoptCount(impExcp.getReason(), null)); 145 System.out.println("method compiled=" + WB.isMethodCompiled(throwImplicitException_m)); 146 } 147 148 // Checks after the test method has been JIT-compiled but before the compiled version has been invoked. 149 private static void checkSimple(TestMode testMode, ImplicitException impExcp, Exception ex, Method throwImplicitException_m, int invocations) { 150 151 printCounters(testMode, impExcp, throwImplicitException_m, invocations); 152 // At this point, throwImplicitException() has been compiled but the compiled version has not been invoked yet. 153 Asserts.assertEQ(WB.getMethodCompilationLevel(throwImplicitException_m), 4, "Method should be compiled at level 4."); 154 155 int trapCount = WB.getMethodTrapCount(throwImplicitException_m); 156 int trapCountSpecific = WB.getMethodTrapCount(throwImplicitException_m, impExcp.getReason()); 157 Asserts.assertEQ(trapCount, invocations, "Trap count must much invocation count."); 158 Asserts.assertEQ(trapCountSpecific, invocations, "Trap count must much invocation count."); 159 Asserts.assertNotNull(ex.getMessage(), "Exceptions thrown in the interpreter should have a message."); 160 } 161 162 // Checks after the JIT-compiled test method has been invoked 'invocations' times. 163 private static void check(TestMode testMode, ImplicitException impExcp, Exception ex, 164 Method throwImplicitException_m, int invocations, int totalInvocations) { 165 166 printCounters(testMode, impExcp, throwImplicitException_m, totalInvocations); 167 // At this point, the compiled version of 'throwImplicitException()' has been invoked 'invocations' times. 168 Asserts.assertEQ(WB.getMethodCompilationLevel(throwImplicitException_m), 4, "Method should be compiled at level 4."); 169 int deoptCount = WB.getDeoptCount(); 170 int deoptCountReason = WB.getDeoptCount(impExcp.getReason(), null/*action*/); 171 if (testMode == TestMode.OMIT_STACKTRACES_IN_FASTTHROW) { 172 // No deoptimizations for '-XX:+OmitStackTraceInFastThrow' 173 Asserts.assertEQ(oldDeoptCount, deoptCount, "Wrong number of deoptimizations."); 174 Asserts.assertEQ(oldDeoptCountReason.get(impExcp.getReason()), deoptCountReason, "Wrong number of deoptimizations."); 175 // '-XX:+OmitStackTraceInFastThrow' never has message because it is using a global singleton exception. 176 Asserts.assertNull(ex.getMessage(), "Optimized exceptions have no message."); 177 } else if (testMode == TestMode.STACKTRACES_IN_FASTTHROW) { 178 // We always deoptimize for '-XX:-OmitStackTraceInFastThrow 179 Asserts.assertEQ(oldDeoptCount + invocations, deoptCount, "Wrong number of deoptimizations."); 180 Asserts.assertEQ(oldDeoptCountReason.get(impExcp.getReason()) + invocations, deoptCountReason, "Wrong number of deoptimizations."); 181 Asserts.assertNotNull(ex.getMessage(), "Exceptions thrown in the interpreter should have a message."); 182 } else { 183 Asserts.fail("Unknown test mode."); 184 } 185 oldDeoptCount = deoptCount; 186 oldDeoptCountReason.put(impExcp.getReason(), deoptCountReason); 187 } 188 189 public static void main(String[] args) throws Exception { 190 191 if (!WB.getBooleanVMFlag("ProfileTraps")) { 192 // The fast-throw optimzation only works if we're running with -XX:+ProfileTraps 193 return; 194 } 195 196 // The following options are both develop, or nops in product build. 197 // If they are set, disable them for test stability. It's fine because we use /othervm above. 198 WB.setBooleanVMFlag("DeoptimizeALot", false); 199 WB.setBooleanVMFlag("DeoptimizeRandom", false); 200 201 // Initialize global deopt counts to zero. 202 for (ImplicitException impExcp : ImplicitException.values()) { 203 oldDeoptCountReason.put(impExcp.getReason(), 0); 204 } 205 // Get a handle of the test method for usage with the WhiteBox API. 206 Method throwImplicitException_m = OptimizeImplicitExceptions.class 207 .getDeclaredMethod("throwImplicitException", new Class[] { ImplicitException.class, Object[].class}); 208 209 for (TestMode testMode : TestMode.values()) { 210 setFlags(testMode); 211 for (ImplicitException impExcp : ImplicitException.values()) { 212 int invocations = 0; 213 Exception lastException = null; 214 215 // Warmup and compile, but don't invoke compiled code. 216 while(!WB.isMethodCompiled(throwImplicitException_m)) { 217 invocations++; 218 try { 219 throwImplicitException(impExcp, impExcp.getReason().equals("null_check") ? null : string_a); 220 } catch (Exception catchedExcp) { 221 lastException = catchedExcp; 222 continue; 223 } 224 throw new Exception("Should not happen"); 225 } 226 227 checkSimple(testMode, impExcp, lastException, throwImplicitException_m, invocations); 228 229 // Invoke compiled code 'PerBytecodeTrapLimit' times. 230 for (int i = 0; i < PerBytecodeTrapLimit; i++) { 231 invocations++; 232 try { 233 throwImplicitException(impExcp, impExcp.getReason().equals("null_check") ? null : string_a); 234 } catch (Exception catchedExcp) { 235 lastException = catchedExcp; 236 continue; 237 } 238 throw new Exception("Should not happen"); 239 } 240 241 check(testMode, impExcp, lastException, throwImplicitException_m, PerBytecodeTrapLimit, invocations); 242 243 // Invoke compiled code 'Tier0InvokeNotifyFreq' times. 244 // If the method was de-compiled before, this will re-compile it again. 245 for (int i = 0; i < Tier0InvokeNotifyFreq; i++) { 246 invocations++; 247 try { 248 throwImplicitException(impExcp, impExcp.getReason().equals("null_check") ? null : string_a); 249 } catch (Exception catchedExcp) { 250 lastException = catchedExcp; 251 continue; 252 } 253 throw new Exception("Should not happen"); 254 } 255 256 check(testMode, impExcp, lastException, throwImplicitException_m, Tier0InvokeNotifyFreq, invocations); 257 258 // Invoke compiled code 'PerBytecodeTrapLimit' times. 259 for (int i = 0; i < PerBytecodeTrapLimit; i++) { 260 invocations++; 261 try { 262 throwImplicitException(impExcp, impExcp.getReason().equals("null_check") ? null : string_a); 263 } catch (Exception catchedExcp) { 264 lastException = catchedExcp; 265 continue; 266 } 267 throw new Exception("Should not happen"); 268 } 269 270 check(testMode, impExcp, lastException, throwImplicitException_m, PerBytecodeTrapLimit, invocations); 271 272 System.out.println("------------------------------------------------------------------"); 273 274 unloadAndClean(throwImplicitException_m); 275 } 276 } 277 } 278 }