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