1 /* 2 * Copyright (c) 2012, 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.debug; 26 27 import static org.graalvm.compiler.debug.DebugContext.BASIC_LEVEL; 28 29 import java.io.ByteArrayOutputStream; 30 import java.io.PrintStream; 31 import java.util.Arrays; 32 import java.util.Collection; 33 import java.util.Collections; 34 import java.util.IdentityHashMap; 35 import java.util.List; 36 import java.util.Map; 37 38 import org.graalvm.compiler.options.OptionValues; 39 40 import jdk.vm.ci.code.BailoutException; 41 import jdk.vm.ci.meta.JavaMethod; 42 43 final class DebugConfigImpl implements DebugConfig { 44 45 private final OptionValues options; 46 47 private final DebugFilter countFilter; 48 private final DebugFilter logFilter; 49 private final DebugFilter trackMemUseFilter; 50 private final DebugFilter timerFilter; 51 private final DebugFilter dumpFilter; 52 private final DebugFilter verifyFilter; 53 private final MethodFilter[] methodFilter; 54 private final List<DebugDumpHandler> dumpHandlers; 55 private final List<DebugVerifyHandler> verifyHandlers; 56 private final PrintStream output; 57 58 DebugConfigImpl(OptionValues options) { 59 this(options, TTY.out, Collections.emptyList(), Collections.emptyList()); 60 } 61 62 DebugConfigImpl(OptionValues options, PrintStream output, 63 List<DebugDumpHandler> dumpHandlers, 64 List<DebugVerifyHandler> verifyHandlers) { 65 this(options, DebugOptions.Log.getValue(options), 66 DebugOptions.Count.getValue(options), 67 DebugOptions.TrackMemUse.getValue(options), 68 DebugOptions.Time.getValue(options), 69 DebugOptions.Dump.getValue(options), 70 getVerifyOptionValue(options), 71 DebugOptions.MethodFilter.getValue(options), 72 output, dumpHandlers, verifyHandlers); 73 } 74 75 DebugConfigImpl(OptionValues options, 76 String logFilter, 77 String countFilter, 78 String trackMemUseFilter, 79 String timerFilter, 80 String dumpFilter, 81 String verifyFilter, 82 String methodFilter, 83 PrintStream output, 84 List<DebugDumpHandler> dumpHandlers, 85 List<DebugVerifyHandler> verifyHandlers) { 86 this.options = options; 87 this.logFilter = DebugFilter.parse(logFilter); 88 this.countFilter = DebugFilter.parse(countFilter); 89 this.trackMemUseFilter = DebugFilter.parse(trackMemUseFilter); 90 this.timerFilter = DebugFilter.parse(timerFilter); 91 this.dumpFilter = DebugFilter.parse(dumpFilter); 92 this.verifyFilter = DebugFilter.parse(verifyFilter); 93 if (methodFilter == null || methodFilter.isEmpty()) { 94 this.methodFilter = null; 95 } else { 96 this.methodFilter = org.graalvm.compiler.debug.MethodFilter.parse(methodFilter); 97 } 98 99 this.dumpHandlers = Collections.unmodifiableList(dumpHandlers); 100 this.verifyHandlers = Collections.unmodifiableList(verifyHandlers); 101 this.output = output; 102 } 103 104 private static String getVerifyOptionValue(OptionValues values) { 105 return !DebugOptions.Verify.hasBeenSet(values) && Assertions.assertionsEnabled() ? "" : DebugOptions.Verify.getValue(values); 106 } 107 108 @Override 109 public OptionValues getOptions() { 110 return options; 111 } 112 113 @Override 114 public int getLogLevel(DebugContext.Scope scope) { 115 return getLevel(scope, logFilter); 116 } 117 118 @Override 119 public boolean isLogEnabledForMethod(DebugContext.Scope scope) { 120 return isEnabledForMethod(scope, logFilter); 121 } 122 123 @Override 124 public boolean isCountEnabled(DebugContext.Scope scope) { 125 return isEnabled(scope, countFilter); 126 } 127 128 @Override 129 public boolean isMemUseTrackingEnabled(DebugContext.Scope scope) { 130 return isEnabled(scope, trackMemUseFilter); 131 } 132 133 @Override 134 public int getDumpLevel(DebugContext.Scope scope) { 135 return getLevel(scope, dumpFilter); 136 } 137 138 @Override 139 public boolean isDumpEnabledForMethod(DebugContext.Scope scope) { 140 return isEnabledForMethod(scope, dumpFilter); 141 } 142 143 @Override 144 public boolean isVerifyEnabled(DebugContext.Scope scope) { 145 return isEnabled(scope, verifyFilter); 146 } 147 148 @Override 149 public boolean isVerifyEnabledForMethod(DebugContext.Scope scope) { 150 return isEnabledForMethod(scope, verifyFilter); 151 } 152 153 @Override 154 public boolean isTimeEnabled(DebugContext.Scope scope) { 155 return isEnabled(scope, timerFilter); 156 } 157 158 @Override 159 public PrintStream output() { 160 return output; 161 } 162 163 private boolean isEnabled(DebugContext.Scope scope, DebugFilter filter) { 164 return getLevel(scope, filter) > 0; 165 } 166 167 private int getLevel(DebugContext.Scope scope, DebugFilter filter) { 168 int level; 169 if (filter == null) { 170 level = 0; 171 } else { 172 String currentScope = scope.getQualifiedName(); 173 level = filter.matchLevel(currentScope); 174 } 175 if (level >= 0 && !checkMethodFilter(scope)) { 176 level = -1; 177 } 178 return level; 179 } 180 181 private boolean isEnabledForMethod(DebugContext.Scope scope, DebugFilter filter) { 182 return filter != null && checkMethodFilter(scope); 183 } 184 185 private boolean checkMethodFilter(DebugContext.Scope scope) { 186 if (methodFilter == null) { 187 return true; 188 } else { 189 JavaMethod lastMethod = null; 190 Iterable<Object> context = scope.getCurrentContext(); 191 for (Object o : context) { 192 if (methodFilter != null) { 193 JavaMethod method = DebugConfig.asJavaMethod(o); 194 if (method != null) { 195 if (!DebugOptions.MethodFilterRootOnly.getValue(options)) { 196 if (org.graalvm.compiler.debug.MethodFilter.matches(methodFilter, method)) { 197 return true; 198 } 199 } else { 200 /* 201 * The context values operate as a stack so if we want MethodFilter to 202 * only apply to the root method we have to check only the last method 203 * seen. 204 */ 205 lastMethod = method; 206 } 207 } 208 } 209 } 210 if (lastMethod != null && org.graalvm.compiler.debug.MethodFilter.matches(methodFilter, lastMethod)) { 211 return true; 212 } 213 return false; 214 } 215 } 216 217 @Override 218 public String toString() { 219 StringBuilder sb = new StringBuilder(); 220 sb.append("Debug config:"); 221 add(sb, "Log", logFilter); 222 add(sb, "Count", countFilter); 223 add(sb, "Time", timerFilter); 224 add(sb, "Dump", dumpFilter); 225 add(sb, "MethodFilter", methodFilter); 226 return sb.toString(); 227 } 228 229 private static void add(StringBuilder sb, String name, Object filter) { 230 if (filter != null) { 231 sb.append(' '); 232 sb.append(name); 233 sb.append('='); 234 if (filter instanceof Object[]) { 235 sb.append(Arrays.toString((Object[]) filter)); 236 } else { 237 sb.append(String.valueOf(filter)); 238 } 239 } 240 } 241 242 @Override 243 public RuntimeException interceptException(DebugContext debug, Throwable e) { 244 if (e instanceof BailoutException) { 245 final boolean causedByCompilerAssert = e instanceof CausableByCompilerAssert && ((CausableByCompilerAssert) e).isCausedByCompilerAssert(); 246 if (!DebugOptions.InterceptBailout.getValue(options) && !causedByCompilerAssert) { 247 return null; 248 } 249 } 250 251 OptionValues interceptOptions = new OptionValues(options, 252 DebugOptions.Count, null, 253 DebugOptions.Time, null, 254 DebugOptions.TrackMemUse, null, 255 DebugOptions.Verify, null, 256 DebugOptions.Dump, ":" + BASIC_LEVEL, 257 DebugOptions.Log, ":" + BASIC_LEVEL); 258 DebugConfigImpl config = new DebugConfigImpl(interceptOptions, output, dumpHandlers, verifyHandlers); 259 ScopeImpl scope = debug.currentScope; 260 scope.updateFlags(config); 261 try { 262 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 263 e.printStackTrace(new PrintStream(baos)); 264 debug.log("Exception raised in scope %s: %s", debug.getCurrentScopeName(), baos); 265 Map<Object, Object> firstSeen = new IdentityHashMap<>(); 266 for (Object o : debug.context()) { 267 // Only dump a context object once. 268 if (!firstSeen.containsKey(o)) { 269 firstSeen.put(o, o); 270 if (DebugOptions.DumpOnError.getValue(options) || DebugOptions.Dump.getValue(options) != null) { 271 debug.dump(DebugContext.BASIC_LEVEL, o, "Exception: %s", e); 272 } 273 debug.log("Context obj %s", o); 274 } 275 } 276 } finally { 277 scope.updateFlags(this); 278 } 279 return null; 280 } 281 282 @Override 283 public Collection<DebugDumpHandler> dumpHandlers() { 284 return dumpHandlers; 285 } 286 287 @Override 288 public Collection<DebugVerifyHandler> verifyHandlers() { 289 return verifyHandlers; 290 } 291 }