1 /*
   2  * Copyright (c) 2012, 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 static org.graalvm.compiler.debug.DelegatingDebugConfig.Feature.INTERCEPT;
  26 import static org.graalvm.compiler.debug.DelegatingDebugConfig.Feature.LOG_METHOD;
  27 import static java.util.FormattableFlags.LEFT_JUSTIFY;
  28 import static java.util.FormattableFlags.UPPERCASE;
  29 
  30 import java.io.PrintStream;
  31 import java.util.ArrayList;
  32 import java.util.Collection;
  33 import java.util.Collections;
  34 import java.util.ConcurrentModificationException;
  35 import java.util.HashSet;
  36 import java.util.List;
  37 import java.util.Map;
  38 import java.util.Objects;
  39 import java.util.Set;
  40 import java.util.concurrent.Callable;
  41 import java.util.concurrent.TimeUnit;
  42 
  43 import org.graalvm.compiler.debug.DelegatingDebugConfig.Level;
  44 import org.graalvm.compiler.debug.internal.CounterImpl;
  45 import org.graalvm.compiler.debug.internal.DebugHistogramImpl;
  46 import org.graalvm.compiler.debug.internal.DebugScope;
  47 import org.graalvm.compiler.debug.internal.MemUseTrackerImpl;
  48 import org.graalvm.compiler.debug.internal.TimerImpl;
  49 import org.graalvm.compiler.debug.internal.method.MethodMetricsImpl;
  50 import org.graalvm.compiler.serviceprovider.GraalServices;
  51 
  52 import jdk.vm.ci.meta.ResolvedJavaMethod;
  53 
  54 /**
  55  * Scope based debugging facility.
  56  *
  57  * This facility is {@linkplain #isEnabled() enabled} if any of the following hold when the
  58  * {@link Debug} class is initialized:
  59  * <ul>
  60  * <li>assertions are enabled for the {@link Debug} class</li>
  61  * <li>{@link Debug#params}{@code .enable} is {@code true}</li>
  62  * </ul>
  63  */
  64 public class Debug {
  65 
  66     private static final Params params = new Params();
  67 
  68     static {
  69         // Load the service providers that may want to modify any of the
  70         // parameters encapsulated by the Initialization class below.
  71         for (DebugInitializationParticipant p : GraalServices.load(DebugInitializationParticipant.class)) {
  72             p.apply(params);
  73         }
  74     }
  75 
  76     /**
  77      * The parameters for configuring the initialization of {@link Debug} class.
  78      */
  79     public static class Params {
  80         public boolean enable;
  81         public boolean enableMethodFilter;
  82         public boolean enableUnscopedTimers;
  83         public boolean enableUnscopedCounters;
  84         public boolean enableUnscopedMethodMetrics;
  85         public boolean enableUnscopedMemUseTrackers;
  86         public boolean interceptCount;
  87         public boolean interceptTime;
  88         public boolean interceptMem;
  89     }
  90 
  91     @SuppressWarnings("all")
  92     private static boolean initialize() {
  93         boolean assertionsEnabled = false;
  94         assert assertionsEnabled = true;
  95         return assertionsEnabled || params.enable || GraalDebugConfig.Options.ForceDebugEnable.getValue();
  96     }
  97 
  98     private static final boolean ENABLED = initialize();
  99 
 100     public static boolean isEnabled() {
 101         return ENABLED;
 102     }
 103 
 104     public static boolean isDumpEnabledForMethod() {
 105         if (!ENABLED) {
 106             return false;
 107         }
 108         DebugConfig config = DebugScope.getConfig();
 109         if (config == null) {
 110             return false;
 111         }
 112         return config.isDumpEnabledForMethod();
 113     }
 114 
 115     public static final int BASIC_LOG_LEVEL = 1;
 116     public static final int INFO_LOG_LEVEL = 2;
 117     public static final int VERBOSE_LOG_LEVEL = 3;
 118     public static final int DETAILED_LOG_LEVEL = 4;
 119     public static final int VERY_DETAILED_LOG_LEVEL = 5;
 120 
 121     public static boolean isDumpEnabled(int dumpLevel) {
 122         return ENABLED && DebugScope.getInstance().isDumpEnabled(dumpLevel);
 123     }
 124 
 125     /**
 126      * Determines if verification is enabled in the current method, regardless of the
 127      * {@linkplain Debug#currentScope() current debug scope}.
 128      *
 129      * @see Debug#verify(Object, String)
 130      */
 131     public static boolean isVerifyEnabledForMethod() {
 132         if (!ENABLED) {
 133             return false;
 134         }
 135         DebugConfig config = DebugScope.getConfig();
 136         if (config == null) {
 137             return false;
 138         }
 139         return config.isVerifyEnabledForMethod();
 140     }
 141 
 142     /**
 143      * Determines if verification is enabled in the {@linkplain Debug#currentScope() current debug
 144      * scope}.
 145      *
 146      * @see Debug#verify(Object, String)
 147      */
 148     public static boolean isVerifyEnabled() {
 149         return ENABLED && DebugScope.getInstance().isVerifyEnabled();
 150     }
 151 
 152     public static boolean isCountEnabled() {
 153         return ENABLED && DebugScope.getInstance().isCountEnabled();
 154     }
 155 
 156     public static boolean isTimeEnabled() {
 157         return ENABLED && DebugScope.getInstance().isTimeEnabled();
 158     }
 159 
 160     public static boolean isMemUseTrackingEnabled() {
 161         return ENABLED && DebugScope.getInstance().isMemUseTrackingEnabled();
 162     }
 163 
 164     public static boolean isLogEnabledForMethod() {
 165         if (!ENABLED) {
 166             return false;
 167         }
 168         DebugConfig config = DebugScope.getConfig();
 169         if (config == null) {
 170             return false;
 171         }
 172         return config.isLogEnabledForMethod();
 173     }
 174 
 175     public static boolean isLogEnabled() {
 176         return isLogEnabled(BASIC_LOG_LEVEL);
 177     }
 178 
 179     public static boolean isLogEnabled(int logLevel) {
 180         return ENABLED && DebugScope.getInstance().isLogEnabled(logLevel);
 181     }
 182 
 183     public static boolean isMethodMeterEnabled() {
 184         return ENABLED && DebugScope.getInstance().isMethodMeterEnabled();
 185     }
 186 
 187     @SuppressWarnings("unused")
 188     public static Runnable decorateDebugRoot(Runnable runnable, String name, DebugConfig config) {
 189         return runnable;
 190     }
 191 
 192     @SuppressWarnings("unused")
 193     public static <T> Callable<T> decorateDebugRoot(Callable<T> callable, String name, DebugConfig config) {
 194         return callable;
 195     }
 196 
 197     @SuppressWarnings("unused")
 198     public static Runnable decorateScope(Runnable runnable, String name, Object... context) {
 199         return runnable;
 200     }
 201 
 202     @SuppressWarnings("unused")
 203     public static <T> Callable<T> decorateScope(Callable<T> callable, String name, Object... context) {
 204         return callable;
 205     }
 206 
 207     /**
 208      * Gets a string composed of the names in the current nesting of debug
 209      * {@linkplain #scope(Object) scopes} separated by {@code '.'}.
 210      */
 211     public static String currentScope() {
 212         if (ENABLED) {
 213             return DebugScope.getInstance().getQualifiedName();
 214         } else {
 215             return "";
 216         }
 217     }
 218 
 219     /**
 220      * Represents a debug scope entered by {@link Debug#scope(Object)} or
 221      * {@link Debug#sandbox(CharSequence, DebugConfig, Object...)}. Leaving the scope is achieved
 222      * via {@link #close()}.
 223      */
 224     public interface Scope extends AutoCloseable {
 225         @Override
 226         void close();
 227     }
 228 
 229     /**
 230      * Creates and enters a new debug scope which will be a child of the current debug scope.
 231      * <p>
 232      * It is recommended to use the try-with-resource statement for managing entering and leaving
 233      * debug scopes. For example:
 234      *
 235      * <pre>
 236      * try (Scope s = Debug.scope(&quot;InliningGraph&quot;, inlineeGraph)) {
 237      *     ...
 238      * } catch (Throwable e) {
 239      *     throw Debug.handle(e);
 240      * }
 241      * </pre>
 242      *
 243      * The {@code name} argument is subject to the following type based conversion before having
 244      * {@link Object#toString()} called on it:
 245      *
 246      * <pre>
 247      *     Type          | Conversion
 248      * ------------------+-----------------
 249      *  java.lang.Class  | arg.getSimpleName()
 250      *                   |
 251      * </pre>
 252      *
 253      * @param name the name of the new scope
 254      * @param contextObjects an array of object to be appended to the {@linkplain #context()
 255      *            current} debug context
 256      * @throws Throwable used to enforce a catch block.
 257      * @return the scope entered by this method which will be exited when its {@link Scope#close()}
 258      *         method is called
 259      */
 260     public static Scope scope(Object name, Object[] contextObjects) throws Throwable {
 261         if (ENABLED) {
 262             return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null, contextObjects);
 263         } else {
 264             return null;
 265         }
 266     }
 267 
 268     /**
 269      * Similar to {@link #scope(Object, Object[])} but without context objects. Therefore the catch
 270      * block can be omitted.
 271      *
 272      * @see #scope(Object, Object[])
 273      */
 274     public static Scope scope(Object name) {
 275         if (ENABLED) {
 276             return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null);
 277         } else {
 278             return null;
 279         }
 280     }
 281 
 282     public static Scope methodMetricsScope(Object name, DebugScope.ExtraInfo metaInfo, boolean newId, Object... context) {
 283         if (ENABLED) {
 284             return DebugScope.getInstance().enhanceWithExtraInfo(convertFormatArg(name).toString(), metaInfo, newId, context);
 285         } else {
 286             return null;
 287         }
 288     }
 289 
 290     /**
 291      * @see #scope(Object, Object[])
 292      * @param context an object to be appended to the {@linkplain #context() current} debug context
 293      */
 294     public static Scope scope(Object name, Object context) throws Throwable {
 295         if (ENABLED) {
 296             return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null, context);
 297         } else {
 298             return null;
 299         }
 300     }
 301 
 302     /**
 303      * @see #scope(Object, Object[])
 304      * @param context1 first object to be appended to the {@linkplain #context() current} debug
 305      *            context
 306      * @param context2 second object to be appended to the {@linkplain #context() current} debug
 307      *            context
 308      */
 309     public static Scope scope(Object name, Object context1, Object context2) throws Throwable {
 310         if (ENABLED) {
 311             return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null, context1, context2);
 312         } else {
 313             return null;
 314         }
 315     }
 316 
 317     /**
 318      * @see #scope(Object, Object[])
 319      * @param context1 first object to be appended to the {@linkplain #context() current} debug
 320      *            context
 321      * @param context2 second object to be appended to the {@linkplain #context() current} debug
 322      *            context
 323      * @param context3 third object to be appended to the {@linkplain #context() current} debug
 324      *            context
 325      */
 326     public static Scope scope(Object name, Object context1, Object context2, Object context3) throws Throwable {
 327         if (ENABLED) {
 328             return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null, context1, context2, context3);
 329         } else {
 330             return null;
 331         }
 332     }
 333 
 334     /**
 335      * Creates and enters a new debug scope which will be disjoint from the current debug scope.
 336      * <p>
 337      * It is recommended to use the try-with-resource statement for managing entering and leaving
 338      * debug scopes. For example:
 339      *
 340      * <pre>
 341      * try (Scope s = Debug.sandbox(&quot;CompilingStub&quot;, null, stubGraph)) {
 342      *     ...
 343      * } catch (Throwable e) {
 344      *     throw Debug.handle(e);
 345      * }
 346      * </pre>
 347      *
 348      * @param name the name of the new scope
 349      * @param config the debug configuration to use for the new scope
 350      * @param context objects to be appended to the {@linkplain #context() current} debug context
 351      * @return the scope entered by this method which will be exited when its {@link Scope#close()}
 352      *         method is called
 353      */
 354     public static Scope sandbox(CharSequence name, DebugConfig config, Object... context) throws Throwable {
 355         if (ENABLED) {
 356             DebugConfig sandboxConfig = config == null ? silentConfig() : config;
 357             return DebugScope.getInstance().scope(name, sandboxConfig, context);
 358         } else {
 359             return null;
 360         }
 361     }
 362 
 363     public static Scope forceLog() throws Throwable {
 364         ArrayList<Object> context = new ArrayList<>();
 365         for (Object obj : context()) {
 366             context.add(obj);
 367         }
 368         return Debug.sandbox("forceLog", new DelegatingDebugConfig().override(Level.LOG, Integer.MAX_VALUE).enable(LOG_METHOD), context.toArray());
 369     }
 370 
 371     /**
 372      * Opens a scope in which exception {@linkplain DebugConfig#interceptException(Throwable)
 373      * interception} is disabled. It is recommended to use the try-with-resource statement for
 374      * managing entering and leaving such scopes:
 375      *
 376      * <pre>
 377      * try (DebugConfigScope s = Debug.disableIntercept()) {
 378      *     ...
 379      * }
 380      * </pre>
 381      *
 382      * This is particularly useful to suppress extraneous output in JUnit tests that are expected to
 383      * throw an exception.
 384      */
 385     public static DebugConfigScope disableIntercept() {
 386         return Debug.setConfig(new DelegatingDebugConfig().disable(INTERCEPT));
 387     }
 388 
 389     /**
 390      * Handles an exception in the context of the debug scope just exited. The just exited scope
 391      * must have the current scope as its parent which will be the case if the try-with-resource
 392      * pattern recommended by {@link #scope(Object)} and
 393      * {@link #sandbox(CharSequence, DebugConfig, Object...)} is used
 394      *
 395      * @see #scope(Object, Object[])
 396      * @see #sandbox(CharSequence, DebugConfig, Object...)
 397      */
 398     public static RuntimeException handle(Throwable exception) {
 399         if (ENABLED) {
 400             return DebugScope.getInstance().handle(exception);
 401         } else {
 402             if (exception instanceof Error) {
 403                 throw (Error) exception;
 404             }
 405             if (exception instanceof RuntimeException) {
 406                 throw (RuntimeException) exception;
 407             }
 408             throw new RuntimeException(exception);
 409         }
 410     }
 411 
 412     public static void log(String msg) {
 413         log(BASIC_LOG_LEVEL, msg);
 414     }
 415 
 416     /**
 417      * Prints a message to the current debug scope's logging stream if logging is enabled.
 418      *
 419      * @param msg the message to log
 420      */
 421     public static void log(int logLevel, String msg) {
 422         if (ENABLED) {
 423             DebugScope.getInstance().log(logLevel, msg);
 424         }
 425     }
 426 
 427     public static void log(String format, Object arg) {
 428         log(BASIC_LOG_LEVEL, format, arg);
 429     }
 430 
 431     /**
 432      * Prints a message to the current debug scope's logging stream if logging is enabled.
 433      *
 434      * @param format a format string
 435      * @param arg the argument referenced by the format specifiers in {@code format}
 436      */
 437     public static void log(int logLevel, String format, Object arg) {
 438         if (ENABLED) {
 439             DebugScope.getInstance().log(logLevel, format, arg);
 440         }
 441     }
 442 
 443     public static void log(String format, int arg) {
 444         log(BASIC_LOG_LEVEL, format, arg);
 445     }
 446 
 447     /**
 448      * Prints a message to the current debug scope's logging stream if logging is enabled.
 449      *
 450      * @param format a format string
 451      * @param arg the argument referenced by the format specifiers in {@code format}
 452      */
 453     public static void log(int logLevel, String format, int arg) {
 454         if (ENABLED) {
 455             DebugScope.getInstance().log(logLevel, format, arg);
 456         }
 457     }
 458 
 459     public static void log(String format, Object arg1, Object arg2) {
 460         log(BASIC_LOG_LEVEL, format, arg1, arg2);
 461     }
 462 
 463     /**
 464      * @see #log(int, String, Object)
 465      */
 466     public static void log(int logLevel, String format, Object arg1, Object arg2) {
 467         if (ENABLED) {
 468             DebugScope.getInstance().log(logLevel, format, arg1, arg2);
 469         }
 470     }
 471 
 472     public static void log(String format, int arg1, Object arg2) {
 473         log(BASIC_LOG_LEVEL, format, arg1, arg2);
 474     }
 475 
 476     /**
 477      * @see #log(int, String, Object)
 478      */
 479     public static void log(int logLevel, String format, int arg1, Object arg2) {
 480         if (ENABLED) {
 481             DebugScope.getInstance().log(logLevel, format, arg1, arg2);
 482         }
 483     }
 484 
 485     public static void log(String format, Object arg1, int arg2) {
 486         log(BASIC_LOG_LEVEL, format, arg1, arg2);
 487     }
 488 
 489     /**
 490      * @see #log(int, String, Object)
 491      */
 492     public static void log(int logLevel, String format, Object arg1, int arg2) {
 493         if (ENABLED) {
 494             DebugScope.getInstance().log(logLevel, format, arg1, arg2);
 495         }
 496     }
 497 
 498     public static void log(String format, int arg1, int arg2) {
 499         log(BASIC_LOG_LEVEL, format, arg1, arg2);
 500     }
 501 
 502     /**
 503      * @see #log(int, String, Object)
 504      */
 505     public static void log(int logLevel, String format, int arg1, int arg2) {
 506         if (ENABLED) {
 507             DebugScope.getInstance().log(logLevel, format, arg1, arg2);
 508         }
 509     }
 510 
 511     public static void log(String format, Object arg1, Object arg2, Object arg3) {
 512         log(BASIC_LOG_LEVEL, format, arg1, arg2, arg3);
 513     }
 514 
 515     /**
 516      * @see #log(int, String, Object)
 517      */
 518     public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3) {
 519         if (ENABLED) {
 520             DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3);
 521         }
 522     }
 523 
 524     public static void log(String format, int arg1, int arg2, int arg3) {
 525         log(BASIC_LOG_LEVEL, format, arg1, arg2, arg3);
 526     }
 527 
 528     /**
 529      * @see #log(int, String, Object)
 530      */
 531     public static void log(int logLevel, String format, int arg1, int arg2, int arg3) {
 532         if (ENABLED) {
 533             DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3);
 534         }
 535     }
 536 
 537     public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4) {
 538         log(BASIC_LOG_LEVEL, format, arg1, arg2, arg3, arg4);
 539     }
 540 
 541     /**
 542      * @see #log(int, String, Object)
 543      */
 544     public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4) {
 545         if (ENABLED) {
 546             DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4);
 547         }
 548     }
 549 
 550     public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
 551         log(BASIC_LOG_LEVEL, format, arg1, arg2, arg3, arg4, arg5);
 552     }
 553 
 554     /**
 555      * @see #log(int, String, Object)
 556      */
 557     public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
 558         if (ENABLED) {
 559             DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4, arg5);
 560         }
 561     }
 562 
 563     public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
 564         log(BASIC_LOG_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6);
 565     }
 566 
 567     /**
 568      * @see #log(int, String, Object)
 569      */
 570     public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
 571         if (ENABLED) {
 572             DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6);
 573         }
 574     }
 575 
 576     public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7) {
 577         log(BASIC_LOG_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
 578     }
 579 
 580     public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8) {
 581         log(BASIC_LOG_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
 582     }
 583 
 584     /**
 585      * @see #log(int, String, Object)
 586      */
 587     public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7) {
 588         if (ENABLED) {
 589             DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
 590         }
 591     }
 592 
 593     public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8) {
 594         if (ENABLED) {
 595             DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
 596         }
 597     }
 598 
 599     public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9) {
 600         log(BASIC_LOG_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
 601     }
 602 
 603     public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9) {
 604         if (ENABLED) {
 605             DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
 606         }
 607     }
 608 
 609     public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9, Object arg10) {
 610         log(BASIC_LOG_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
 611     }
 612 
 613     public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9, Object arg10) {
 614         if (ENABLED) {
 615             DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
 616         }
 617     }
 618 
 619     public static void logv(String format, Object... args) {
 620         logv(BASIC_LOG_LEVEL, format, args);
 621     }
 622 
 623     /**
 624      * Prints a message to the current debug scope's logging stream. This method must only be called
 625      * if debugging is {@linkplain Debug#isEnabled() enabled} as it incurs allocation at the call
 626      * site. If possible, call one of the other {@code log()} methods in this class that take a
 627      * fixed number of parameters.
 628      *
 629      * @param format a format string
 630      * @param args the arguments referenced by the format specifiers in {@code format}
 631      */
 632     public static void logv(int logLevel, String format, Object... args) {
 633         if (!ENABLED) {
 634             throw new InternalError("Use of Debug.logv() must be guarded by a test of Debug.isEnabled()");
 635         }
 636         DebugScope.getInstance().log(logLevel, format, args);
 637     }
 638 
 639     /**
 640      * This override exists to catch cases when {@link #log(String, Object)} is called with one
 641      * argument bound to a varargs method parameter. It will bind to this method instead of the
 642      * single arg variant and produce a deprecation warning instead of silently wrapping the
 643      * Object[] inside of another Object[].
 644      */
 645     @Deprecated
 646     public static void log(String format, Object[] args) {
 647         assert false : "shouldn't use this";
 648         log(BASIC_LOG_LEVEL, format, args);
 649     }
 650 
 651     /**
 652      * This override exists to catch cases when {@link #log(int, String, Object)} is called with one
 653      * argument bound to a varargs method parameter. It will bind to this method instead of the
 654      * single arg variant and produce a deprecation warning instead of silently wrapping the
 655      * Object[] inside of another Object[].
 656      */
 657     @Deprecated
 658     public static void log(int logLevel, String format, Object[] args) {
 659         assert false : "shouldn't use this";
 660         logv(logLevel, format, args);
 661     }
 662 
 663     public static void dump(int dumpLevel, Object object, String msg) {
 664         if (ENABLED && DebugScope.getInstance().isDumpEnabled(dumpLevel)) {
 665             DebugScope.getInstance().dump(dumpLevel, object, msg);
 666         }
 667     }
 668 
 669     public static void dump(int dumpLevel, Object object, String format, Object arg) {
 670         if (ENABLED && DebugScope.getInstance().isDumpEnabled(dumpLevel)) {
 671             DebugScope.getInstance().dump(dumpLevel, object, format, arg);
 672         }
 673     }
 674 
 675     public static void dump(int dumpLevel, Object object, String format, Object arg1, Object arg2) {
 676         if (ENABLED && DebugScope.getInstance().isDumpEnabled(dumpLevel)) {
 677             DebugScope.getInstance().dump(dumpLevel, object, format, arg1, arg2);
 678         }
 679     }
 680 
 681     public static void dump(int dumpLevel, Object object, String format, Object arg1, Object arg2, Object arg3) {
 682         if (ENABLED && DebugScope.getInstance().isDumpEnabled(dumpLevel)) {
 683             DebugScope.getInstance().dump(dumpLevel, object, format, arg1, arg2, arg3);
 684         }
 685     }
 686 
 687     /**
 688      * This override exists to catch cases when {@link #dump(int, Object, String, Object)} is called
 689      * with one argument bound to a varargs method parameter. It will bind to this method instead of
 690      * the single arg variant and produce a deprecation warning instead of silently wrapping the
 691      * Object[] inside of another Object[].
 692      */
 693     @Deprecated
 694     public static void dump(int dumpLevel, Object object, String format, Object[] args) {
 695         assert false : "shouldn't use this";
 696         if (ENABLED && DebugScope.getInstance().isDumpEnabled(dumpLevel)) {
 697             DebugScope.getInstance().dump(dumpLevel, object, format, args);
 698         }
 699     }
 700 
 701     /**
 702      * Calls all {@link DebugVerifyHandler}s in the current {@linkplain DebugScope#getConfig()
 703      * config} to perform verification on a given object.
 704      *
 705      * @param object object to verify
 706      * @param message description of verification context
 707      *
 708      * @see DebugVerifyHandler#verify(Object, String)
 709      */
 710     public static void verify(Object object, String message) {
 711         if (ENABLED && DebugScope.getInstance().isVerifyEnabled()) {
 712             DebugScope.getInstance().verify(object, message);
 713         }
 714     }
 715 
 716     /**
 717      * Calls all {@link DebugVerifyHandler}s in the current {@linkplain DebugScope#getConfig()
 718      * config} to perform verification on a given object.
 719      *
 720      * @param object object to verify
 721      * @param format a format string for the description of the verification context
 722      * @param arg the argument referenced by the format specifiers in {@code format}
 723      *
 724      * @see DebugVerifyHandler#verify(Object, String)
 725      */
 726     public static void verify(Object object, String format, Object arg) {
 727         if (ENABLED && DebugScope.getInstance().isVerifyEnabled()) {
 728             DebugScope.getInstance().verify(object, format, arg);
 729         }
 730     }
 731 
 732     /**
 733      * This override exists to catch cases when {@link #verify(Object, String, Object)} is called
 734      * with one argument bound to a varargs method parameter. It will bind to this method instead of
 735      * the single arg variant and produce a deprecation warning instead of silently wrapping the
 736      * Object[] inside of another Object[].
 737      */
 738     @Deprecated
 739     public static void verify(Object object, String format, Object[] args) {
 740         assert false : "shouldn't use this";
 741         if (ENABLED && DebugScope.getInstance().isVerifyEnabled()) {
 742             DebugScope.getInstance().verify(object, format, args);
 743         }
 744     }
 745 
 746     /**
 747      * Opens a new indentation level (by adding some spaces) based on the current indentation level.
 748      * This should be used in a {@linkplain Indent try-with-resources} pattern.
 749      *
 750      * @return an object that reverts to the current indentation level when
 751      *         {@linkplain Indent#close() closed} or null if debugging is disabled
 752      * @see #logAndIndent(int, String)
 753      * @see #logAndIndent(int, String, Object)
 754      */
 755     public static Indent indent() {
 756         if (ENABLED) {
 757             DebugScope scope = DebugScope.getInstance();
 758             return scope.pushIndentLogger();
 759         }
 760         return null;
 761     }
 762 
 763     public static Indent logAndIndent(String msg) {
 764         return logAndIndent(BASIC_LOG_LEVEL, msg);
 765     }
 766 
 767     /**
 768      * A convenience function which combines {@link #log(String)} and {@link #indent()}.
 769      *
 770      * @param msg the message to log
 771      * @return an object that reverts to the current indentation level when
 772      *         {@linkplain Indent#close() closed} or null if debugging is disabled
 773      */
 774     public static Indent logAndIndent(int logLevel, String msg) {
 775         if (ENABLED && Debug.isLogEnabled(logLevel)) {
 776             return logvAndIndentInternal(logLevel, msg);
 777         }
 778         return null;
 779     }
 780 
 781     public static Indent logAndIndent(String format, Object arg) {
 782         return logAndIndent(BASIC_LOG_LEVEL, format, arg);
 783     }
 784 
 785     /**
 786      * A convenience function which combines {@link #log(String, Object)} and {@link #indent()}.
 787      *
 788      * @param format a format string
 789      * @param arg the argument referenced by the format specifiers in {@code format}
 790      * @return an object that reverts to the current indentation level when
 791      *         {@linkplain Indent#close() closed} or null if debugging is disabled
 792      */
 793     public static Indent logAndIndent(int logLevel, String format, Object arg) {
 794         if (ENABLED && Debug.isLogEnabled(logLevel)) {
 795             return logvAndIndentInternal(logLevel, format, arg);
 796         }
 797         return null;
 798     }
 799 
 800     public static Indent logAndIndent(String format, int arg) {
 801         return logAndIndent(BASIC_LOG_LEVEL, format, arg);
 802     }
 803 
 804     /**
 805      * A convenience function which combines {@link #log(String, Object)} and {@link #indent()}.
 806      *
 807      * @param format a format string
 808      * @param arg the argument referenced by the format specifiers in {@code format}
 809      * @return an object that reverts to the current indentation level when
 810      *         {@linkplain Indent#close() closed} or null if debugging is disabled
 811      */
 812     public static Indent logAndIndent(int logLevel, String format, int arg) {
 813         if (ENABLED && Debug.isLogEnabled(logLevel)) {
 814             return logvAndIndentInternal(logLevel, format, arg);
 815         }
 816         return null;
 817     }
 818 
 819     public static Indent logAndIndent(String format, int arg1, Object arg2) {
 820         return logAndIndent(BASIC_LOG_LEVEL, format, arg1, arg2);
 821     }
 822 
 823     /**
 824      * @see #logAndIndent(int, String, Object)
 825      */
 826     public static Indent logAndIndent(int logLevel, String format, int arg1, Object arg2) {
 827         if (ENABLED && Debug.isLogEnabled(logLevel)) {
 828             return logvAndIndentInternal(logLevel, format, arg1, arg2);
 829         }
 830         return null;
 831     }
 832 
 833     public static Indent logAndIndent(String format, Object arg1, int arg2) {
 834         return logAndIndent(BASIC_LOG_LEVEL, format, arg1, arg2);
 835     }
 836 
 837     /**
 838      * @see #logAndIndent(int, String, Object)
 839      */
 840     public static Indent logAndIndent(int logLevel, String format, Object arg1, int arg2) {
 841         if (ENABLED && Debug.isLogEnabled(logLevel)) {
 842             return logvAndIndentInternal(logLevel, format, arg1, arg2);
 843         }
 844         return null;
 845     }
 846 
 847     public static Indent logAndIndent(String format, int arg1, int arg2) {
 848         return logAndIndent(BASIC_LOG_LEVEL, format, arg1, arg2);
 849     }
 850 
 851     /**
 852      * @see #logAndIndent(int, String, Object)
 853      */
 854     public static Indent logAndIndent(int logLevel, String format, int arg1, int arg2) {
 855         if (ENABLED && Debug.isLogEnabled(logLevel)) {
 856             return logvAndIndentInternal(logLevel, format, arg1, arg2);
 857         }
 858         return null;
 859     }
 860 
 861     public static Indent logAndIndent(String format, Object arg1, Object arg2) {
 862         return logAndIndent(BASIC_LOG_LEVEL, format, arg1, arg2);
 863     }
 864 
 865     /**
 866      * @see #logAndIndent(int, String, Object)
 867      */
 868     public static Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2) {
 869         if (ENABLED && Debug.isLogEnabled(logLevel)) {
 870             return logvAndIndentInternal(logLevel, format, arg1, arg2);
 871         }
 872         return null;
 873     }
 874 
 875     public static Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3) {
 876         return logAndIndent(BASIC_LOG_LEVEL, format, arg1, arg2, arg3);
 877     }
 878 
 879     /**
 880      * @see #logAndIndent(int, String, Object)
 881      */
 882     public static Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3) {
 883         if (ENABLED && Debug.isLogEnabled(logLevel)) {
 884             return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3);
 885         }
 886         return null;
 887     }
 888 
 889     public static Indent logAndIndent(String format, int arg1, int arg2, int arg3) {
 890         return logAndIndent(BASIC_LOG_LEVEL, format, arg1, arg2, arg3);
 891     }
 892 
 893     /**
 894      * @see #logAndIndent(int, String, Object)
 895      */
 896     public static Indent logAndIndent(int logLevel, String format, int arg1, int arg2, int arg3) {
 897         if (ENABLED && Debug.isLogEnabled(logLevel)) {
 898             return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3);
 899         }
 900         return null;
 901     }
 902 
 903     public static Indent logAndIndent(String format, Object arg1, int arg2, int arg3) {
 904         return logAndIndent(BASIC_LOG_LEVEL, format, arg1, arg2, arg3);
 905     }
 906 
 907     /**
 908      * @see #logAndIndent(int, String, Object)
 909      */
 910     public static Indent logAndIndent(int logLevel, String format, Object arg1, int arg2, int arg3) {
 911         if (ENABLED && Debug.isLogEnabled(logLevel)) {
 912             return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3);
 913         }
 914         return null;
 915     }
 916 
 917     public static Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4) {
 918         return logAndIndent(BASIC_LOG_LEVEL, format, arg1, arg2, arg3, arg4);
 919     }
 920 
 921     /**
 922      * @see #logAndIndent(int, String, Object)
 923      */
 924     public static Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4) {
 925         if (ENABLED && Debug.isLogEnabled(logLevel)) {
 926             return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3, arg4);
 927         }
 928         return null;
 929     }
 930 
 931     public static Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
 932         return logAndIndent(BASIC_LOG_LEVEL, format, arg1, arg2, arg3, arg4, arg5);
 933     }
 934 
 935     /**
 936      * @see #logAndIndent(int, String, Object)
 937      */
 938     public static Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
 939         if (ENABLED && Debug.isLogEnabled(logLevel)) {
 940             return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3, arg4, arg5);
 941         }
 942         return null;
 943     }
 944 
 945     public static Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
 946         return logAndIndent(BASIC_LOG_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6);
 947     }
 948 
 949     /**
 950      * @see #logAndIndent(int, String, Object)
 951      */
 952     public static Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
 953         if (ENABLED && Debug.isLogEnabled(logLevel)) {
 954             return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6);
 955         }
 956         return null;
 957     }
 958 
 959     /**
 960      * A convenience function which combines {@link #logv(int, String, Object...)} and
 961      * {@link #indent()}.
 962      *
 963      * @param format a format string
 964      * @param args the arguments referenced by the format specifiers in {@code format}
 965      * @return an object that reverts to the current indentation level when
 966      *         {@linkplain Indent#close() closed} or null if debugging is disabled
 967      */
 968     public static Indent logvAndIndent(int logLevel, String format, Object... args) {
 969         if (ENABLED) {
 970             if (Debug.isLogEnabled(logLevel)) {
 971                 return logvAndIndentInternal(logLevel, format, args);
 972             }
 973             return null;
 974         }
 975         throw new InternalError("Use of Debug.logvAndIndent() must be guarded by a test of Debug.isEnabled()");
 976     }
 977 
 978     private static Indent logvAndIndentInternal(int logLevel, String format, Object... args) {
 979         assert ENABLED && Debug.isLogEnabled(logLevel) : "must have checked Debug.isLogEnabled()";
 980         DebugScope scope = DebugScope.getInstance();
 981         scope.log(logLevel, format, args);
 982         return scope.pushIndentLogger();
 983     }
 984 
 985     /**
 986      * This override exists to catch cases when {@link #logAndIndent(String, Object)} is called with
 987      * one argument bound to a varargs method parameter. It will bind to this method instead of the
 988      * single arg variant and produce a deprecation warning instead of silently wrapping the
 989      * Object[] inside of another Object[].
 990      */
 991     @Deprecated
 992     public static void logAndIndent(String format, Object[] args) {
 993         assert false : "shouldn't use this";
 994         logAndIndent(BASIC_LOG_LEVEL, format, args);
 995     }
 996 
 997     /**
 998      * This override exists to catch cases when {@link #logAndIndent(int, String, Object)} is called
 999      * with one argument bound to a varargs method parameter. It will bind to this method instead of
1000      * the single arg variant and produce a deprecation warning instead of silently wrapping the
1001      * Object[] inside of another Object[].
1002      */
1003     @Deprecated
1004     public static void logAndIndent(int logLevel, String format, Object[] args) {
1005         assert false : "shouldn't use this";
1006         logvAndIndent(logLevel, format, args);
1007     }
1008 
1009     public static Iterable<Object> context() {
1010         if (ENABLED) {
1011             return DebugScope.getInstance().getCurrentContext();
1012         } else {
1013             return Collections.emptyList();
1014         }
1015     }
1016 
1017     @SuppressWarnings("unchecked")
1018     public static <T> List<T> contextSnapshot(Class<T> clazz) {
1019         if (ENABLED) {
1020             List<T> result = new ArrayList<>();
1021             for (Object o : context()) {
1022                 if (clazz.isInstance(o)) {
1023                     result.add((T) o);
1024                 }
1025             }
1026             return result;
1027         } else {
1028             return Collections.emptyList();
1029         }
1030     }
1031 
1032     /**
1033      * Searches the current debug scope, bottom up, for a context object that is an instance of a
1034      * given type. The first such object found is returned.
1035      */
1036     @SuppressWarnings("unchecked")
1037     public static <T> T contextLookup(Class<T> clazz) {
1038         if (ENABLED) {
1039             for (Object o : context()) {
1040                 if (clazz.isInstance(o)) {
1041                     return ((T) o);
1042                 }
1043             }
1044         }
1045         return null;
1046     }
1047 
1048     /**
1049      * Creates a {@linkplain DebugMemUseTracker memory use tracker} that is enabled iff debugging is
1050      * {@linkplain #isEnabled() enabled}.
1051      * <p>
1052      * A disabled tracker has virtually no overhead.
1053      */
1054     public static DebugMemUseTracker memUseTracker(CharSequence name) {
1055         if (!isUnconditionalMemUseTrackingEnabled && !ENABLED) {
1056             return VOID_MEM_USE_TRACKER;
1057         }
1058         return createMemUseTracker("%s", name, null);
1059     }
1060 
1061     /**
1062      * Creates a debug memory use tracker. Invoking this method is equivalent to:
1063      *
1064      * <pre>
1065      * Debug.memUseTracker(format, arg, null)
1066      * </pre>
1067      *
1068      * except that the string formatting only happens if mem tracking is enabled.
1069      *
1070      * @see #counter(String, Object, Object)
1071      */
1072     public static DebugMemUseTracker memUseTracker(String format, Object arg) {
1073         if (!isUnconditionalMemUseTrackingEnabled && !ENABLED) {
1074             return VOID_MEM_USE_TRACKER;
1075         }
1076         return createMemUseTracker(format, arg, null);
1077     }
1078 
1079     /**
1080      * Creates a debug memory use tracker. Invoking this method is equivalent to:
1081      *
1082      * <pre>
1083      * Debug.memUseTracker(String.format(format, arg1, arg2))
1084      * </pre>
1085      *
1086      * except that the string formatting only happens if memory use tracking is enabled. In
1087      * addition, each argument is subject to the following type based conversion before being passed
1088      * as an argument to {@link String#format(String, Object...)}:
1089      *
1090      * <pre>
1091      *     Type          | Conversion
1092      * ------------------+-----------------
1093      *  java.lang.Class  | arg.getSimpleName()
1094      *                   |
1095      * </pre>
1096      *
1097      * @see #memUseTracker(CharSequence)
1098      */
1099     public static DebugMemUseTracker memUseTracker(String format, Object arg1, Object arg2) {
1100         if (!isUnconditionalMemUseTrackingEnabled && !ENABLED) {
1101             return VOID_MEM_USE_TRACKER;
1102         }
1103         return createMemUseTracker(format, arg1, arg2);
1104     }
1105 
1106     private static DebugMemUseTracker createMemUseTracker(String format, Object arg1, Object arg2) {
1107         String name = formatDebugName(format, arg1, arg2);
1108         return DebugValueFactory.createMemUseTracker(name, !isUnconditionalMemUseTrackingEnabled);
1109     }
1110 
1111     /**
1112      * Creates a {@linkplain DebugCounter counter} that is enabled iff debugging is
1113      * {@linkplain #isEnabled() enabled} or the system property whose name is formed by adding
1114      * {@value #ENABLE_COUNTER_PROPERTY_NAME_PREFIX} to {@code name} is
1115      * {@linkplain Boolean#getBoolean(String) true}. If the latter condition is true, then the
1116      * returned counter is {@linkplain DebugCounter#isConditional() unconditional} otherwise it is
1117      * conditional.
1118      * <p>
1119      * A disabled counter has virtually no overhead.
1120      */
1121     public static DebugCounter counter(CharSequence name) {
1122         if (!areUnconditionalCountersEnabled() && !ENABLED) {
1123             return VOID_COUNTER;
1124         }
1125         return createCounter("%s", name, null);
1126     }
1127 
1128     /**
1129      * Creates a {@link DebugMethodMetrics metric} that is enabled iff debugging is
1130      * {@link #isEnabled() enabled}.
1131      */
1132     public static DebugMethodMetrics methodMetrics(ResolvedJavaMethod method) {
1133         if (isMethodMeterEnabled() && method != null) {
1134             return MethodMetricsImpl.getMethodMetrics(method);
1135         }
1136         return VOID_MM;
1137     }
1138 
1139     public static String applyFormattingFlagsAndWidth(String s, int flags, int width) {
1140         if (flags == 0 && width < 0) {
1141             return s;
1142         }
1143         StringBuilder sb = new StringBuilder(s);
1144 
1145         // apply width and justification
1146         int len = sb.length();
1147         if (len < width) {
1148             for (int i = 0; i < width - len; i++) {
1149                 if ((flags & LEFT_JUSTIFY) == LEFT_JUSTIFY) {
1150                     sb.append(' ');
1151                 } else {
1152                     sb.insert(0, ' ');
1153                 }
1154             }
1155         }
1156 
1157         String res = sb.toString();
1158         if ((flags & UPPERCASE) == UPPERCASE) {
1159             res = res.toUpperCase();
1160         }
1161         return res;
1162     }
1163 
1164     /**
1165      * Creates a debug counter. Invoking this method is equivalent to:
1166      *
1167      * <pre>
1168      * Debug.counter(format, arg, null)
1169      * </pre>
1170      *
1171      * except that the string formatting only happens if count is enabled.
1172      *
1173      * @see #counter(String, Object, Object)
1174      */
1175     public static DebugCounter counter(String format, Object arg) {
1176         if (!areUnconditionalCountersEnabled() && !ENABLED) {
1177             return VOID_COUNTER;
1178         }
1179         return createCounter(format, arg, null);
1180     }
1181 
1182     /**
1183      * Creates a debug counter. Invoking this method is equivalent to:
1184      *
1185      * <pre>
1186      * Debug.counter(String.format(format, arg1, arg2))
1187      * </pre>
1188      *
1189      * except that the string formatting only happens if count is enabled. In addition, each
1190      * argument is subject to the following type based conversion before being passed as an argument
1191      * to {@link String#format(String, Object...)}:
1192      *
1193      * <pre>
1194      *     Type          | Conversion
1195      * ------------------+-----------------
1196      *  java.lang.Class  | arg.getSimpleName()
1197      *                   |
1198      * </pre>
1199      *
1200      * @see #counter(CharSequence)
1201      */
1202     public static DebugCounter counter(String format, Object arg1, Object arg2) {
1203         if (!areUnconditionalCountersEnabled() && !ENABLED) {
1204             return VOID_COUNTER;
1205         }
1206         return createCounter(format, arg1, arg2);
1207     }
1208 
1209     private static DebugCounter createCounter(String format, Object arg1, Object arg2) {
1210         String name = formatDebugName(format, arg1, arg2);
1211         boolean conditional = enabledCounters == null || !findMatch(enabledCounters, enabledCountersSubstrings, name);
1212         if (!ENABLED && conditional) {
1213             return VOID_COUNTER;
1214         }
1215         return DebugValueFactory.createCounter(name, conditional);
1216     }
1217 
1218     /**
1219      * Changes the debug configuration for the current thread.
1220      *
1221      * @param config new configuration to use for the current thread
1222      * @return an object that when {@linkplain DebugConfigScope#close() closed} will restore the
1223      *         debug configuration for the current thread to what it was before this method was
1224      *         called
1225      */
1226     public static DebugConfigScope setConfig(DebugConfig config) {
1227         if (ENABLED) {
1228             return new DebugConfigScope(config);
1229         } else {
1230             return null;
1231         }
1232     }
1233 
1234     /**
1235      * Creates an object for counting value frequencies.
1236      */
1237     public static DebugHistogram createHistogram(String name) {
1238         return new DebugHistogramImpl(name);
1239     }
1240 
1241     public static DebugConfig silentConfig() {
1242         return fixedConfig(0, 0, false, false, false, false, false, Collections.<DebugDumpHandler> emptyList(), Collections.<DebugVerifyHandler> emptyList(), null);
1243     }
1244 
1245     public static DebugConfig fixedConfig(final int logLevel, final int dumpLevel, final boolean isCountEnabled, final boolean isMemUseTrackingEnabled, final boolean isTimerEnabled,
1246                     final boolean isVerifyEnabled, final boolean isMMEnabled, final Collection<DebugDumpHandler> dumpHandlers, final Collection<DebugVerifyHandler> verifyHandlers,
1247                     final PrintStream output) {
1248         return new DebugConfig() {
1249 
1250             @Override
1251             public int getLogLevel() {
1252                 return logLevel;
1253             }
1254 
1255             @Override
1256             public boolean isLogEnabledForMethod() {
1257                 return logLevel > 0;
1258             }
1259 
1260             @Override
1261             public boolean isCountEnabled() {
1262                 return isCountEnabled;
1263             }
1264 
1265             @Override
1266             public boolean isMemUseTrackingEnabled() {
1267                 return isMemUseTrackingEnabled;
1268             }
1269 
1270             @Override
1271             public int getDumpLevel() {
1272                 return dumpLevel;
1273             }
1274 
1275             @Override
1276             public boolean isDumpEnabledForMethod() {
1277                 return dumpLevel > 0;
1278             }
1279 
1280             @Override
1281             public boolean isVerifyEnabled() {
1282                 return isVerifyEnabled;
1283             }
1284 
1285             @Override
1286             public boolean isVerifyEnabledForMethod() {
1287                 return isVerifyEnabled;
1288             }
1289 
1290             @Override
1291             public boolean isMethodMeterEnabled() {
1292                 return isMMEnabled;
1293             }
1294 
1295             @Override
1296             public boolean isTimeEnabled() {
1297                 return isTimerEnabled;
1298             }
1299 
1300             @Override
1301             public RuntimeException interceptException(Throwable e) {
1302                 return null;
1303             }
1304 
1305             @Override
1306             public Collection<DebugDumpHandler> dumpHandlers() {
1307                 return dumpHandlers;
1308             }
1309 
1310             @Override
1311             public Collection<DebugVerifyHandler> verifyHandlers() {
1312                 return verifyHandlers;
1313             }
1314 
1315             @Override
1316             public PrintStream output() {
1317                 return output;
1318             }
1319 
1320             @Override
1321             public void addToContext(Object o) {
1322             }
1323 
1324             @Override
1325             public void removeFromContext(Object o) {
1326             }
1327         };
1328     }
1329 
1330     private static final DebugCounter VOID_COUNTER = new DebugCounter() {
1331 
1332         @Override
1333         public void increment() {
1334         }
1335 
1336         @Override
1337         public void add(long value) {
1338         }
1339 
1340         @Override
1341         public void setConditional(boolean flag) {
1342             throw new InternalError("Cannot make void counter conditional");
1343         }
1344 
1345         @Override
1346         public boolean isConditional() {
1347             return false;
1348         }
1349 
1350         @Override
1351         public long getCurrentValue() {
1352             return 0L;
1353         }
1354     };
1355 
1356     private static final DebugMethodMetrics VOID_MM = new DebugMethodMetrics() {
1357 
1358         @Override
1359         public void addToMetric(long value, String metricName) {
1360         }
1361 
1362         @Override
1363         public void addToMetric(long value, String format, Object arg1) {
1364         }
1365 
1366         @Override
1367         public void addToMetric(long value, String format, Object arg1, Object arg2) {
1368         }
1369 
1370         @Override
1371         public void addToMetric(long value, String format, Object arg1, Object arg2, Object arg3) {
1372         }
1373 
1374         @Override
1375         public void incrementMetric(String metricName) {
1376         }
1377 
1378         @Override
1379         public void incrementMetric(String format, Object arg1) {
1380         }
1381 
1382         @Override
1383         public void incrementMetric(String format, Object arg1, Object arg2) {
1384         }
1385 
1386         @Override
1387         public void incrementMetric(String format, Object arg1, Object arg2, Object arg3) {
1388         }
1389 
1390         @Override
1391         public long getCurrentMetricValue(String metricName) {
1392             return 0;
1393         }
1394 
1395         @Override
1396         public long getCurrentMetricValue(String format, Object arg1) {
1397             return 0;
1398         }
1399 
1400         @Override
1401         public long getCurrentMetricValue(String format, Object arg1, Object arg2) {
1402             return 0;
1403         }
1404 
1405         @Override
1406         public long getCurrentMetricValue(String format, Object arg1, Object arg2, Object arg3) {
1407             return 0;
1408         }
1409 
1410         @Override
1411         public ResolvedJavaMethod getMethod() {
1412             return null;
1413         }
1414 
1415     };
1416 
1417     private static final DebugMemUseTracker VOID_MEM_USE_TRACKER = new DebugMemUseTracker() {
1418 
1419         @Override
1420         public DebugCloseable start() {
1421             return DebugCloseable.VOID_CLOSEABLE;
1422         }
1423 
1424         @Override
1425         public long getCurrentValue() {
1426             return 0;
1427         }
1428     };
1429 
1430     /**
1431      * @see #timer(CharSequence)
1432      */
1433     public static final String ENABLE_TIMER_PROPERTY_NAME_PREFIX = "graaldebug.timer.";
1434 
1435     /**
1436      * @see #counter(CharSequence)
1437      */
1438     public static final String ENABLE_COUNTER_PROPERTY_NAME_PREFIX = "graaldebug.counter.";
1439 
1440     /**
1441      * Set of unconditionally enabled counters. Possible values and their meanings:
1442      * <ul>
1443      * <li>{@code null}: no unconditionally enabled counters</li>
1444      * <li>{@code isEmpty()}: all counters are unconditionally enabled</li>
1445      * <li>{@code !isEmpty()}: use {@link #findMatch(Set, Set, String)} on this set and
1446      * {@link #enabledCountersSubstrings} to determine which counters are unconditionally enabled
1447      * </li>
1448      * </ul>
1449      */
1450     private static final Set<String> enabledCounters;
1451 
1452     /**
1453      * Set of unconditionally enabled timers. Same interpretation of values as for
1454      * {@link #enabledCounters}.
1455      */
1456     private static final Set<String> enabledTimers;
1457 
1458     private static final Set<String> enabledCountersSubstrings = new HashSet<>();
1459     private static final Set<String> enabledTimersSubstrings = new HashSet<>();
1460 
1461     /**
1462      * Specifies if all mem use trackers are unconditionally enabled.
1463      */
1464     private static final boolean isUnconditionalMemUseTrackingEnabled;
1465 
1466     static {
1467         Set<String> counters = new HashSet<>();
1468         Set<String> timers = new HashSet<>();
1469         parseCounterAndTimerSystemProperties(counters, timers, enabledCountersSubstrings, enabledTimersSubstrings);
1470         counters = counters.isEmpty() && enabledCountersSubstrings.isEmpty() ? null : counters;
1471         timers = timers.isEmpty() && enabledTimersSubstrings.isEmpty() ? null : timers;
1472         if (counters == null && params.enableUnscopedCounters && !params.enableMethodFilter) {
1473             counters = Collections.emptySet();
1474         }
1475         if (timers == null && params.enableUnscopedTimers && !params.enableMethodFilter) {
1476             timers = Collections.emptySet();
1477         }
1478         enabledCounters = counters;
1479         enabledTimers = timers;
1480         isUnconditionalMemUseTrackingEnabled = params.enableUnscopedMemUseTrackers;
1481         DebugValueFactory = initDebugValueFactory();
1482     }
1483 
1484     private static DebugValueFactory initDebugValueFactory() {
1485         return new DebugValueFactory() {
1486 
1487             @Override
1488             public DebugTimer createTimer(String name, boolean conditional) {
1489                 return new TimerImpl(name, conditional, params.interceptTime);
1490             }
1491 
1492             @Override
1493             public DebugCounter createCounter(String name, boolean conditional) {
1494                 return CounterImpl.create(name, conditional, params.interceptCount);
1495             }
1496 
1497             @Override
1498             public DebugMethodMetrics createMethodMetrics(ResolvedJavaMethod method) {
1499                 return MethodMetricsImpl.getMethodMetrics(method);
1500             }
1501 
1502             @Override
1503             public DebugMemUseTracker createMemUseTracker(String name, boolean conditional) {
1504                 return new MemUseTrackerImpl(name, conditional, params.interceptMem);
1505             }
1506         };
1507     }
1508 
1509     private static DebugValueFactory DebugValueFactory;
1510 
1511     public static void setDebugValueFactory(DebugValueFactory factory) {
1512         Objects.requireNonNull(factory);
1513         DebugValueFactory = factory;
1514     }
1515 
1516     public static DebugValueFactory getDebugValueFactory() {
1517         return DebugValueFactory;
1518     }
1519 
1520     private static boolean findMatch(Set<String> haystack, Set<String> haystackSubstrings, String needle) {
1521         if (haystack.isEmpty() && haystackSubstrings.isEmpty()) {
1522             // Empty haystack means match all
1523             return true;
1524         }
1525         if (haystack.contains(needle)) {
1526             return true;
1527         }
1528         if (!haystackSubstrings.isEmpty()) {
1529             for (String h : haystackSubstrings) {
1530                 if (needle.startsWith(h)) {
1531                     return true;
1532                 }
1533             }
1534         }
1535         return false;
1536     }
1537 
1538     public static boolean areUnconditionalTimersEnabled() {
1539         return enabledTimers != null;
1540     }
1541 
1542     public static boolean areUnconditionalCountersEnabled() {
1543         return enabledCounters != null;
1544     }
1545 
1546     public static boolean isMethodFilteringEnabled() {
1547         return params.enableMethodFilter;
1548     }
1549 
1550     public static boolean areUnconditionalMethodMetricsEnabled() {
1551         // we do not collect mm substrings
1552         return params.enableUnscopedMethodMetrics;
1553     }
1554 
1555     protected static void parseCounterAndTimerSystemProperties(Set<String> counters, Set<String> timers, Set<String> countersSubstrings, Set<String> timersSubstrings) {
1556         do {
1557             try {
1558                 for (Map.Entry<Object, Object> e : System.getProperties().entrySet()) {
1559                     String name = e.getKey().toString();
1560                     if (name.startsWith(ENABLE_COUNTER_PROPERTY_NAME_PREFIX) && Boolean.parseBoolean(e.getValue().toString())) {
1561                         if (name.endsWith("*")) {
1562                             countersSubstrings.add(name.substring(ENABLE_COUNTER_PROPERTY_NAME_PREFIX.length(), name.length() - 1));
1563                         } else {
1564                             counters.add(name.substring(ENABLE_COUNTER_PROPERTY_NAME_PREFIX.length()));
1565                         }
1566                     }
1567                     if (name.startsWith(ENABLE_TIMER_PROPERTY_NAME_PREFIX) && Boolean.parseBoolean(e.getValue().toString())) {
1568                         if (name.endsWith("*")) {
1569                             timersSubstrings.add(name.substring(ENABLE_TIMER_PROPERTY_NAME_PREFIX.length(), name.length() - 1));
1570                         } else {
1571                             timers.add(name.substring(ENABLE_TIMER_PROPERTY_NAME_PREFIX.length()));
1572                         }
1573                     }
1574                 }
1575                 return;
1576             } catch (ConcurrentModificationException e) {
1577                 // Iterating over the system properties may race with another thread that is
1578                 // updating the system properties. Simply try again in this case.
1579             }
1580         } while (true);
1581     }
1582 
1583     /**
1584      * Creates a {@linkplain DebugTimer timer} that is enabled iff debugging is
1585      * {@linkplain #isEnabled() enabled} or the system property whose name is formed by adding
1586      * {@value #ENABLE_TIMER_PROPERTY_NAME_PREFIX} to {@code name} is
1587      * {@linkplain Boolean#getBoolean(String) true}. If the latter condition is true, then the
1588      * returned timer is {@linkplain DebugCounter#isConditional() unconditional} otherwise it is
1589      * conditional.
1590      * <p>
1591      * A disabled timer has virtually no overhead.
1592      */
1593     public static DebugTimer timer(CharSequence name) {
1594         if (!areUnconditionalTimersEnabled() && !ENABLED) {
1595             return VOID_TIMER;
1596         }
1597         return createTimer("%s", name, null);
1598     }
1599 
1600     /**
1601      * Creates a debug timer. Invoking this method is equivalent to:
1602      *
1603      * <pre>
1604      * Debug.timer(format, arg, null)
1605      * </pre>
1606      *
1607      * except that the string formatting only happens if timing is enabled.
1608      *
1609      * @see #timer(String, Object, Object)
1610      */
1611     public static DebugTimer timer(String format, Object arg) {
1612         if (!areUnconditionalTimersEnabled() && !ENABLED) {
1613             return VOID_TIMER;
1614         }
1615         return createTimer(format, arg, null);
1616     }
1617 
1618     /**
1619      * Creates a debug timer. Invoking this method is equivalent to:
1620      *
1621      * <pre>
1622      * Debug.timer(String.format(format, arg1, arg2))
1623      * </pre>
1624      *
1625      * except that the string formatting only happens if timing is enabled. In addition, each
1626      * argument is subject to the following type based conversion before being passed as an argument
1627      * to {@link String#format(String, Object...)}:
1628      *
1629      * <pre>
1630      *     Type          | Conversion
1631      * ------------------+-----------------
1632      *  java.lang.Class  | arg.getSimpleName()
1633      *                   |
1634      * </pre>
1635      *
1636      * @see #timer(CharSequence)
1637      */
1638     public static DebugTimer timer(String format, Object arg1, Object arg2) {
1639         if (!areUnconditionalTimersEnabled() && !ENABLED) {
1640             return VOID_TIMER;
1641         }
1642         return createTimer(format, arg1, arg2);
1643     }
1644 
1645     /**
1646      * There are paths where construction of formatted class names are common and the code below is
1647      * surprisingly expensive, so compute it once and cache it.
1648      */
1649     private static final ClassValue<String> formattedClassName = new ClassValue<String>() {
1650         @Override
1651         protected String computeValue(Class<?> c) {
1652             final String simpleName = c.getSimpleName();
1653             Class<?> enclosingClass = c.getEnclosingClass();
1654             if (enclosingClass != null) {
1655                 String prefix = "";
1656                 while (enclosingClass != null) {
1657                     prefix = enclosingClass.getSimpleName() + "_" + prefix;
1658                     enclosingClass = enclosingClass.getEnclosingClass();
1659                 }
1660                 return prefix + simpleName;
1661             } else {
1662                 return simpleName;
1663             }
1664         }
1665     };
1666 
1667     public static Object convertFormatArg(Object arg) {
1668         if (arg instanceof Class) {
1669             return formattedClassName.get((Class<?>) arg);
1670         }
1671         return arg;
1672     }
1673 
1674     private static String formatDebugName(String format, Object arg1, Object arg2) {
1675         return String.format(format, convertFormatArg(arg1), convertFormatArg(arg2));
1676     }
1677 
1678     private static DebugTimer createTimer(String format, Object arg1, Object arg2) {
1679         String name = formatDebugName(format, arg1, arg2);
1680         boolean conditional = enabledTimers == null || !findMatch(enabledTimers, enabledTimersSubstrings, name);
1681         if (!ENABLED && conditional) {
1682             return VOID_TIMER;
1683         }
1684         return DebugValueFactory.createTimer(name, conditional);
1685     }
1686 
1687     private static final DebugTimer VOID_TIMER = new DebugTimer() {
1688 
1689         @Override
1690         public DebugCloseable start() {
1691             return DebugCloseable.VOID_CLOSEABLE;
1692         }
1693 
1694         @Override
1695         public void setConditional(boolean flag) {
1696             throw new InternalError("Cannot make void timer conditional");
1697         }
1698 
1699         @Override
1700         public boolean isConditional() {
1701             return false;
1702         }
1703 
1704         @Override
1705         public long getCurrentValue() {
1706             return 0L;
1707         }
1708 
1709         @Override
1710         public TimeUnit getTimeUnit() {
1711             return null;
1712         }
1713     };
1714 }