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 }