001/* 002 * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.debug; 024 025import java.io.*; 026import java.lang.reflect.*; 027import java.util.*; 028import java.util.regex.*; 029 030import jdk.internal.jvmci.service.*; 031 032/** 033 * A collection of static methods for printing debug and informational output to a global 034 * {@link LogStream}. The output can be (temporarily) suppressed per thread through use of a 035 * {@linkplain Filter filter}. 036 */ 037public class TTY { 038 039 /** 040 * Support for thread-local suppression of {@link TTY}. 041 */ 042 public static class Filter { 043 044 private LogStream previous; 045 private final Thread thread = Thread.currentThread(); 046 047 /** 048 * Creates an object that will suppress {@link TTY} for the current thread if the given 049 * filter does not match the given object. To revert the suppression state to how it was 050 * before this call, the {@link #remove()} method must be called on the suppression object. 051 * 052 * @param filter the pattern for matching. If {@code null}, then the match is successful. If 053 * it starts with "~", then a regular expression 054 * {@linkplain Pattern#matches(String, CharSequence) match} is performed where 055 * the regular expression is specified by {@code filter} without the "~" prefix. 056 * Otherwise, a simple {@linkplain String#contains(CharSequence) substring} match 057 * is performed where {@code filter} is the substring used. 058 * @param object an object whose {@linkplain Object#toString() string} value is matched 059 * against {@code filter} 060 */ 061 public Filter(String filter, Object object) { 062 boolean suppressed = false; 063 if (filter != null) { 064 String input = object.toString(); 065 if (filter.startsWith("~")) { 066 suppressed = !Pattern.matches(filter.substring(1), input); 067 } else { 068 suppressed = !input.contains(filter); 069 } 070 if (suppressed) { 071 previous = out(); 072 log.set(LogStream.SINK); 073 } 074 } 075 } 076 077 /** 078 * Creates an object that will suppress {@link TTY} for the current thread. To revert the 079 * suppression state to how it was before this call, the {@link #remove()} method must be 080 * called on this filter object. 081 */ 082 public Filter() { 083 previous = out(); 084 log.set(LogStream.SINK); 085 } 086 087 /** 088 * Reverts the suppression state of {@link TTY} to how it was before this object was 089 * constructed. 090 */ 091 public void remove() { 092 assert thread == Thread.currentThread(); 093 if (previous != null) { 094 log.set(previous); 095 } 096 } 097 } 098 099 /** 100 * The {@link PrintStream} to which all non-suppressed output from {@link TTY} is written. 101 */ 102 public static final PrintStream out; 103 static { 104 TTYStreamProvider p = Services.loadSingle(TTYStreamProvider.class, false); 105 out = p == null ? System.out : p.getStream(); 106 } 107 108 private static final ThreadLocal<LogStream> log = new ThreadLocal<LogStream>() { 109 110 @Override 111 protected LogStream initialValue() { 112 return new LogStream(out); 113 } 114 }; 115 116 public static boolean isSuppressed() { 117 return log.get() == LogStream.SINK; 118 } 119 120 /** 121 * Gets the thread-local log stream to which the static methods of this class send their output. 122 * This will either be a global log stream or the global {@linkplain LogStream#SINK sink} 123 * depending on whether any suppression {@linkplain Filter filters} are in effect for the 124 * current thread. 125 */ 126 public static LogStream out() { 127 return log.get(); 128 } 129 130 /** 131 * @see LogStream#print(String) 132 */ 133 public static void print(String s) { 134 out().print(s); 135 } 136 137 /** 138 * @see LogStream#print(int) 139 */ 140 public static void print(int i) { 141 out().print(i); 142 } 143 144 /** 145 * @see LogStream#print(long) 146 */ 147 public static void print(long i) { 148 out().print(i); 149 } 150 151 /** 152 * @see LogStream#print(char) 153 */ 154 public static void print(char c) { 155 out().print(c); 156 } 157 158 /** 159 * @see LogStream#print(boolean) 160 */ 161 public static void print(boolean b) { 162 out().print(b); 163 } 164 165 /** 166 * @see LogStream#print(double) 167 */ 168 public static void print(double d) { 169 out().print(d); 170 } 171 172 /** 173 * @see LogStream#print(float) 174 */ 175 public static void print(float f) { 176 out().print(f); 177 } 178 179 /** 180 * @see LogStream#println(String) 181 */ 182 public static void println(String s) { 183 out().println(s); 184 } 185 186 /** 187 * @see LogStream#println() 188 */ 189 public static void println() { 190 out().println(); 191 } 192 193 /** 194 * @see LogStream#println(int) 195 */ 196 public static void println(int i) { 197 out().println(i); 198 } 199 200 /** 201 * @see LogStream#println(long) 202 */ 203 public static void println(long l) { 204 out().println(l); 205 } 206 207 /** 208 * @see LogStream#println(char) 209 */ 210 public static void println(char c) { 211 out().println(c); 212 } 213 214 /** 215 * @see LogStream#println(boolean) 216 */ 217 public static void println(boolean b) { 218 out().println(b); 219 } 220 221 /** 222 * @see LogStream#println(double) 223 */ 224 public static void println(double d) { 225 out().println(d); 226 } 227 228 /** 229 * @see LogStream#println(float) 230 */ 231 public static void println(float f) { 232 out().println(f); 233 } 234 235 public static void print(String format, Object... args) { 236 out().printf(format, args); 237 } 238 239 public static void println(String format, Object... args) { 240 out().printf(format + "%n", args); 241 } 242 243 public static void fillTo(int i) { 244 out().fillTo(i, ' '); 245 } 246 247 public static void printFields(Class<?> javaClass) { 248 final String className = javaClass.getSimpleName(); 249 TTY.println(className + " {"); 250 for (final Field field : javaClass.getFields()) { 251 printField(field, false); 252 } 253 TTY.println("}"); 254 } 255 256 public static void printField(final Field field, boolean tabbed) { 257 final String fieldName = String.format("%35s", field.getName()); 258 try { 259 String prefix = tabbed ? "" : " " + fieldName + " = "; 260 String postfix = tabbed ? "\t" : "\n"; 261 if (field.getType() == int.class) { 262 TTY.print(prefix + field.getInt(null) + postfix); 263 } else if (field.getType() == boolean.class) { 264 TTY.print(prefix + field.getBoolean(null) + postfix); 265 } else if (field.getType() == float.class) { 266 TTY.print(prefix + field.getFloat(null) + postfix); 267 } else if (field.getType() == String.class) { 268 TTY.print(prefix + field.get(null) + postfix); 269 } else if (field.getType() == Map.class) { 270 Map<?, ?> m = (Map<?, ?>) field.get(null); 271 TTY.print(prefix + printMap(m) + postfix); 272 } else { 273 TTY.print(prefix + field.get(null) + postfix); 274 } 275 } catch (IllegalAccessException e) { 276 // do nothing. 277 } 278 } 279 280 private static String printMap(Map<?, ?> m) { 281 StringBuilder sb = new StringBuilder(); 282 283 List<String> keys = new ArrayList<>(); 284 for (Object key : m.keySet()) { 285 keys.add((String) key); 286 } 287 Collections.sort(keys); 288 289 for (String key : keys) { 290 sb.append(key); 291 sb.append("\t"); 292 sb.append(m.get(key)); 293 sb.append("\n"); 294 } 295 296 return sb.toString(); 297 } 298 299 public static void flush() { 300 out().flush(); 301 } 302}