001/*
002 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.debug;
024
025import java.io.*;
026import java.util.*;
027
028import jdk.internal.jvmci.code.*;
029import jdk.internal.jvmci.meta.*;
030import jdk.internal.jvmci.options.*;
031
032public class GraalDebugConfig implements DebugConfig {
033    @SuppressWarnings("all")
034    private static boolean assertionsEnabled() {
035        boolean assertionsEnabled = false;
036        assert assertionsEnabled = true;
037        return assertionsEnabled;
038    }
039
040    // @formatter:off
041    @Option(help = "Pattern for scope(s) in which dumping is enabled (see DebugFilter and Debug.dump)", type = OptionType.Debug)
042    public static final OptionValue<String> Dump = new OptionValue<>(null);
043    @Option(help = "Pattern for scope(s) in which metering is enabled (see DebugFilter and Debug.metric). " +
044                   "An empty value enables all metrics unconditionally.", type = OptionType.Debug)
045    public static final OptionValue<String> Meter = new OptionValue<>(null);
046    @Option(help = "Pattern for scope(s) in which verification is enabled (see DebugFilter and Debug.verify).", type = OptionType.Debug)
047    public static final OptionValue<String> Verify = new OptionValue<String>() {
048        @Override
049        protected String defaultValue() {
050            return assertionsEnabled() ? "" : null;
051        }
052    };
053    @Option(help = "Pattern for scope(s) in which memory use tracking is enabled (see DebugFilter and Debug.metric). " +
054                   "An empty value enables all memory use trackers unconditionally.", type = OptionType.Debug)
055    public static final OptionValue<String> TrackMemUse = new OptionValue<>(null);
056    @Option(help = "Pattern for scope(s) in which timing is enabled (see DebugFilter and Debug.timer). " +
057                   "An empty value enables all timers unconditionally.", type = OptionType.Debug)
058    public static final OptionValue<String> Time = new OptionValue<>(null);
059    @Option(help = "Pattern for scope(s) in which logging is enabled (see DebugFilter and Debug.log)", type = OptionType.Debug)
060    public static final OptionValue<String> Log = new OptionValue<>(null);
061    @Option(help = "Pattern for filtering debug scope output based on method context (see MethodFilter)", type = OptionType.Debug)
062    public static final OptionValue<String> MethodFilter = new OptionValue<>(null);
063    @Option(help = "Only check MethodFilter against the root method in the context if true, otherwise check all methods", type = OptionType.Debug)
064    public static final OptionValue<Boolean> MethodFilterRootOnly = new OptionValue<>(false);
065
066    @Option(help = "How to print metric and timing values:%n" +
067                   "Name - aggregate by unqualified name%n" +
068                   "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" +
069                   "Complete - aggregate by qualified name%n" +
070                   "Thread - aggregate by qualified name and thread", type = OptionType.Debug)
071    public static final OptionValue<String> DebugValueSummary = new OptionValue<>("Name");
072    @Option(help = "Omit reporting 0-value metrics", type = OptionType.Debug)
073    public static final OptionValue<Boolean> SuppressZeroDebugValues = new OptionValue<>(true);
074    @Option(help = "Only report debug values for maps which match the regular expression.", type = OptionType.Debug)
075    public static final OptionValue<String> DebugValueThreadFilter = new OptionValue<>(null);
076    @Option(help = "Send JVMCI compiler IR to dump handlers on error", type = OptionType.Debug)
077    public static final OptionValue<Boolean> DumpOnError = new OptionValue<>(false);
078    @Option(help = "Intercept also bailout exceptions", type = OptionType.Debug)
079    public static final OptionValue<Boolean> InterceptBailout = new OptionValue<>(false);
080    @Option(help = "Enable more verbose log output when available", type = OptionType.Debug)
081    public static final OptionValue<Boolean> LogVerbose = new OptionValue<>(false);
082    // @formatter:on
083
084    static boolean isNotEmpty(OptionValue<String> option) {
085        return option.getValue() != null && !option.getValue().isEmpty();
086    }
087
088    public static boolean areDebugScopePatternsEnabled() {
089        return DumpOnError.getValue() || Dump.getValue() != null || Log.getValue() != null || areScopedMetricsOrTimersEnabled();
090    }
091
092    /**
093     * Determines if any of {@link #Meter}, {@link #Time} or {@link #TrackMemUse} has a non-null,
094     * non-empty value.
095     */
096    public static boolean areScopedMetricsOrTimersEnabled() {
097        return isNotEmpty(Meter) || isNotEmpty(Time) || isNotEmpty(TrackMemUse);
098    }
099
100    private final DebugFilter logFilter;
101    private final DebugFilter meterFilter;
102    private final DebugFilter trackMemUseFilter;
103    private final DebugFilter timerFilter;
104    private final DebugFilter dumpFilter;
105    private final DebugFilter verifyFilter;
106    private final MethodFilter[] methodFilter;
107    private final List<DebugDumpHandler> dumpHandlers;
108    private final List<DebugVerifyHandler> verifyHandlers;
109    private final PrintStream output;
110    private final Set<Object> extraFilters = new HashSet<>();
111
112    public GraalDebugConfig(String logFilter, String meterFilter, String trackMemUseFilter, String timerFilter, String dumpFilter, String verifyFilter, String methodFilter, PrintStream output,
113                    List<DebugDumpHandler> dumpHandlers, List<DebugVerifyHandler> verifyHandlers) {
114        this.logFilter = DebugFilter.parse(logFilter);
115        this.meterFilter = DebugFilter.parse(meterFilter);
116        this.trackMemUseFilter = DebugFilter.parse(trackMemUseFilter);
117        this.timerFilter = DebugFilter.parse(timerFilter);
118        this.dumpFilter = DebugFilter.parse(dumpFilter);
119        this.verifyFilter = DebugFilter.parse(verifyFilter);
120        if (methodFilter == null || methodFilter.isEmpty()) {
121            this.methodFilter = null;
122        } else {
123            this.methodFilter = com.oracle.graal.debug.MethodFilter.parse(methodFilter);
124        }
125
126        // Report the filters that have been configured so the user can verify it's what they expect
127        if (logFilter != null || meterFilter != null || timerFilter != null || dumpFilter != null || methodFilter != null) {
128            // TTY.println(Thread.currentThread().getName() + ": " + toString());
129        }
130        this.dumpHandlers = dumpHandlers;
131        this.verifyHandlers = verifyHandlers;
132        this.output = output;
133    }
134
135    public int getLogLevel() {
136        return getLevel(logFilter);
137    }
138
139    public boolean isLogEnabledForMethod() {
140        return isEnabledForMethod(logFilter);
141    }
142
143    public boolean isMeterEnabled() {
144        return isEnabled(meterFilter);
145    }
146
147    public boolean isMemUseTrackingEnabled() {
148        return isEnabled(trackMemUseFilter);
149    }
150
151    public int getDumpLevel() {
152        return getLevel(dumpFilter);
153    }
154
155    public boolean isDumpEnabledForMethod() {
156        return isEnabledForMethod(dumpFilter);
157    }
158
159    public boolean isVerifyEnabled() {
160        return isEnabled(verifyFilter);
161    }
162
163    public boolean isVerifyEnabledForMethod() {
164        return isEnabledForMethod(verifyFilter);
165    }
166
167    public boolean isTimeEnabled() {
168        return isEnabled(timerFilter);
169    }
170
171    public PrintStream output() {
172        return output;
173    }
174
175    private boolean isEnabled(DebugFilter filter) {
176        return getLevel(filter) > 0;
177    }
178
179    private int getLevel(DebugFilter filter) {
180        int level;
181        if (filter == null) {
182            level = 0;
183        } else {
184            level = filter.matchLevel(Debug.currentScope());
185        }
186        if (level > 0 && !checkMethodFilter()) {
187            level = 0;
188        }
189        return level;
190    }
191
192    private boolean isEnabledForMethod(DebugFilter filter) {
193        return filter != null && checkMethodFilter();
194    }
195
196    /**
197     * Extracts a {@link JavaMethod} from an opaque debug context.
198     *
199     * @return the {@link JavaMethod} represented by {@code context} or null
200     */
201    public static JavaMethod asJavaMethod(Object context) {
202        if (context instanceof JavaMethodContext) {
203            return ((JavaMethodContext) context).asJavaMethod();
204        }
205        return null;
206    }
207
208    private boolean checkMethodFilter() {
209        if (methodFilter == null && extraFilters.isEmpty()) {
210            return true;
211        } else {
212            JavaMethod lastMethod = null;
213            for (Object o : Debug.context()) {
214                if (extraFilters.contains(o)) {
215                    return true;
216                } else if (methodFilter != null) {
217                    JavaMethod method = asJavaMethod(o);
218                    if (method != null) {
219                        if (!MethodFilterRootOnly.getValue()) {
220                            if (com.oracle.graal.debug.MethodFilter.matches(methodFilter, method)) {
221                                return true;
222                            }
223                        } else {
224                            /*
225                             * The context values operate as a stack so if we want MethodFilter to
226                             * only apply to the root method we have to check only the last method
227                             * seen.
228                             */
229                            lastMethod = method;
230                        }
231                    }
232                }
233            }
234            if (lastMethod != null && com.oracle.graal.debug.MethodFilter.matches(methodFilter, lastMethod)) {
235                return true;
236            }
237            return false;
238        }
239    }
240
241    @Override
242    public String toString() {
243        StringBuilder sb = new StringBuilder();
244        sb.append("Debug config:");
245        add(sb, "Log", logFilter);
246        add(sb, "Meter", meterFilter);
247        add(sb, "Time", timerFilter);
248        add(sb, "Dump", dumpFilter);
249        add(sb, "MethodFilter", methodFilter);
250        return sb.toString();
251    }
252
253    private static void add(StringBuilder sb, String name, Object filter) {
254        if (filter != null) {
255            sb.append(' ');
256            sb.append(name);
257            sb.append('=');
258            if (filter instanceof Object[]) {
259                sb.append(Arrays.toString((Object[]) filter));
260            } else {
261                sb.append(String.valueOf(filter));
262            }
263        }
264    }
265
266    @Override
267    public RuntimeException interceptException(Throwable e) {
268        if (e instanceof BailoutException && !InterceptBailout.getValue()) {
269            return null;
270        }
271        Debug.setConfig(Debug.fixedConfig(Debug.DEFAULT_LOG_LEVEL, Debug.DEFAULT_LOG_LEVEL, false, false, false, false, dumpHandlers, verifyHandlers, output));
272        Debug.log(String.format("Exception occurred in scope: %s", Debug.currentScope()));
273        for (Object o : Debug.context()) {
274            if (DumpOnError.getValue()) {
275                Debug.dump(o, "Exception: " + e.toString());
276            } else {
277                Debug.log("Context obj %s", o);
278            }
279
280        }
281        return null;
282    }
283
284    @Override
285    public Collection<DebugDumpHandler> dumpHandlers() {
286        return dumpHandlers;
287    }
288
289    @Override
290    public Collection<DebugVerifyHandler> verifyHandlers() {
291        return verifyHandlers;
292    }
293
294    @Override
295    public void addToContext(Object o) {
296        extraFilters.add(o);
297    }
298
299    @Override
300    public void removeFromContext(Object o) {
301        extraFilters.remove(o);
302    }
303}