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 
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 
DebugConfigImpl(OptionValues options)58     DebugConfigImpl(OptionValues options) {
59         this(options, TTY.out, Collections.emptyList(), Collections.emptyList());
60     }
61 
DebugConfigImpl(OptionValues options, PrintStream output, List<DebugDumpHandler> dumpHandlers, List<DebugVerifyHandler> verifyHandlers)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 
DebugConfigImpl(OptionValues options, String logFilter, String countFilter, String trackMemUseFilter, String timerFilter, String dumpFilter, String verifyFilter, String methodFilter, PrintStream output, List<DebugDumpHandler> dumpHandlers, List<DebugVerifyHandler> verifyHandlers)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 
getVerifyOptionValue(OptionValues values)104     private static String getVerifyOptionValue(OptionValues values) {
105         return !DebugOptions.Verify.hasBeenSet(values) && Assertions.assertionsEnabled() ? "" : DebugOptions.Verify.getValue(values);
106     }
107 
108     @Override
getOptions()109     public OptionValues getOptions() {
110         return options;
111     }
112 
113     @Override
getLogLevel(DebugContext.Scope scope)114     public int getLogLevel(DebugContext.Scope scope) {
115         return getLevel(scope, logFilter);
116     }
117 
118     @Override
isLogEnabledForMethod(DebugContext.Scope scope)119     public boolean isLogEnabledForMethod(DebugContext.Scope scope) {
120         return isEnabledForMethod(scope, logFilter);
121     }
122 
123     @Override
isCountEnabled(DebugContext.Scope scope)124     public boolean isCountEnabled(DebugContext.Scope scope) {
125         return isEnabled(scope, countFilter);
126     }
127 
128     @Override
isMemUseTrackingEnabled(DebugContext.Scope scope)129     public boolean isMemUseTrackingEnabled(DebugContext.Scope scope) {
130         return isEnabled(scope, trackMemUseFilter);
131     }
132 
133     @Override
getDumpLevel(DebugContext.Scope scope)134     public int getDumpLevel(DebugContext.Scope scope) {
135         return getLevel(scope, dumpFilter);
136     }
137 
138     @Override
isDumpEnabledForMethod(DebugContext.Scope scope)139     public boolean isDumpEnabledForMethod(DebugContext.Scope scope) {
140         return isEnabledForMethod(scope, dumpFilter);
141     }
142 
143     @Override
isVerifyEnabled(DebugContext.Scope scope)144     public boolean isVerifyEnabled(DebugContext.Scope scope) {
145         return isEnabled(scope, verifyFilter);
146     }
147 
148     @Override
isVerifyEnabledForMethod(DebugContext.Scope scope)149     public boolean isVerifyEnabledForMethod(DebugContext.Scope scope) {
150         return isEnabledForMethod(scope, verifyFilter);
151     }
152 
153     @Override
isTimeEnabled(DebugContext.Scope scope)154     public boolean isTimeEnabled(DebugContext.Scope scope) {
155         return isEnabled(scope, timerFilter);
156     }
157 
158     @Override
output()159     public PrintStream output() {
160         return output;
161     }
162 
isEnabled(DebugContext.Scope scope, DebugFilter filter)163     private boolean isEnabled(DebugContext.Scope scope, DebugFilter filter) {
164         return getLevel(scope, filter) > 0;
165     }
166 
getLevel(DebugContext.Scope scope, DebugFilter filter)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 
isEnabledForMethod(DebugContext.Scope scope, DebugFilter filter)181     private boolean isEnabledForMethod(DebugContext.Scope scope, DebugFilter filter) {
182         return filter != null && checkMethodFilter(scope);
183     }
184 
checkMethodFilter(DebugContext.Scope scope)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
toString()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 
add(StringBuilder sb, String name, Object filter)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
interceptException(DebugContext debug, Throwable e)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
dumpHandlers()283     public Collection<DebugDumpHandler> dumpHandlers() {
284         return dumpHandlers;
285     }
286 
287     @Override
verifyHandlers()288     public Collection<DebugVerifyHandler> verifyHandlers() {
289         return verifyHandlers;
290     }
291 }
292