1 /* 2 * Copyright (c) 2012, 2015, 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.debug; 24 25 import java.io.PrintStream; 26 import java.util.Arrays; 27 import java.util.Collection; 28 import java.util.Collections; 29 import java.util.IdentityHashMap; 30 import java.util.List; 31 import java.util.Map; 32 import java.util.Set; 33 34 import org.graalvm.compiler.options.Option; 35 import org.graalvm.compiler.options.OptionType; 36 import org.graalvm.compiler.options.OptionValue; 37 38 import jdk.vm.ci.code.BailoutException; 39 import jdk.vm.ci.meta.JavaMethod; 40 41 public class GraalDebugConfig implements DebugConfig { 42 @SuppressWarnings("all") 43 private static boolean assertionsEnabled() { 44 boolean assertionsEnabled = false; 45 assert assertionsEnabled = true; 46 return assertionsEnabled; 47 } 48 49 public static class Options { 50 // @formatter:off 51 @Option(help = "Pattern for scope(s) in which dumping is enabled (see DebugFilter and Debug.dump)", type = OptionType.Debug) 52 public static final OptionValue<String> Dump = new OptionValue<>(null); 53 @Option(help = "Pattern for scope(s) in which counting is enabled (see DebugFilter and Debug.counter). " + 54 "An empty value enables all counters unconditionally.", type = OptionType.Debug) 55 public static final OptionValue<String> Count = new OptionValue<>(null); 56 @Option(help = "Pattern for scope(s) in which verification is enabled (see DebugFilter and Debug.verify).", type = OptionType.Debug) 57 public static final OptionValue<String> Verify = new OptionValue<String>() { 58 @Override 59 protected String defaultValue() { 60 return assertionsEnabled() ? "" : null; 61 } 62 }; 63 @Option(help = "Pattern for scope(s) in which memory use tracking is enabled (see DebugFilter and Debug.counter). " + 64 "An empty value enables all memory use trackers unconditionally.", type = OptionType.Debug) 65 public static final OptionValue<String> TrackMemUse = new OptionValue<>(null); 66 @Option(help = "Pattern for scope(s) in which timing is enabled (see DebugFilter and Debug.timer). " + 67 "An empty value enables all timers unconditionally.", type = OptionType.Debug) 68 public static final OptionValue<String> Time = new OptionValue<>(null); 69 @Option(help = "Pattern for scope(s) in which logging is enabled (see DebugFilter and Debug.log)", type = OptionType.Debug) 70 public static final OptionValue<String> Log = new OptionValue<>(null); 71 @Option(help = "Pattern for filtering debug scope output based on method context (see MethodFilter)", type = OptionType.Debug) 72 public static final OptionValue<String> MethodFilter = new OptionValue<>(null); 73 @Option(help = "Only check MethodFilter against the root method in the context if true, otherwise check all methods", type = OptionType.Debug) 74 public static final OptionValue<Boolean> MethodFilterRootOnly = new OptionValue<>(false); 75 76 @Option(help = "How to print counters and timing values:%n" + 77 "Name - aggregate by unqualified name%n" + 78 "Partial - aggregate by partially qualified name (e.g., A.B.C.D.Counter and X.Y.Z.D.Counter will be merged to D.Counter)%n" + 79 "Complete - aggregate by qualified name%n" + 80 "Thread - aggregate by qualified name and thread", type = OptionType.Debug) 81 public static final OptionValue<String> DebugValueSummary = new OptionValue<>("Name"); 82 @Option(help = "Print counters and timers in a human readable form.", type = OptionType.Debug) 83 public static final OptionValue<Boolean> DebugValueHumanReadable = new OptionValue<>(true); 84 @Option(help = "Omit reporting 0-value counters", type = OptionType.Debug) 85 public static final OptionValue<Boolean> SuppressZeroDebugValues = new OptionValue<>(true); 86 @Option(help = "Only report debug values for maps which match the regular expression.", type = OptionType.Debug) 87 public static final OptionValue<String> DebugValueThreadFilter = new OptionValue<>(null); 88 @Option(help = "Write debug values into a file instead of the terminal. " + 89 "If DebugValueSummary is Thread, the thread name will be prepended.", type = OptionType.Debug) 90 public static final OptionValue<String> DebugValueFile = new OptionValue<>(null); 91 @Option(help = "Send Graal compiler IR to dump handlers on error", type = OptionType.Debug) 92 public static final OptionValue<Boolean> DumpOnError = new OptionValue<>(false); 93 @Option(help = "Intercept also bailout exceptions", type = OptionType.Debug) 94 public static final OptionValue<Boolean> InterceptBailout = new OptionValue<>(false); 95 @Option(help = "Enable more verbose log output when available", type = OptionType.Debug) 96 public static final OptionValue<Boolean> LogVerbose = new OptionValue<>(false); 97 98 @Option(help = "The directory where various Graal dump files are written.") 99 public static final OptionValue<String> DumpPath = new OptionValue<>("."); 100 101 @Option(help = "Enable dumping to the C1Visualizer. Enabling this option implies PrintBackendCFG.", type = OptionType.Debug) 102 public static final OptionValue<Boolean> PrintCFG = new OptionValue<>(false); 103 @Option(help = "Enable dumping LIR, register allocation and code generation info to the C1Visualizer.", type = OptionType.Debug) 104 public static final OptionValue<Boolean> PrintBackendCFG = new OptionValue<>(true); 105 @Option(help = "Base filename when dumping C1Visualizer output to files.", type = OptionType.Debug) 106 public static final OptionValue<String> PrintCFGFileName = new OptionValue<>("compilations"); 107 108 @Option(help = "Output probabilities for fixed nodes during binary graph dumping", type = OptionType.Debug) 109 public static final OptionValue<Boolean> PrintGraphProbabilities = new OptionValue<>(false); 110 @Option(help = "Enable dumping to the IdealGraphVisualizer.", type = OptionType.Debug) 111 public static final OptionValue<Boolean> PrintIdealGraph = new OptionValue<>(true); 112 @Option(help = "Dump IdealGraphVisualizer output in binary format", type = OptionType.Debug) 113 public static final OptionValue<Boolean> PrintBinaryGraphs = new OptionValue<>(true); 114 @Option(help = "Print Ideal graphs as opposed to sending them over the network.", type = OptionType.Debug) 115 public static final OptionValue<Boolean> PrintIdealGraphFile = new OptionValue<>(false); 116 @Option(help = "Base filename when dumping Ideal graphs to files.", type = OptionType.Debug) 117 public static final OptionValue<String> PrintIdealGraphFileName = new OptionValue<>("runtime-graphs"); 118 119 @Option(help = "", type = OptionType.Debug) 120 public static final OptionValue<String> PrintIdealGraphAddress = new OptionValue<>("127.0.0.1"); 121 @Option(help = "", type = OptionType.Debug) 122 public static final OptionValue<Integer> PrintIdealGraphPort = new OptionValue<>(4444); 123 @Option(help = "", type = OptionType.Debug) 124 public static final OptionValue<Integer> PrintBinaryGraphPort = new OptionValue<>(4445); 125 @Option(help = "", type = OptionType.Debug) 126 public static final OptionValue<Boolean> PrintIdealGraphSchedule = new OptionValue<>(false); 127 @Option(help = "Enable dumping Truffle ASTs to the IdealGraphVisualizer.", type = OptionType.Debug) 128 public static final OptionValue<Boolean> PrintTruffleTrees = new OptionValue<>(true); 129 130 @Option(help = "Enable dumping canonical text from for graphs.", type = OptionType.Debug) 131 public static final OptionValue<Boolean> PrintCanonicalGraphStrings = new OptionValue<>(false); 132 @Option(help = "Base directory when dumping graphs strings to files.", type = OptionType.Debug) 133 public static final OptionValue<String> PrintCanonicalGraphStringsDirectory = new OptionValue<>("graph-strings"); 134 @Option(help = "Choose format used when dumping canonical text for graphs: " + 135 "0 gives a scheduled graph (better for spotting changes involving the schedule)" + 136 "while 1 gives a CFG containing expressions rooted at fixed nodes (better for spotting small structure differences)", type = OptionType.Debug) 137 public static final OptionValue<Integer> PrintCanonicalGraphStringFlavor = new OptionValue<>(0); 138 @Option(help = "Exclude virtual nodes when dumping canonical text for graphs.", type = OptionType.Debug) 139 public static final OptionValue<Boolean> CanonicalGraphStringsExcludeVirtuals = new OptionValue<>(true); 140 @Option(help = "Exclude virtual nodes when dumping canonical text for graphs.", type = OptionType.Debug) 141 public static final OptionValue<Boolean> CanonicalGraphStringsCheckConstants = new OptionValue<>(false); 142 @Option(help = "Attempts to remove object identity hashes when dumping canonical text for graphs.", type = OptionType.Debug) 143 public static final OptionValue<Boolean> CanonicalGraphStringsRemoveIdentities = new OptionValue<>(true); 144 145 @Option(help = "Enable per method metrics that are collected across all compilations of a method." + 146 "Pattern for scope(s) in which method metering is enabled (see DebugFilter and Debug.metric).", type = OptionType.Debug) 147 public static final OptionValue<String> MethodMeter = new OptionValue<>(null); 148 @Option(help = "If a global metric (DebugTimer, DebugCounter or DebugMemUseTracker) is enabled in the same scope as a method metric, " + 149 "use the global metric to update the method metric for the current compilation. " + 150 "This option enables the re-use of global metrics on per-compilation basis. " + 151 "Whenever a value is added to a global metric, the value is also added to a MethodMetric under the same name " + 152 "as the global metric. " + 153 "This option incurs a small but constant overhead due to the context method lookup at each metric update. " + 154 "Format to specify GlobalMetric interception:(Timers|Counters|MemUseTrackers)(,Timers|,Counters|,MemUseTrackers)*", type = OptionType.Debug) 155 public static final OptionValue<String> GlobalMetricsInterceptedByMethodMetrics = new OptionValue<>(null); 156 @Option(help = "Force-enable debug code paths", type = OptionType.Debug) 157 public static final OptionValue<Boolean> ForceDebugEnable = new OptionValue<>(false); 158 @Option(help = "Clear the debug metrics after bootstrap.", type = OptionType.Debug) 159 public static final OptionValue<Boolean> ClearMetricsAfterBootstrap = new OptionValue<>(false); 160 @Option(help = "Do not compile anything on bootstrap but just initialize the compiler.", type = OptionType.Debug) 161 public static final OptionValue<Boolean> BootstrapInitializeOnly = new OptionValue<>(false); 162 // @formatter:on 163 } 164 165 public static boolean isNotEmpty(OptionValue<String> option) { 166 return option.getValue() != null && !option.getValue().isEmpty(); 167 } 168 169 public static boolean areDebugScopePatternsEnabled() { 170 return Options.DumpOnError.getValue() || Options.Dump.getValue() != null || Options.Log.getValue() != null || areScopedGlobalMetricsEnabled(); 171 } 172 173 public static boolean isGlobalMetricsInterceptedByMethodMetricsEnabled() { 174 return isNotEmpty(Options.GlobalMetricsInterceptedByMethodMetrics); 175 } 176 177 /** 178 * Determines if any of {@link Options#Count}, {@link Options#Time} or 179 * {@link Options#TrackMemUse} has a non-null, non-empty value. 180 */ 181 public static boolean areScopedGlobalMetricsEnabled() { 182 return isNotEmpty(Options.Count) || isNotEmpty(Options.Time) || isNotEmpty(Options.TrackMemUse) || isNotEmpty(Options.MethodMeter); 183 } 184 185 private final DebugFilter countFilter; 186 private final DebugFilter logFilter; 187 private final DebugFilter methodMetricsFilter; 188 private final DebugFilter trackMemUseFilter; 189 private final DebugFilter timerFilter; 190 private final DebugFilter dumpFilter; 191 private final DebugFilter verifyFilter; 192 private final MethodFilter[] methodFilter; 193 private final List<DebugDumpHandler> dumpHandlers; 194 private final List<DebugVerifyHandler> verifyHandlers; 195 private final PrintStream output; 196 197 // Use an identity set to handle context objects that don't support hashCode(). 198 private final Set<Object> extraFilters = Collections.newSetFromMap(new IdentityHashMap<>()); 199 200 public GraalDebugConfig(String logFilter, String countFilter, String trackMemUseFilter, String timerFilter, String dumpFilter, String verifyFilter, String methodFilter, 201 String methodMetricsFilter, PrintStream output, List<DebugDumpHandler> dumpHandlers, List<DebugVerifyHandler> verifyHandlers) { 202 this.logFilter = DebugFilter.parse(logFilter); 203 this.countFilter = DebugFilter.parse(countFilter); 204 this.trackMemUseFilter = DebugFilter.parse(trackMemUseFilter); 205 this.timerFilter = DebugFilter.parse(timerFilter); 206 this.dumpFilter = DebugFilter.parse(dumpFilter); 207 this.verifyFilter = DebugFilter.parse(verifyFilter); 208 this.methodMetricsFilter = DebugFilter.parse(methodMetricsFilter); 209 if (methodFilter == null || methodFilter.isEmpty()) { 210 this.methodFilter = null; 211 } else { 212 this.methodFilter = org.graalvm.compiler.debug.MethodFilter.parse(methodFilter); 213 } 214 215 // Report the filters that have been configured so the user can verify it's what they expect 216 if (logFilter != null || countFilter != null || timerFilter != null || dumpFilter != null || methodFilter != null) { 217 // TTY.println(Thread.currentThread().getName() + ": " + toString()); 218 } 219 this.dumpHandlers = dumpHandlers; 220 this.verifyHandlers = verifyHandlers; 221 this.output = output; 222 } 223 224 @Override 225 public int getLogLevel() { 226 return getLevel(logFilter); 227 } 228 229 @Override 230 public boolean isLogEnabledForMethod() { 231 return isEnabledForMethod(logFilter); 232 } 233 234 @Override 235 public boolean isCountEnabled() { 236 return isEnabled(countFilter); 237 } 238 239 @Override 240 public boolean isMemUseTrackingEnabled() { 241 return isEnabled(trackMemUseFilter); 242 } 243 244 @Override 245 public int getDumpLevel() { 246 return getLevel(dumpFilter); 247 } 248 249 @Override 250 public boolean isDumpEnabledForMethod() { 251 return isEnabledForMethod(dumpFilter); 252 } 253 254 @Override 255 public boolean isVerifyEnabled() { 256 return isEnabled(verifyFilter); 257 } 258 259 @Override 260 public boolean isVerifyEnabledForMethod() { 261 return isEnabledForMethod(verifyFilter); 262 } 263 264 @Override 265 public boolean isMethodMeterEnabled() { 266 return isEnabled(methodMetricsFilter); 267 } 268 269 @Override 270 public boolean isTimeEnabled() { 271 return isEnabled(timerFilter); 272 } 273 274 @Override 275 public PrintStream output() { 276 return output; 277 } 278 279 private boolean isEnabled(DebugFilter filter) { 280 return getLevel(filter) > 0; 281 } 282 283 private int getLevel(DebugFilter filter) { 284 int level; 285 if (filter == null) { 286 level = 0; 287 } else { 288 level = filter.matchLevel(Debug.currentScope()); 289 } 290 if (level > 0 && !checkMethodFilter()) { 291 level = 0; 292 } 293 return level; 294 } 295 296 private boolean isEnabledForMethod(DebugFilter filter) { 297 return filter != null && checkMethodFilter(); 298 } 299 300 /** 301 * Extracts a {@link JavaMethod} from an opaque debug context. 302 * 303 * @return the {@link JavaMethod} represented by {@code context} or null 304 */ 305 public static JavaMethod asJavaMethod(Object context) { 306 if (context instanceof JavaMethodContext) { 307 return ((JavaMethodContext) context).asJavaMethod(); 308 } 309 if (context instanceof JavaMethod) { 310 return (JavaMethod) context; 311 } 312 return null; 313 } 314 315 private boolean checkMethodFilter() { 316 if (methodFilter == null && extraFilters.isEmpty()) { 317 return true; 318 } else { 319 JavaMethod lastMethod = null; 320 for (Object o : Debug.context()) { 321 if (extraFilters.contains(o)) { 322 return true; 323 } else if (methodFilter != null) { 324 JavaMethod method = asJavaMethod(o); 325 if (method != null) { 326 if (!Options.MethodFilterRootOnly.getValue()) { 327 if (org.graalvm.compiler.debug.MethodFilter.matches(methodFilter, method)) { 328 return true; 329 } 330 } else { 331 /* 332 * The context values operate as a stack so if we want MethodFilter to 333 * only apply to the root method we have to check only the last method 334 * seen. 335 */ 336 lastMethod = method; 337 } 338 } 339 } 340 } 341 if (lastMethod != null && org.graalvm.compiler.debug.MethodFilter.matches(methodFilter, lastMethod)) { 342 return true; 343 } 344 return false; 345 } 346 } 347 348 @Override 349 public String toString() { 350 StringBuilder sb = new StringBuilder(); 351 sb.append("Debug config:"); 352 add(sb, "Log", logFilter); 353 add(sb, "Count", countFilter); 354 add(sb, "MethodMeter", methodMetricsFilter); 355 add(sb, "Time", timerFilter); 356 add(sb, "Dump", dumpFilter); 357 add(sb, "MethodFilter", methodFilter); 358 return sb.toString(); 359 } 360 361 private static void add(StringBuilder sb, String name, Object filter) { 362 if (filter != null) { 363 sb.append(' '); 364 sb.append(name); 365 sb.append('='); 366 if (filter instanceof Object[]) { 367 sb.append(Arrays.toString((Object[]) filter)); 368 } else { 369 sb.append(String.valueOf(filter)); 370 } 371 } 372 } 373 374 @Override 375 public RuntimeException interceptException(Throwable e) { 376 if (e instanceof BailoutException && !Options.InterceptBailout.getValue()) { 377 return null; 378 } 379 Debug.setConfig(Debug.fixedConfig(Debug.BASIC_LOG_LEVEL, Debug.BASIC_LOG_LEVEL, false, false, false, false, false, dumpHandlers, verifyHandlers, output)); 380 Debug.log("Exception occurred in scope: %s", Debug.currentScope()); 381 Map<Object, Object> firstSeen = new IdentityHashMap<>(); 382 for (Object o : Debug.context()) { 383 // Only dump a context object once. 384 if (!firstSeen.containsKey(o)) { 385 firstSeen.put(o, o); 386 if (Options.DumpOnError.getValue()) { 387 Debug.dump(Debug.BASIC_LOG_LEVEL, o, "Exception: %s", e); 388 } else { 389 Debug.log("Context obj %s", o); 390 } 391 } 392 } 393 return null; 394 } 395 396 @Override 397 public Collection<DebugDumpHandler> dumpHandlers() { 398 return dumpHandlers; 399 } 400 401 @Override 402 public Collection<DebugVerifyHandler> verifyHandlers() { 403 return verifyHandlers; 404 } 405 406 @Override 407 public void addToContext(Object o) { 408 extraFilters.add(o); 409 } 410 411 @Override 412 public void removeFromContext(Object o) { 413 extraFilters.remove(o); 414 } 415 416 }