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}