1 /* 2 * Copyright (c) 2009, 2011, 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.io.PrintStream; 26 import java.lang.reflect.Field; 27 import java.util.ArrayList; 28 import java.util.Collections; 29 import java.util.List; 30 import java.util.Map; 31 import java.util.regex.Pattern; 32 33 import org.graalvm.compiler.serviceprovider.GraalServices; 34 35 /** 36 * A collection of static methods for printing debug and informational output to a global 37 * {@link LogStream}. The output can be (temporarily) suppressed per thread through use of a 38 * {@linkplain Filter filter}. 39 */ 40 public class TTY { 41 42 /** 43 * Support for thread-local suppression of {@link TTY}. 44 */ 45 public static class Filter { 46 47 private LogStream previous; 48 private final Thread thread = Thread.currentThread(); 49 50 /** 51 * Creates an object that will suppress {@link TTY} for the current thread if the given 52 * filter does not match the given object. To revert the suppression state to how it was 53 * before this call, the {@link #remove()} method must be called on the suppression object. 54 * 55 * @param filter the pattern for matching. If {@code null}, then the match is successful. If 56 * it starts with "~", then a regular expression 57 * {@linkplain Pattern#matches(String, CharSequence) match} is performed where 58 * the regular expression is specified by {@code filter} without the "~" prefix. 59 * Otherwise, a simple {@linkplain String#contains(CharSequence) substring} match 60 * is performed where {@code filter} is the substring used. 61 * @param object an object whose {@linkplain Object#toString() string} value is matched 62 * against {@code filter} 63 */ 64 public Filter(String filter, Object object) { 65 boolean suppressed = false; 66 if (filter != null) { 67 String input = object.toString(); 68 if (filter.startsWith("~")) { 69 suppressed = !Pattern.matches(filter.substring(1), input); 70 } else { 71 suppressed = !input.contains(filter); 72 } 73 if (suppressed) { 74 previous = out(); 75 log.set(LogStream.SINK); 76 } 77 } 78 } 79 80 /** 81 * Creates an object that will suppress {@link TTY} for the current thread. To revert the 82 * suppression state to how it was before this call, the {@link #remove()} method must be 83 * called on this filter object. 84 */ 85 public Filter() { 86 previous = out(); 87 log.set(LogStream.SINK); 88 } 89 90 /** 91 * Reverts the suppression state of {@link TTY} to how it was before this object was 92 * constructed. 93 */ 94 public void remove() { 95 assert thread == Thread.currentThread(); 96 if (previous != null) { 97 log.set(previous); 98 } 99 } 100 } 101 102 /** 103 * The {@link PrintStream} to which all non-suppressed output from {@link TTY} is written. 104 */ 105 public static final PrintStream out; 106 static { 107 TTYStreamProvider p = GraalServices.loadSingle(TTYStreamProvider.class, false); 108 out = p == null ? System.out : p.getStream(); 109 } 110 111 private static final ThreadLocal<LogStream> log = new ThreadLocal<LogStream>() { 112 113 @Override 114 protected LogStream initialValue() { 115 return new LogStream(out); 116 } 117 }; 118 119 public static boolean isSuppressed() { 120 return log.get() == LogStream.SINK; 121 } 122 123 /** 124 * Gets the thread-local log stream to which the static methods of this class send their output. 125 * This will either be a global log stream or the global {@linkplain LogStream#SINK sink} 126 * depending on whether any suppression {@linkplain Filter filters} are in effect for the 127 * current thread. 128 */ 129 public static LogStream out() { 130 return log.get(); 131 } 132 133 /** 134 * @see LogStream#print(String) 135 */ 136 public static void print(String s) { 137 out().print(s); 138 } 139 140 /** 141 * @see LogStream#print(int) 142 */ 143 public static void print(int i) { 144 out().print(i); 145 } 146 147 /** 148 * @see LogStream#print(long) 149 */ 150 public static void print(long i) { 151 out().print(i); 152 } 153 154 /** 155 * @see LogStream#print(char) 156 */ 157 public static void print(char c) { 158 out().print(c); 159 } 160 161 /** 162 * @see LogStream#print(boolean) 163 */ 164 public static void print(boolean b) { 165 out().print(b); 166 } 167 168 /** 169 * @see LogStream#print(double) 170 */ 171 public static void print(double d) { 172 out().print(d); 173 } 174 175 /** 176 * @see LogStream#print(float) 177 */ 178 public static void print(float f) { 179 out().print(f); 180 } 181 182 /** 183 * @see LogStream#println(String) 184 */ 185 public static void println(String s) { 186 out().println(s); 187 } 188 189 /** 190 * @see LogStream#println() 191 */ 192 public static void println() { 193 out().println(); 194 } 195 196 /** 197 * @see LogStream#println(int) 198 */ 199 public static void println(int i) { 200 out().println(i); 201 } 202 203 /** 204 * @see LogStream#println(long) 205 */ 206 public static void println(long l) { 207 out().println(l); 208 } 209 210 /** 211 * @see LogStream#println(char) 212 */ 213 public static void println(char c) { 214 out().println(c); 215 } 216 217 /** 218 * @see LogStream#println(boolean) 219 */ 220 public static void println(boolean b) { 221 out().println(b); 222 } 223 224 /** 225 * @see LogStream#println(double) 226 */ 227 public static void println(double d) { 228 out().println(d); 229 } 230 231 /** 232 * @see LogStream#println(float) 233 */ 234 public static void println(float f) { 235 out().println(f); 236 } 237 238 public static void printf(String format, Object... args) { 239 out().printf(format, args); 240 } 241 242 public static void println(String format, Object... args) { 243 out().printf(format + "%n", args); 244 } 245 246 public static void fillTo(int i) { 247 out().fillTo(i, ' '); 248 } 249 250 public static void printFields(Class<?> javaClass) { 251 final String className = javaClass.getSimpleName(); 252 TTY.println(className + " {"); 253 for (final Field field : javaClass.getFields()) { 254 printField(field, false); 255 } 256 TTY.println("}"); 257 } 258 259 public static void printField(final Field field, boolean tabbed) { 260 final String fieldName = String.format("%35s", field.getName()); 261 try { 262 String prefix = tabbed ? "" : " " + fieldName + " = "; 263 String postfix = tabbed ? "\t" : "\n"; 264 if (field.getType() == int.class) { 265 TTY.print(prefix + field.getInt(null) + postfix); 266 } else if (field.getType() == boolean.class) { 267 TTY.print(prefix + field.getBoolean(null) + postfix); 268 } else if (field.getType() == float.class) { 269 TTY.print(prefix + field.getFloat(null) + postfix); 270 } else if (field.getType() == String.class) { 271 TTY.print(prefix + field.get(null) + postfix); 272 } else if (field.getType() == Map.class) { 273 Map<?, ?> m = (Map<?, ?>) field.get(null); 274 TTY.print(prefix + printMap(m) + postfix); 275 } else { 276 TTY.print(prefix + field.get(null) + postfix); 277 } 278 } catch (IllegalAccessException e) { 279 // do nothing. 280 } 281 } 282 283 private static String printMap(Map<?, ?> m) { 284 StringBuilder sb = new StringBuilder(); 285 286 List<String> keys = new ArrayList<>(); 287 for (Object key : m.keySet()) { 288 keys.add((String) key); 289 } 290 Collections.sort(keys); 291 292 for (String key : keys) { 293 sb.append(key); 294 sb.append("\t"); 295 sb.append(m.get(key)); 296 sb.append("\n"); 297 } 298 299 return sb.toString(); 300 } 301 302 public static void flush() { 303 out().flush(); 304 } 305 }