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 implements AutoCloseable { 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 * Creates an object that will overwrite {@link TTY} for the current thread with a custom 92 * log stream. To revert the overwritten state to how it was before this call, the 93 * {@link #remove()} method must be called on this filter object. 94 */ 95 public Filter(LogStream newStream) { 96 previous = out(); 97 log.set(newStream); 98 } 99 100 /** 101 * Reverts the suppression state of {@link TTY} to how it was before this object was 102 * constructed. 103 */ 104 public void remove() { 105 assert thread == Thread.currentThread(); 106 if (previous != null) { 107 log.set(previous); 108 } 109 } 110 111 @Override 112 public void close() { 113 remove(); 114 } 115 } 116 117 /** 118 * The {@link PrintStream} to which all non-suppressed output from {@link TTY} is written. 119 */ 120 public static final PrintStream out; 121 static { 122 TTYStreamProvider p = GraalServices.loadSingle(TTYStreamProvider.class, false); 123 out = p == null ? System.out : p.getStream(); 124 } 125 126 private static final ThreadLocal<LogStream> log = new ThreadLocal<LogStream>() { 127 128 @Override 129 protected LogStream initialValue() { 130 return new LogStream(out); 131 } 132 }; 133 134 public static boolean isSuppressed() { 135 return log.get() == LogStream.SINK; 136 } 137 138 /** 139 * Gets the thread-local log stream to which the static methods of this class send their output. 140 * This will either be a global log stream or the global {@linkplain LogStream#SINK sink} 141 * depending on whether any suppression {@linkplain Filter filters} are in effect for the 142 * current thread. 143 */ 144 public static LogStream out() { 145 return log.get(); 146 } 147 148 /** 149 * @see LogStream#print(String) 150 */ 151 public static void print(String s) { 152 out().print(s); 153 } 154 155 /** 156 * @see LogStream#print(int) 157 */ 158 public static void print(int i) { 159 out().print(i); 160 } 161 162 /** 163 * @see LogStream#print(long) 164 */ 165 public static void print(long i) { 166 out().print(i); 167 } 168 169 /** 170 * @see LogStream#print(char) 171 */ 172 public static void print(char c) { 173 out().print(c); 174 } 175 176 /** 177 * @see LogStream#print(boolean) 178 */ 179 public static void print(boolean b) { 180 out().print(b); 181 } 182 183 /** 184 * @see LogStream#print(double) 185 */ 186 public static void print(double d) { 187 out().print(d); 188 } 189 190 /** 191 * @see LogStream#print(float) 192 */ 193 public static void print(float f) { 194 out().print(f); 195 } 196 197 /** 198 * @see LogStream#println(String) 199 */ 200 public static void println(String s) { 201 out().println(s); 202 } 203 204 /** 205 * @see LogStream#println() 206 */ 207 public static void println() { 208 out().println(); 209 } 210 211 /** 212 * @see LogStream#println(int) 213 */ 214 public static void println(int i) { 215 out().println(i); 216 } 217 218 /** 219 * @see LogStream#println(long) 220 */ 221 public static void println(long l) { 222 out().println(l); 223 } 224 225 /** 226 * @see LogStream#println(char) 227 */ 228 public static void println(char c) { 229 out().println(c); 230 } 231 232 /** 233 * @see LogStream#println(boolean) 234 */ 235 public static void println(boolean b) { 236 out().println(b); 237 } 238 239 /** 240 * @see LogStream#println(double) 241 */ 242 public static void println(double d) { 243 out().println(d); 244 } 245 246 /** 247 * @see LogStream#println(float) 248 */ 249 public static void println(float f) { 250 out().println(f); 251 } 252 253 public static void printf(String format, Object... args) { 254 out().printf(format, args); 255 } 256 257 public static void println(String format, Object... args) { 258 out().printf(format + "%n", args); 259 } 260 261 public static void fillTo(int i) { 262 out().fillTo(i, ' '); 263 } 264 265 public static void printFields(Class<?> javaClass) { 266 final String className = javaClass.getSimpleName(); 267 TTY.println(className + " {"); 268 for (final Field field : javaClass.getFields()) { 269 printField(field, false); 270 } 271 TTY.println("}"); 272 } 273 274 public static void printField(final Field field, boolean tabbed) { 275 final String fieldName = String.format("%35s", field.getName()); 276 try { 277 String prefix = tabbed ? "" : " " + fieldName + " = "; 278 String postfix = tabbed ? "\t" : "\n"; 279 if (field.getType() == int.class) { 280 TTY.print(prefix + field.getInt(null) + postfix); 281 } else if (field.getType() == boolean.class) { 282 TTY.print(prefix + field.getBoolean(null) + postfix); 283 } else if (field.getType() == float.class) { 284 TTY.print(prefix + field.getFloat(null) + postfix); 285 } else if (field.getType() == String.class) { 286 TTY.print(prefix + field.get(null) + postfix); 287 } else if (field.getType() == Map.class) { 288 Map<?, ?> m = (Map<?, ?>) field.get(null); 289 TTY.print(prefix + printMap(m) + postfix); 290 } else { 291 TTY.print(prefix + field.get(null) + postfix); 292 } 293 } catch (IllegalAccessException e) { 294 // do nothing. 295 } 296 } 297 298 private static String printMap(Map<?, ?> m) { 299 StringBuilder sb = new StringBuilder(); 300 301 List<String> keys = new ArrayList<>(); 302 for (Object key : m.keySet()) { 303 keys.add((String) key); 304 } 305 Collections.sort(keys); 306 307 for (String key : keys) { 308 sb.append(key); 309 sb.append("\t"); 310 sb.append(m.get(key)); 311 sb.append("\n"); 312 } 313 314 return sb.toString(); 315 } 316 317 public static void flush() { 318 out().flush(); 319 } 320 }