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