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 package org.graalvm.compiler.debug;
  24 
  25 import static org.graalvm.compiler.debug.DebugContext.BASIC_LEVEL;
  26 
  27 import java.io.ByteArrayOutputStream;
  28 import java.io.PrintStream;
  29 import java.util.Arrays;
  30 import java.util.Collection;
  31 import java.util.Collections;
  32 import java.util.IdentityHashMap;
  33 import java.util.List;
  34 import java.util.Map;
  35 
  36 import org.graalvm.compiler.options.OptionValues;
  37 
  38 import jdk.vm.ci.code.BailoutException;
  39 import jdk.vm.ci.meta.JavaMethod;
  40 
  41 final class DebugConfigImpl implements DebugConfig {
  42 
  43     private final OptionValues options;
  44 
  45     private final DebugFilter countFilter;
  46     private final DebugFilter logFilter;
  47     private final DebugFilter trackMemUseFilter;
  48     private final DebugFilter timerFilter;
  49     private final DebugFilter dumpFilter;
  50     private final DebugFilter verifyFilter;
  51     private final MethodFilter[] methodFilter;
  52     private final List<DebugDumpHandler> dumpHandlers;
  53     private final List<DebugVerifyHandler> verifyHandlers;
  54     private final PrintStream output;
  55 
  56     DebugConfigImpl(OptionValues options) {
  57         this(options, TTY.out, Collections.emptyList(), Collections.emptyList());
  58     }
  59 
  60     DebugConfigImpl(OptionValues options, PrintStream output,
  61                     List<DebugDumpHandler> dumpHandlers,
  62                     List<DebugVerifyHandler> verifyHandlers) {
  63         this(options, DebugOptions.Log.getValue(options),
  64                         DebugOptions.Count.getValue(options),
  65                         DebugOptions.TrackMemUse.getValue(options),
  66                         DebugOptions.Time.getValue(options),
  67                         DebugOptions.Dump.getValue(options),
  68                         DebugOptions.Verify.getValue(options),
  69                         DebugOptions.MethodFilter.getValue(options),
  70                         output, dumpHandlers, verifyHandlers);
  71     }
  72 
  73     DebugConfigImpl(OptionValues options,
  74                     String logFilter,
  75                     String countFilter,
  76                     String trackMemUseFilter,
  77                     String timerFilter,
  78                     String dumpFilter,
  79                     String verifyFilter,
  80                     String methodFilter,
  81                     PrintStream output,
  82                     List<DebugDumpHandler> dumpHandlers,
  83                     List<DebugVerifyHandler> verifyHandlers) {
  84         this.options = options;
  85         this.logFilter = DebugFilter.parse(logFilter);
  86         this.countFilter = DebugFilter.parse(countFilter);
  87         this.trackMemUseFilter = DebugFilter.parse(trackMemUseFilter);
  88         this.timerFilter = DebugFilter.parse(timerFilter);
  89         this.dumpFilter = DebugFilter.parse(dumpFilter);
  90         this.verifyFilter = DebugFilter.parse(verifyFilter);
  91         if (methodFilter == null || methodFilter.isEmpty()) {
  92             this.methodFilter = null;
  93         } else {
  94             this.methodFilter = org.graalvm.compiler.debug.MethodFilter.parse(methodFilter);
  95         }
  96 
  97         this.dumpHandlers = Collections.unmodifiableList(dumpHandlers);
  98         this.verifyHandlers = Collections.unmodifiableList(verifyHandlers);
  99         this.output = output;
 100     }
 101 
 102     @Override
 103     public OptionValues getOptions() {
 104         return options;
 105     }
 106 
 107     @Override
 108     public int getLogLevel(DebugContext.Scope scope) {
 109         return getLevel(scope, logFilter);
 110     }
 111 
 112     @Override
 113     public boolean isLogEnabledForMethod(DebugContext.Scope scope) {
 114         return isEnabledForMethod(scope, logFilter);
 115     }
 116 
 117     @Override
 118     public boolean isCountEnabled(DebugContext.Scope scope) {
 119         return isEnabled(scope, countFilter);
 120     }
 121 
 122     @Override
 123     public boolean isMemUseTrackingEnabled(DebugContext.Scope scope) {
 124         return isEnabled(scope, trackMemUseFilter);
 125     }
 126 
 127     @Override
 128     public int getDumpLevel(DebugContext.Scope scope) {
 129         return getLevel(scope, dumpFilter);
 130     }
 131 
 132     @Override
 133     public boolean isDumpEnabledForMethod(DebugContext.Scope scope) {
 134         return isEnabledForMethod(scope, dumpFilter);
 135     }
 136 
 137     @Override
 138     public boolean isVerifyEnabled(DebugContext.Scope scope) {
 139         return isEnabled(scope, verifyFilter);
 140     }
 141 
 142     @Override
 143     public boolean isVerifyEnabledForMethod(DebugContext.Scope scope) {
 144         return isEnabledForMethod(scope, verifyFilter);
 145     }
 146 
 147     @Override
 148     public boolean isTimeEnabled(DebugContext.Scope scope) {
 149         return isEnabled(scope, timerFilter);
 150     }
 151 
 152     @Override
 153     public PrintStream output() {
 154         return output;
 155     }
 156 
 157     private boolean isEnabled(DebugContext.Scope scope, DebugFilter filter) {
 158         return getLevel(scope, filter) > 0;
 159     }
 160 
 161     private int getLevel(DebugContext.Scope scope, DebugFilter filter) {
 162         int level;
 163         if (filter == null) {
 164             level = 0;
 165         } else {
 166             String currentScope = scope.getQualifiedName();
 167             level = filter.matchLevel(currentScope);
 168         }
 169         if (level >= 0 && !checkMethodFilter(scope)) {
 170             level = -1;
 171         }
 172         return level;
 173     }
 174 
 175     private boolean isEnabledForMethod(DebugContext.Scope scope, DebugFilter filter) {
 176         return filter != null && checkMethodFilter(scope);
 177     }
 178 
 179     private boolean checkMethodFilter(DebugContext.Scope scope) {
 180         if (methodFilter == null) {
 181             return true;
 182         } else {
 183             JavaMethod lastMethod = null;
 184             Iterable<Object> context = scope.getCurrentContext();
 185             for (Object o : context) {
 186                 if (methodFilter != null) {
 187                     JavaMethod method = DebugConfig.asJavaMethod(o);
 188                     if (method != null) {
 189                         if (!DebugOptions.MethodFilterRootOnly.getValue(options)) {
 190                             if (org.graalvm.compiler.debug.MethodFilter.matches(methodFilter, method)) {
 191                                 return true;
 192                             }
 193                         } else {
 194                             /*
 195                              * The context values operate as a stack so if we want MethodFilter to
 196                              * only apply to the root method we have to check only the last method
 197                              * seen.
 198                              */
 199                             lastMethod = method;
 200                         }
 201                     }
 202                 }
 203             }
 204             if (lastMethod != null && org.graalvm.compiler.debug.MethodFilter.matches(methodFilter, lastMethod)) {
 205                 return true;
 206             }
 207             return false;
 208         }
 209     }
 210 
 211     @Override
 212     public String toString() {
 213         StringBuilder sb = new StringBuilder();
 214         sb.append("Debug config:");
 215         add(sb, "Log", logFilter);
 216         add(sb, "Count", countFilter);
 217         add(sb, "Time", timerFilter);
 218         add(sb, "Dump", dumpFilter);
 219         add(sb, "MethodFilter", methodFilter);
 220         return sb.toString();
 221     }
 222 
 223     private static void add(StringBuilder sb, String name, Object filter) {
 224         if (filter != null) {
 225             sb.append(' ');
 226             sb.append(name);
 227             sb.append('=');
 228             if (filter instanceof Object[]) {
 229                 sb.append(Arrays.toString((Object[]) filter));
 230             } else {
 231                 sb.append(String.valueOf(filter));
 232             }
 233         }
 234     }
 235 
 236     @Override
 237     public RuntimeException interceptException(DebugContext debug, Throwable e) {
 238         if (e instanceof BailoutException && !DebugOptions.InterceptBailout.getValue(options)) {
 239             return null;
 240         }
 241 
 242         OptionValues interceptOptions = new OptionValues(options,
 243                         DebugOptions.Count, null,
 244                         DebugOptions.Time, null,
 245                         DebugOptions.TrackMemUse, null,
 246                         DebugOptions.Verify, null,
 247                         DebugOptions.Dump, ":" + BASIC_LEVEL,
 248                         DebugOptions.Log, ":" + BASIC_LEVEL);
 249         DebugConfigImpl config = new DebugConfigImpl(interceptOptions, output, dumpHandlers, verifyHandlers);
 250         ScopeImpl scope = debug.currentScope;
 251         scope.updateFlags(config);
 252         try {
 253             ByteArrayOutputStream baos = new ByteArrayOutputStream();
 254             e.printStackTrace(new PrintStream(baos));
 255             debug.log("Exception raised in scope %s: %s", debug.getCurrentScopeName(), baos);
 256             Map<Object, Object> firstSeen = new IdentityHashMap<>();
 257             for (Object o : debug.context()) {
 258                 // Only dump a context object once.
 259                 if (!firstSeen.containsKey(o)) {
 260                     firstSeen.put(o, o);
 261                     if (DebugOptions.DumpOnError.getValue(options) || DebugOptions.Dump.getValue(options) != null) {
 262                         debug.dump(DebugContext.BASIC_LEVEL, o, "Exception: %s", e);
 263                     } else {
 264                         debug.log("Context obj %s", o);
 265                     }
 266                 }
 267             }
 268         } finally {
 269             scope.updateFlags(this);
 270         }
 271         return null;
 272     }
 273 
 274     @Override
 275     public Collection<DebugDumpHandler> dumpHandlers() {
 276         return dumpHandlers;
 277     }
 278 
 279     @Override
 280     public Collection<DebugVerifyHandler> verifyHandlers() {
 281         return verifyHandlers;
 282     }
 283 }