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 }