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