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