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 java.util.Arrays; 26 import java.util.regex.Pattern; 27 28 import org.graalvm.compiler.debug.GraalDebugConfig.Options; 29 import org.graalvm.compiler.debug.internal.DebugScope; 30 31 /** 32 * Implements the filter specified by the {@link Options#Dump}, {@link Options#Log}, 33 * {@link Options#Count},{@link Options#MethodMeter} and {@link Options#Time} options. 34 * <p> 35 * These options enable the associated debug facility if their filter matches the 36 * {@linkplain DebugScope#getQualifiedName() name} of the {@linkplain Debug#currentScope() current 37 * scope}. For the {@link Options#Dump} and {@link Options#Log} options, the log or dump level is 38 * set. The {@link Options#Count},{@link Options#MethodMeter} and {@link Options#Time} options don't 39 * have a level, for them {@code level = 0} means disabled and a {@code level > 0} means enabled. 40 * <p> 41 * A filter is a list of comma-separated terms of the form {@code <pattern>[:<level>]}. {@code 42 * <pattern>} is interpreted as a glob pattern if it contains a "*" or "?" character. Otherwise, it 43 * is interpreted as a substring. If {@code <pattern>} is empty, it matches every scope. If {@code : 44 * <level>} is omitted, it defaults to {@link Debug#BASIC_LOG_LEVEL}. The term {@code ~<pattern>} is 45 * a shorthand for {@code <pattern>:0} to disable a debug facility for a pattern. 46 * <p> 47 * The resulting log level of a scope is determined by the <em>last</em> matching term. If no term 48 * matches, the log level is 0 (disabled). A filter with no terms matches every scope with a log 49 * level of {@link Debug#BASIC_LOG_LEVEL}. 50 * 51 * <h2>Examples of filters</h2> 52 * 53 * <ul> 54 * <li>(empty string)<br> 55 * Matches any scope with log level {@link Debug#BASIC_LOG_LEVEL}. 56 * 57 * <li>{@code :1}<br> 58 * Matches any scope with log level 1. 59 * 60 * <li>{@code *}<br> 61 * Matches any scope with log level {@link Debug#BASIC_LOG_LEVEL}. 62 * 63 * <li>{@code CodeGen,CodeInstall}<br> 64 * Matches scopes containing "CodeGen" or "CodeInstall", both with log level 65 * {@link Debug#BASIC_LOG_LEVEL}. 66 * 67 * <li>{@code CodeGen:2,CodeInstall:1}<br> 68 * Matches scopes containing "CodeGen" with log level 2, or "CodeInstall" with log level 1. 69 * 70 * <li>{@code :1,Dead:2}<br> 71 * Matches scopes containing "Dead" with log level 2, and all other scopes with log level 1. 72 * 73 * <li>{@code :1,Dead:0}<br> 74 * Matches all scopes with log level 1, except those containing "Dead". 75 * 76 * <li>{@code Code*}<br> 77 * Matches scopes starting with "Code" with log level {@link Debug#BASIC_LOG_LEVEL}. 78 * 79 * <li>{@code Code,~Dead}<br> 80 * Matches scopes containing "Code" but not "Dead", with log level {@link Debug#BASIC_LOG_LEVEL}. 81 * </ul> 82 */ 83 final class DebugFilter { 84 85 public static DebugFilter parse(String spec) { 86 if (spec == null) { 87 return null; 88 } 89 return new DebugFilter(spec.split(",")); 90 } 91 92 private final Term[] terms; 93 94 private DebugFilter(String[] terms) { 95 if (terms.length == 0) { 96 this.terms = null; 97 } else { 98 this.terms = new Term[terms.length]; 99 for (int i = 0; i < terms.length; i++) { 100 String t = terms[i]; 101 int idx = t.indexOf(':'); 102 103 String pattern; 104 int level; 105 if (idx < 0) { 106 if (t.startsWith("~")) { 107 pattern = t.substring(1); 108 level = 0; 109 } else { 110 pattern = t; 111 level = Debug.BASIC_LOG_LEVEL; 112 } 113 } else { 114 pattern = t.substring(0, idx); 115 if (idx + 1 < t.length()) { 116 String levelString = t.substring(idx + 1); 117 try { 118 level = Integer.parseInt(levelString); 119 } catch (NumberFormatException e) { 120 switch (levelString) { 121 case "basic": 122 level = Debug.BASIC_LOG_LEVEL; 123 break; 124 case "info": 125 level = Debug.INFO_LOG_LEVEL; 126 break; 127 case "verbose": 128 level = Debug.VERBOSE_LOG_LEVEL; 129 break; 130 default: 131 throw new IllegalArgumentException("Unknown dump level: \"" + levelString + "\" expected basic, info, verbose or an integer"); 132 } 133 } 134 135 } else { 136 level = Debug.BASIC_LOG_LEVEL; 137 } 138 } 139 140 this.terms[i] = new Term(pattern, level); 141 } 142 } 143 } 144 145 /** 146 * Check whether a given input is matched by this filter, and determine the log level. 147 */ 148 public int matchLevel(String input) { 149 if (terms == null) { 150 return Debug.BASIC_LOG_LEVEL; 151 } else { 152 int level = 0; 153 for (Term t : terms) { 154 if (t.matches(input)) { 155 level = t.level; 156 } 157 } 158 return level; 159 } 160 } 161 162 @Override 163 public String toString() { 164 StringBuilder buf = new StringBuilder("DebugFilter"); 165 if (terms != null) { 166 buf.append(Arrays.toString(terms)); 167 } else { 168 buf.append("[]"); 169 } 170 return buf.toString(); 171 } 172 173 private static class Term { 174 175 private final Pattern pattern; 176 public final int level; 177 178 Term(String filter, int level) { 179 this.level = level; 180 if (filter.isEmpty()) { 181 this.pattern = null; 182 } else if (filter.contains("*") || filter.contains("?")) { 183 this.pattern = Pattern.compile(MethodFilter.createGlobString(filter)); 184 } else { 185 this.pattern = Pattern.compile(".*" + MethodFilter.createGlobString(filter) + ".*"); 186 } 187 } 188 189 /** 190 * Determines if a given input is matched by this filter. 191 */ 192 public boolean matches(String input) { 193 return pattern == null || pattern.matcher(input).matches(); 194 } 195 196 @Override 197 public String toString() { 198 return (pattern == null ? ".*" : pattern.toString()) + ":" + level; 199 } 200 } 201 }