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