--- /dev/null 2017-01-22 10:16:57.869617664 -0800 +++ new/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/GraalDebugConfig.java 2017-02-15 16:58:20.648923985 -0800 @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.debug; + +import java.io.PrintStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.graalvm.compiler.options.Option; +import org.graalvm.compiler.options.OptionType; +import org.graalvm.compiler.options.OptionValue; + +import jdk.vm.ci.code.BailoutException; +import jdk.vm.ci.meta.JavaMethod; + +public class GraalDebugConfig implements DebugConfig { + @SuppressWarnings("all") + private static boolean assertionsEnabled() { + boolean assertionsEnabled = false; + assert assertionsEnabled = true; + return assertionsEnabled; + } + + public static class Options { + // @formatter:off + @Option(help = "Pattern for scope(s) in which dumping is enabled (see DebugFilter and Debug.dump)", type = OptionType.Debug) + public static final OptionValue Dump = new OptionValue<>(null); + @Option(help = "Pattern for scope(s) in which counting is enabled (see DebugFilter and Debug.counter). " + + "An empty value enables all counters unconditionally.", type = OptionType.Debug) + public static final OptionValue Count = new OptionValue<>(null); + @Option(help = "Pattern for scope(s) in which verification is enabled (see DebugFilter and Debug.verify).", type = OptionType.Debug) + public static final OptionValue Verify = new OptionValue() { + @Override + protected String defaultValue() { + return assertionsEnabled() ? "" : null; + } + }; + @Option(help = "Pattern for scope(s) in which memory use tracking is enabled (see DebugFilter and Debug.counter). " + + "An empty value enables all memory use trackers unconditionally.", type = OptionType.Debug) + public static final OptionValue TrackMemUse = new OptionValue<>(null); + @Option(help = "Pattern for scope(s) in which timing is enabled (see DebugFilter and Debug.timer). " + + "An empty value enables all timers unconditionally.", type = OptionType.Debug) + public static final OptionValue Time = new OptionValue<>(null); + @Option(help = "Pattern for scope(s) in which logging is enabled (see DebugFilter and Debug.log)", type = OptionType.Debug) + public static final OptionValue Log = new OptionValue<>(null); + @Option(help = "Pattern for filtering debug scope output based on method context (see MethodFilter)", type = OptionType.Debug) + public static final OptionValue MethodFilter = new OptionValue<>(null); + @Option(help = "Only check MethodFilter against the root method in the context if true, otherwise check all methods", type = OptionType.Debug) + public static final OptionValue MethodFilterRootOnly = new OptionValue<>(false); + + @Option(help = "How to print counters and timing values:%n" + + "Name - aggregate by unqualified name%n" + + "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" + + "Complete - aggregate by qualified name%n" + + "Thread - aggregate by qualified name and thread", type = OptionType.Debug) + public static final OptionValue DebugValueSummary = new OptionValue<>("Name"); + @Option(help = "Print counters and timers in a human readable form.", type = OptionType.Debug) + public static final OptionValue DebugValueHumanReadable = new OptionValue<>(true); + @Option(help = "Omit reporting 0-value counters", type = OptionType.Debug) + public static final OptionValue SuppressZeroDebugValues = new OptionValue<>(true); + @Option(help = "Only report debug values for maps which match the regular expression.", type = OptionType.Debug) + public static final OptionValue DebugValueThreadFilter = new OptionValue<>(null); + @Option(help = "Write debug values into a file instead of the terminal. " + + "If DebugValueSummary is Thread, the thread name will be prepended.", type = OptionType.Debug) + public static final OptionValue DebugValueFile = new OptionValue<>(null); + @Option(help = "Send Graal compiler IR to dump handlers on error", type = OptionType.Debug) + public static final OptionValue DumpOnError = new OptionValue<>(false); + @Option(help = "Intercept also bailout exceptions", type = OptionType.Debug) + public static final OptionValue InterceptBailout = new OptionValue<>(false); + @Option(help = "Enable more verbose log output when available", type = OptionType.Debug) + public static final OptionValue LogVerbose = new OptionValue<>(false); + + @Option(help = "The directory where various Graal dump files are written.") + public static final OptionValue DumpPath = new OptionValue<>("."); + + @Option(help = "Enable dumping to the C1Visualizer. Enabling this option implies PrintBackendCFG.", type = OptionType.Debug) + public static final OptionValue PrintCFG = new OptionValue<>(false); + @Option(help = "Enable dumping LIR, register allocation and code generation info to the C1Visualizer.", type = OptionType.Debug) + public static final OptionValue PrintBackendCFG = new OptionValue<>(true); + @Option(help = "Base filename when dumping C1Visualizer output to files.", type = OptionType.Debug) + public static final OptionValue PrintCFGFileName = new OptionValue<>("compilations"); + + @Option(help = "Output probabilities for fixed nodes during binary graph dumping", type = OptionType.Debug) + public static final OptionValue PrintGraphProbabilities = new OptionValue<>(false); + @Option(help = "Enable dumping to the IdealGraphVisualizer.", type = OptionType.Debug) + public static final OptionValue PrintIdealGraph = new OptionValue<>(true); + @Option(help = "Dump IdealGraphVisualizer output in binary format", type = OptionType.Debug) + public static final OptionValue PrintBinaryGraphs = new OptionValue<>(true); + @Option(help = "Print Ideal graphs as opposed to sending them over the network.", type = OptionType.Debug) + public static final OptionValue PrintIdealGraphFile = new OptionValue<>(false); + @Option(help = "Base filename when dumping Ideal graphs to files.", type = OptionType.Debug) + public static final OptionValue PrintIdealGraphFileName = new OptionValue<>("runtime-graphs"); + + @Option(help = "", type = OptionType.Debug) + public static final OptionValue PrintIdealGraphAddress = new OptionValue<>("127.0.0.1"); + @Option(help = "", type = OptionType.Debug) + public static final OptionValue PrintIdealGraphPort = new OptionValue<>(4444); + @Option(help = "", type = OptionType.Debug) + public static final OptionValue PrintBinaryGraphPort = new OptionValue<>(4445); + @Option(help = "", type = OptionType.Debug) + public static final OptionValue PrintIdealGraphSchedule = new OptionValue<>(false); + @Option(help = "Enable dumping Truffle ASTs to the IdealGraphVisualizer.", type = OptionType.Debug) + public static final OptionValue PrintTruffleTrees = new OptionValue<>(true); + + @Option(help = "Enable dumping canonical text from for graphs.", type = OptionType.Debug) + public static final OptionValue PrintCanonicalGraphStrings = new OptionValue<>(false); + @Option(help = "Base directory when dumping graphs strings to files.", type = OptionType.Debug) + public static final OptionValue PrintCanonicalGraphStringsDirectory = new OptionValue<>("graph-strings"); + @Option(help = "Choose format used when dumping canonical text for graphs: " + + "0 gives a scheduled graph (better for spotting changes involving the schedule)" + + "while 1 gives a CFG containing expressions rooted at fixed nodes (better for spotting small structure differences)", type = OptionType.Debug) + public static final OptionValue PrintCanonicalGraphStringFlavor = new OptionValue<>(0); + @Option(help = "Exclude virtual nodes when dumping canonical text for graphs.", type = OptionType.Debug) + public static final OptionValue CanonicalGraphStringsExcludeVirtuals = new OptionValue<>(true); + @Option(help = "Exclude virtual nodes when dumping canonical text for graphs.", type = OptionType.Debug) + public static final OptionValue CanonicalGraphStringsCheckConstants = new OptionValue<>(false); + @Option(help = "Attempts to remove object identity hashes when dumping canonical text for graphs.", type = OptionType.Debug) + public static final OptionValue CanonicalGraphStringsRemoveIdentities = new OptionValue<>(true); + + @Option(help = "Enable per method metrics that are collected across all compilations of a method." + + "Pattern for scope(s) in which method metering is enabled (see DebugFilter and Debug.metric).", type = OptionType.Debug) + public static final OptionValue MethodMeter = new OptionValue<>(null); + @Option(help = "If a global metric (DebugTimer, DebugCounter or DebugMemUseTracker) is enabled in the same scope as a method metric, " + + "use the global metric to update the method metric for the current compilation. " + + "This option enables the re-use of global metrics on per-compilation basis. " + + "Whenever a value is added to a global metric, the value is also added to a MethodMetric under the same name " + + "as the global metric. " + + "This option incurs a small but constant overhead due to the context method lookup at each metric update. " + + "Format to specify GlobalMetric interception:(Timers|Counters|MemUseTrackers)(,Timers|,Counters|,MemUseTrackers)*", type = OptionType.Debug) + public static final OptionValue GlobalMetricsInterceptedByMethodMetrics = new OptionValue<>(null); + @Option(help = "Force-enable debug code paths", type = OptionType.Debug) + public static final OptionValue ForceDebugEnable = new OptionValue<>(false); + @Option(help = "Clear the debug metrics after bootstrap.", type = OptionType.Debug) + public static final OptionValue ClearMetricsAfterBootstrap = new OptionValue<>(false); + @Option(help = "Do not compile anything on bootstrap but just initialize the compiler.", type = OptionType.Debug) + public static final OptionValue BootstrapInitializeOnly = new OptionValue<>(false); + // @formatter:on + } + + public static boolean isNotEmpty(OptionValue option) { + return option.getValue() != null && !option.getValue().isEmpty(); + } + + public static boolean areDebugScopePatternsEnabled() { + return Options.DumpOnError.getValue() || Options.Dump.getValue() != null || Options.Log.getValue() != null || areScopedGlobalMetricsEnabled(); + } + + public static boolean isGlobalMetricsInterceptedByMethodMetricsEnabled() { + return isNotEmpty(Options.GlobalMetricsInterceptedByMethodMetrics); + } + + /** + * Determines if any of {@link Options#Count}, {@link Options#Time} or + * {@link Options#TrackMemUse} has a non-null, non-empty value. + */ + public static boolean areScopedGlobalMetricsEnabled() { + return isNotEmpty(Options.Count) || isNotEmpty(Options.Time) || isNotEmpty(Options.TrackMemUse) || isNotEmpty(Options.MethodMeter); + } + + private final DebugFilter countFilter; + private final DebugFilter logFilter; + private final DebugFilter methodMetricsFilter; + private final DebugFilter trackMemUseFilter; + private final DebugFilter timerFilter; + private final DebugFilter dumpFilter; + private final DebugFilter verifyFilter; + private final MethodFilter[] methodFilter; + private final List dumpHandlers; + private final List verifyHandlers; + private final PrintStream output; + + // Use an identity set to handle context objects that don't support hashCode(). + private final Set extraFilters = Collections.newSetFromMap(new IdentityHashMap<>()); + + public GraalDebugConfig(String logFilter, String countFilter, String trackMemUseFilter, String timerFilter, String dumpFilter, String verifyFilter, String methodFilter, + String methodMetricsFilter, PrintStream output, List dumpHandlers, List verifyHandlers) { + this.logFilter = DebugFilter.parse(logFilter); + this.countFilter = DebugFilter.parse(countFilter); + this.trackMemUseFilter = DebugFilter.parse(trackMemUseFilter); + this.timerFilter = DebugFilter.parse(timerFilter); + this.dumpFilter = DebugFilter.parse(dumpFilter); + this.verifyFilter = DebugFilter.parse(verifyFilter); + this.methodMetricsFilter = DebugFilter.parse(methodMetricsFilter); + if (methodFilter == null || methodFilter.isEmpty()) { + this.methodFilter = null; + } else { + this.methodFilter = org.graalvm.compiler.debug.MethodFilter.parse(methodFilter); + } + + // Report the filters that have been configured so the user can verify it's what they expect + if (logFilter != null || countFilter != null || timerFilter != null || dumpFilter != null || methodFilter != null) { + // TTY.println(Thread.currentThread().getName() + ": " + toString()); + } + this.dumpHandlers = dumpHandlers; + this.verifyHandlers = verifyHandlers; + this.output = output; + } + + @Override + public int getLogLevel() { + return getLevel(logFilter); + } + + @Override + public boolean isLogEnabledForMethod() { + return isEnabledForMethod(logFilter); + } + + @Override + public boolean isCountEnabled() { + return isEnabled(countFilter); + } + + @Override + public boolean isMemUseTrackingEnabled() { + return isEnabled(trackMemUseFilter); + } + + @Override + public int getDumpLevel() { + return getLevel(dumpFilter); + } + + @Override + public boolean isDumpEnabledForMethod() { + return isEnabledForMethod(dumpFilter); + } + + @Override + public boolean isVerifyEnabled() { + return isEnabled(verifyFilter); + } + + @Override + public boolean isVerifyEnabledForMethod() { + return isEnabledForMethod(verifyFilter); + } + + @Override + public boolean isMethodMeterEnabled() { + return isEnabled(methodMetricsFilter); + } + + @Override + public boolean isTimeEnabled() { + return isEnabled(timerFilter); + } + + @Override + public PrintStream output() { + return output; + } + + private boolean isEnabled(DebugFilter filter) { + return getLevel(filter) > 0; + } + + private int getLevel(DebugFilter filter) { + int level; + if (filter == null) { + level = 0; + } else { + level = filter.matchLevel(Debug.currentScope()); + } + if (level > 0 && !checkMethodFilter()) { + level = 0; + } + return level; + } + + private boolean isEnabledForMethod(DebugFilter filter) { + return filter != null && checkMethodFilter(); + } + + /** + * Extracts a {@link JavaMethod} from an opaque debug context. + * + * @return the {@link JavaMethod} represented by {@code context} or null + */ + public static JavaMethod asJavaMethod(Object context) { + if (context instanceof JavaMethodContext) { + return ((JavaMethodContext) context).asJavaMethod(); + } + if (context instanceof JavaMethod) { + return (JavaMethod) context; + } + return null; + } + + private boolean checkMethodFilter() { + if (methodFilter == null && extraFilters.isEmpty()) { + return true; + } else { + JavaMethod lastMethod = null; + for (Object o : Debug.context()) { + if (extraFilters.contains(o)) { + return true; + } else if (methodFilter != null) { + JavaMethod method = asJavaMethod(o); + if (method != null) { + if (!Options.MethodFilterRootOnly.getValue()) { + if (org.graalvm.compiler.debug.MethodFilter.matches(methodFilter, method)) { + return true; + } + } else { + /* + * The context values operate as a stack so if we want MethodFilter to + * only apply to the root method we have to check only the last method + * seen. + */ + lastMethod = method; + } + } + } + } + if (lastMethod != null && org.graalvm.compiler.debug.MethodFilter.matches(methodFilter, lastMethod)) { + return true; + } + return false; + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Debug config:"); + add(sb, "Log", logFilter); + add(sb, "Count", countFilter); + add(sb, "MethodMeter", methodMetricsFilter); + add(sb, "Time", timerFilter); + add(sb, "Dump", dumpFilter); + add(sb, "MethodFilter", methodFilter); + return sb.toString(); + } + + private static void add(StringBuilder sb, String name, Object filter) { + if (filter != null) { + sb.append(' '); + sb.append(name); + sb.append('='); + if (filter instanceof Object[]) { + sb.append(Arrays.toString((Object[]) filter)); + } else { + sb.append(String.valueOf(filter)); + } + } + } + + @Override + public RuntimeException interceptException(Throwable e) { + if (e instanceof BailoutException && !Options.InterceptBailout.getValue()) { + return null; + } + Debug.setConfig(Debug.fixedConfig(Debug.BASIC_LOG_LEVEL, Debug.BASIC_LOG_LEVEL, false, false, false, false, false, dumpHandlers, verifyHandlers, output)); + Debug.log("Exception occurred in scope: %s", Debug.currentScope()); + Map firstSeen = new IdentityHashMap<>(); + for (Object o : Debug.context()) { + // Only dump a context object once. + if (!firstSeen.containsKey(o)) { + firstSeen.put(o, o); + if (Options.DumpOnError.getValue()) { + Debug.dump(Debug.BASIC_LOG_LEVEL, o, "Exception: %s", e); + } else { + Debug.log("Context obj %s", o); + } + } + } + return null; + } + + @Override + public Collection dumpHandlers() { + return dumpHandlers; + } + + @Override + public Collection verifyHandlers() { + return verifyHandlers; + } + + @Override + public void addToContext(Object o) { + extraFilters.add(o); + } + + @Override + public void removeFromContext(Object o) { + extraFilters.remove(o); + } + +}