1 /*
   2  * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 
  25 package org.graalvm.compiler.debug;
  26 
  27 import static java.util.FormattableFlags.LEFT_JUSTIFY;
  28 import static java.util.FormattableFlags.UPPERCASE;
  29 import static org.graalvm.compiler.debug.DebugOptions.Count;
  30 import static org.graalvm.compiler.debug.DebugOptions.Counters;
  31 import static org.graalvm.compiler.debug.DebugOptions.Dump;
  32 import static org.graalvm.compiler.debug.DebugOptions.DumpOnError;
  33 import static org.graalvm.compiler.debug.DebugOptions.DumpOnPhaseChange;
  34 import static org.graalvm.compiler.debug.DebugOptions.DumpPath;
  35 import static org.graalvm.compiler.debug.DebugOptions.ListMetrics;
  36 import static org.graalvm.compiler.debug.DebugOptions.Log;
  37 import static org.graalvm.compiler.debug.DebugOptions.MemUseTrackers;
  38 import static org.graalvm.compiler.debug.DebugOptions.ShowDumpFiles;
  39 import static org.graalvm.compiler.debug.DebugOptions.Time;
  40 import static org.graalvm.compiler.debug.DebugOptions.Timers;
  41 import static org.graalvm.compiler.debug.DebugOptions.TrackMemUse;
  42 
  43 import java.io.ByteArrayOutputStream;
  44 import java.io.File;
  45 import java.io.IOException;
  46 import java.io.PrintStream;
  47 import java.nio.file.Files;
  48 import java.nio.file.Path;
  49 import java.nio.file.Paths;
  50 import java.nio.file.StandardOpenOption;
  51 import java.util.ArrayList;
  52 import java.util.Arrays;
  53 import java.util.Collection;
  54 import java.util.Collections;
  55 import java.util.Formatter;
  56 import java.util.List;
  57 import java.util.Map;
  58 import java.util.SortedMap;
  59 import java.util.TreeMap;
  60 
  61 import jdk.internal.vm.compiler.collections.EconomicMap;
  62 import jdk.internal.vm.compiler.collections.EconomicSet;
  63 import jdk.internal.vm.compiler.collections.Pair;
  64 import org.graalvm.compiler.options.OptionKey;
  65 import org.graalvm.compiler.options.OptionValues;
  66 import org.graalvm.compiler.serviceprovider.GraalServices;
  67 import org.graalvm.graphio.GraphOutput;
  68 
  69 import jdk.vm.ci.meta.JavaMethod;
  70 
  71 /**
  72  * A facility for logging and dumping as well as a container for values associated with
  73  * {@link MetricKey}s.
  74  *
  75  * A {@code DebugContext} object must only be used on the thread that created it. This means it
  76  * needs to be passed around as a parameter. For convenience, it can be encapsulated in a widely
  77  * used object that is in scope wherever a {@code DebugContext} is needed. However, care must be
  78  * taken when such objects can be exposed to multiple threads (e.g., they are in a non-thread-local
  79  * cache).
  80  */
  81 public final class DebugContext implements AutoCloseable {
  82 
  83     public static final Description NO_DESCRIPTION = new Description(null, "NO_DESCRIPTION");
  84     public static final GlobalMetrics NO_GLOBAL_METRIC_VALUES = null;
  85     public static final Iterable<DebugHandlersFactory> NO_CONFIG_CUSTOMIZERS = Collections.emptyList();
  86 
  87     public static final PrintStream DEFAULT_LOG_STREAM = TTY.out;
  88 
  89     /**
  90      * Contains the immutable parts of a debug context. This separation allows the immutable parts
  91      * to be shared and reduces the overhead of initialization since most immutable fields are
  92      * configured by parsing options.
  93      */
  94     final Immutable immutable;
  95 
  96     /**
  97      * Determines whether metrics are enabled.
  98      */
  99     boolean metricsEnabled;
 100 
 101     DebugConfigImpl currentConfig;
 102     ScopeImpl currentScope;
 103     CloseableCounter currentTimer;
 104     CloseableCounter currentMemUseTracker;
 105     Scope lastClosedScope;
 106     Throwable lastExceptionThrown;
 107     private IgvDumpChannel sharedChannel;
 108     private GraphOutput<?, ?> parentOutput;
 109 
 110     /**
 111      * Stores the {@link MetricKey} values.
 112      */
 113     private long[] metricValues;
 114 
 115     /**
 116      * Determines if dynamic scopes are enabled.
 117      */
 118     public boolean areScopesEnabled() {
 119         return immutable.scopesEnabled;
 120     }
 121 
 122     public <G, N, M> GraphOutput<G, M> buildOutput(GraphOutput.Builder<G, N, M> builder) throws IOException {
 123         if (parentOutput != null) {
 124             return builder.build(parentOutput);
 125         } else {
 126             if (sharedChannel == null) {
 127                 sharedChannel = new IgvDumpChannel(() -> getDumpPath(".bgv", false), immutable.options);
 128             }
 129             final GraphOutput<G, M> output = builder.build(sharedChannel);
 130             parentOutput = output;
 131             return output;
 132         }
 133     }
 134 
 135     /**
 136      * Adds version properties to the provided map. The version properties are read at a start of
 137      * the JVM from a JVM specific location. Each property identifiers a commit of a certain
 138      * component in the system. The properties added to the {@code properties} map are prefixed with
 139      * {@code "version."} prefix.
 140      *
 141      * @param properties map to add the version properties to or {@code null}
 142      * @return {@code properties} with version properties added or an unmodifiable map containing
 143      *         the version properties if {@code properties == null}
 144      */
 145     public static Map<Object, Object> addVersionProperties(Map<Object, Object> properties) {
 146         return Versions.VERSIONS.withVersions(properties);
 147     }
 148 
 149     /**
 150      * The immutable configuration that can be shared between {@link DebugContext} objects.
 151      */
 152     static final class Immutable {
 153 
 154         private static final Immutable[] CACHE = new Immutable[5];
 155 
 156         /**
 157          * The options from which this object was configured.
 158          */
 159         final OptionValues options;
 160 
 161         /**
 162          * Specifies if dynamic scopes are enabled.
 163          */
 164         final boolean scopesEnabled;
 165 
 166         final boolean listMetrics;
 167 
 168         /**
 169          * Names of unscoped counters. A counter is unscoped if this set is empty or contains the
 170          * counter's name.
 171          */
 172         final EconomicSet<String> unscopedCounters;
 173 
 174         /**
 175          * Names of unscoped timers. A timer is unscoped if this set is empty or contains the
 176          * timer's name.
 177          */
 178         final EconomicSet<String> unscopedTimers;
 179 
 180         /**
 181          * Names of unscoped memory usage trackers. A memory usage tracker is unscoped if this set
 182          * is empty or contains the memory usage tracker's name.
 183          */
 184         final EconomicSet<String> unscopedMemUseTrackers;
 185 
 186         private static EconomicSet<String> parseUnscopedMetricSpec(String spec, boolean unconditional, boolean accumulatedKey) {
 187             EconomicSet<String> res;
 188             if (spec == null) {
 189                 if (!unconditional) {
 190                     res = null;
 191                 } else {
 192                     res = EconomicSet.create();
 193                 }
 194             } else {
 195                 res = EconomicSet.create();
 196                 if (!spec.isEmpty()) {
 197                     if (!accumulatedKey) {
 198                         res.addAll(Arrays.asList(spec.split(",")));
 199                     } else {
 200                         for (String n : spec.split(",")) {
 201                             res.add(n + AccumulatedKey.ACCUMULATED_KEY_SUFFIX);
 202                             res.add(n + AccumulatedKey.FLAT_KEY_SUFFIX);
 203                         }
 204                     }
 205 
 206                 }
 207             }
 208             return res;
 209         }
 210 
 211         static Immutable create(OptionValues options) {
 212             int i = 0;
 213             while (i < CACHE.length) {
 214                 Immutable immutable = CACHE[i];
 215                 if (immutable == null) {
 216                     break;
 217                 }
 218                 if (immutable.options == options) {
 219                     return immutable;
 220                 }
 221                 i++;
 222             }
 223             Immutable immutable = new Immutable(options);
 224             if (i < CACHE.length) {
 225                 CACHE[i] = immutable;
 226             }
 227             return immutable;
 228         }
 229 
 230         private static boolean isNotEmpty(OptionKey<String> option, OptionValues options) {
 231             return option.getValue(options) != null && !option.getValue(options).isEmpty();
 232         }
 233 
 234         private Immutable(OptionValues options) {
 235             this.options = options;
 236             String timeValue = Time.getValue(options);
 237             String trackMemUseValue = TrackMemUse.getValue(options);
 238             this.unscopedCounters = parseUnscopedMetricSpec(Counters.getValue(options), "".equals(Count.getValue(options)), false);
 239             this.unscopedTimers = parseUnscopedMetricSpec(Timers.getValue(options), "".equals(timeValue), true);
 240             this.unscopedMemUseTrackers = parseUnscopedMetricSpec(MemUseTrackers.getValue(options), "".equals(trackMemUseValue), true);
 241 
 242             if (unscopedMemUseTrackers != null || trackMemUseValue != null) {
 243                 if (!GraalServices.isThreadAllocatedMemorySupported()) {
 244                     TTY.println("WARNING: Missing VM support for MemUseTrackers and TrackMemUse options so all reported memory usage will be 0");
 245                 }
 246             }
 247 
 248             this.scopesEnabled = DumpOnError.getValue(options) ||
 249                             Dump.getValue(options) != null ||
 250                             Log.getValue(options) != null ||
 251                             isNotEmpty(DebugOptions.Count, options) ||
 252                             isNotEmpty(DebugOptions.Time, options) ||
 253                             isNotEmpty(DebugOptions.TrackMemUse, options) ||
 254                             DumpOnPhaseChange.getValue(options) != null;
 255             this.listMetrics = ListMetrics.getValue(options);
 256         }
 257 
 258         private Immutable() {
 259             this.options = new OptionValues(EconomicMap.create());
 260             this.unscopedCounters = null;
 261             this.unscopedTimers = null;
 262             this.unscopedMemUseTrackers = null;
 263             this.scopesEnabled = false;
 264             this.listMetrics = false;
 265         }
 266 
 267         public boolean hasUnscopedMetrics() {
 268             return unscopedCounters != null || unscopedTimers != null || unscopedMemUseTrackers != null;
 269         }
 270     }
 271 
 272     /**
 273      * Gets the options this debug context was constructed with.
 274      */
 275     public OptionValues getOptions() {
 276         return immutable.options;
 277     }
 278 
 279     static class Activated extends ThreadLocal<DebugContext> {
 280     }
 281 
 282     private static final Activated activated = new Activated();
 283 
 284     /**
 285      * An object used to undo the changes made by DebugContext#activate().
 286      */
 287     public static class Activation implements AutoCloseable {
 288         private final DebugContext parent;
 289 
 290         Activation(DebugContext parent) {
 291             this.parent = parent;
 292         }
 293 
 294         @Override
 295         public void close() {
 296             activated.set(parent);
 297         }
 298     }
 299 
 300     /**
 301      * Activates this object as the debug context {@linkplain DebugContext#forCurrentThread for the
 302      * current thread}. This method should be used in a try-with-resources statement.
 303      *
 304      * @return an object that will deactivate the debug context for the current thread when
 305      *         {@link Activation#close()} is called on it
 306      */
 307     public Activation activate() {
 308         Activation res = new Activation(activated.get());
 309         activated.set(this);
 310         return res;
 311     }
 312 
 313     /**
 314      * Singleton used to represent a disabled debug context.
 315      */
 316     private static final DebugContext DISABLED = new DebugContext(NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, DEFAULT_LOG_STREAM, new Immutable(), NO_CONFIG_CUSTOMIZERS);
 317 
 318     /**
 319      * Create a DebugContext with debugging disabled.
 320      */
 321     public static DebugContext disabled(OptionValues options) {
 322         if (options == null || options.getMap().isEmpty()) {
 323             return DISABLED;
 324         }
 325         return new DebugContext(NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, DEFAULT_LOG_STREAM, Immutable.create(options), NO_CONFIG_CUSTOMIZERS);
 326     }
 327 
 328     /**
 329      * Gets the debug context for the current thread. This should only be used when there is no
 330      * other reasonable means to get a hold of a debug context.
 331      */
 332     public static DebugContext forCurrentThread() {
 333         DebugContext current = activated.get();
 334         if (current == null) {
 335             return DISABLED;
 336         }
 337         return current;
 338     }
 339 
 340     private final GlobalMetrics globalMetrics;
 341 
 342     /**
 343      * Describes the computation associated with a {@link DebugContext}.
 344      */
 345     public static class Description {
 346         /**
 347          * The primary input to the computation.
 348          */
 349         final Object compilable;
 350 
 351         /**
 352          * A runtime based identifier that is most likely to be unique.
 353          */
 354         final String identifier;
 355 
 356         public Description(Object compilable, String identifier) {
 357             this.compilable = compilable;
 358             this.identifier = identifier;
 359         }
 360 
 361         @Override
 362         public String toString() {
 363             String compilableName = compilable instanceof JavaMethod ? ((JavaMethod) compilable).format("%H.%n(%p)%R") : String.valueOf(compilable);
 364             return identifier + ":" + compilableName;
 365         }
 366 
 367         final String getLabel() {
 368             if (compilable instanceof JavaMethod) {
 369                 JavaMethod method = (JavaMethod) compilable;
 370                 return method.format("%h.%n(%p)%r");
 371             }
 372             return String.valueOf(compilable);
 373         }
 374     }
 375 
 376     private final Description description;
 377 
 378     /**
 379      * Gets a description of the computation associated with this debug context.
 380      *
 381      * @return {@code null} if no description is available
 382      */
 383     public Description getDescription() {
 384         return description;
 385     }
 386 
 387     /**
 388      * Gets the global metrics associated with this debug context.
 389      *
 390      * @return {@code null} if no global metrics are available
 391      */
 392     public GlobalMetrics getGlobalMetrics() {
 393         return globalMetrics;
 394     }
 395 
 396     /**
 397      * Creates a {@link DebugContext} based on a given set of option values and {@code factory}.
 398      */
 399     public static DebugContext create(OptionValues options, DebugHandlersFactory factory) {
 400         return new DebugContext(NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, DEFAULT_LOG_STREAM, Immutable.create(options), Collections.singletonList(factory));
 401     }
 402 
 403     /**
 404      * Creates a {@link DebugContext} based on a given set of option values and {@code factories}.
 405      * The {@link DebugHandlersFactory#LOADER} can be used for the latter.
 406      */
 407     public static DebugContext create(OptionValues options, Iterable<DebugHandlersFactory> factories) {
 408         return new DebugContext(NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, DEFAULT_LOG_STREAM, Immutable.create(options), factories);
 409     }
 410 
 411     public static DebugContext create(OptionValues options, PrintStream logStream, DebugHandlersFactory factory) {
 412         return new DebugContext(NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, logStream, Immutable.create(options), Collections.singletonList(factory));
 413     }
 414 
 415     /**
 416      * Creates a {@link DebugContext} based on a given set of option values and {@code factories}.
 417      * The {@link DebugHandlersFactory#LOADER} can be used for the latter.
 418      */
 419     public static DebugContext create(OptionValues options, Description description, Iterable<DebugHandlersFactory> factories) {
 420         return new DebugContext(description, NO_GLOBAL_METRIC_VALUES, DEFAULT_LOG_STREAM, Immutable.create(options), factories);
 421     }
 422 
 423     /**
 424      * Creates a {@link DebugContext}.
 425      */
 426     public static DebugContext create(OptionValues options, Description description, GlobalMetrics globalMetrics, PrintStream logStream, Iterable<DebugHandlersFactory> factories) {
 427         return new DebugContext(description, globalMetrics, logStream, Immutable.create(options), factories);
 428     }
 429 
 430     private DebugContext(Description description, GlobalMetrics globalMetrics, PrintStream logStream, Immutable immutable, Iterable<DebugHandlersFactory> factories) {
 431         this.immutable = immutable;
 432         this.description = description;
 433         this.globalMetrics = globalMetrics;
 434         if (immutable.scopesEnabled) {
 435             OptionValues options = immutable.options;
 436             List<DebugDumpHandler> dumpHandlers = new ArrayList<>();
 437             List<DebugVerifyHandler> verifyHandlers = new ArrayList<>();
 438             for (DebugHandlersFactory factory : factories) {
 439                 for (DebugHandler handler : factory.createHandlers(options)) {
 440                     if (handler instanceof DebugDumpHandler) {
 441                         dumpHandlers.add((DebugDumpHandler) handler);
 442                     } else {
 443                         assert handler instanceof DebugVerifyHandler;
 444                         verifyHandlers.add((DebugVerifyHandler) handler);
 445                     }
 446                 }
 447             }
 448             currentConfig = new DebugConfigImpl(options, logStream, dumpHandlers, verifyHandlers);
 449             currentScope = new ScopeImpl(this, Thread.currentThread());
 450             currentScope.updateFlags(currentConfig);
 451             metricsEnabled = true;
 452         } else {
 453             metricsEnabled = immutable.hasUnscopedMetrics() || immutable.listMetrics;
 454         }
 455     }
 456 
 457     public Path getDumpPath(String extension, boolean createMissingDirectory) {
 458         try {
 459             String id = description == null ? null : description.identifier;
 460             String label = description == null ? null : description.getLabel();
 461             Path result = PathUtilities.createUnique(immutable.options, DumpPath, id, label, extension, createMissingDirectory);
 462             if (ShowDumpFiles.getValue(immutable.options)) {
 463                 TTY.println("Dumping debug output to %s", result.toAbsolutePath().toString());
 464             }
 465             return result;
 466         } catch (IOException ex) {
 467             throw rethrowSilently(RuntimeException.class, ex);
 468         }
 469     }
 470 
 471     /**
 472      * A special dump level that indicates the dumping machinery is enabled but no dumps will be
 473      * produced except through other options.
 474      */
 475     public static final int ENABLED_LEVEL = 0;
 476 
 477     /**
 478      * Basic debug level.
 479      *
 480      * For HIR dumping, only ~5 graphs per method: after parsing, after inlining, after high tier,
 481      * after mid tier, after low tier.
 482      *
 483      * LIR dumping: After LIR generation, after each pre-allocation, allocation and post allocation
 484      * stage, and after code installation.
 485      */
 486     public static final int BASIC_LEVEL = 1;
 487 
 488     /**
 489      * Informational debug level.
 490      *
 491      * HIR dumping: One graph after each applied top-level phase.
 492      *
 493      * LIR dumping: After each applied phase.
 494      */
 495     public static final int INFO_LEVEL = 2;
 496 
 497     /**
 498      * Verbose debug level.
 499      *
 500      * HIR dumping: One graph after each phase (including sub phases).
 501      *
 502      * LIR dumping: After each phase including sub phases.
 503      */
 504     public static final int VERBOSE_LEVEL = 3;
 505 
 506     /**
 507      * Detailed debug level.
 508      *
 509      * HIR dumping: Graphs within phases where interesting for a phase, max ~5 per phase.
 510      *
 511      * LIR dumping: Dump CFG within phases where interesting.
 512      */
 513     public static final int DETAILED_LEVEL = 4;
 514 
 515     /**
 516      * Very detailed debug level.
 517      *
 518      * HIR dumping: Graphs per node granularity graph change (before/after change).
 519      *
 520      * LIR dumping: Intermediate CFGs of phases where interesting.
 521      */
 522     public static final int VERY_DETAILED_LEVEL = 5;
 523 
 524     public boolean isDumpEnabled(int dumpLevel) {
 525         return currentScope != null && currentScope.isDumpEnabled(dumpLevel);
 526     }
 527 
 528     /**
 529      * Determines if verification is enabled for any {@link JavaMethod} in the current scope.
 530      *
 531      * @see DebugContext#verify(Object, String)
 532      */
 533     public boolean isVerifyEnabledForMethod() {
 534         if (currentScope == null) {
 535             return false;
 536         }
 537         if (currentConfig == null) {
 538             return false;
 539         }
 540         return currentConfig.isVerifyEnabledForMethod(currentScope);
 541     }
 542 
 543     /**
 544      * Determines if verification is enabled in the current scope.
 545      *
 546      * @see DebugContext#verify(Object, String)
 547      */
 548     public boolean isVerifyEnabled() {
 549         return currentScope != null && currentScope.isVerifyEnabled();
 550     }
 551 
 552     public boolean isCountEnabled() {
 553         return currentScope != null && currentScope.isCountEnabled();
 554     }
 555 
 556     public boolean isTimeEnabled() {
 557         return currentScope != null && currentScope.isTimeEnabled();
 558     }
 559 
 560     public boolean isMemUseTrackingEnabled() {
 561         return currentScope != null && currentScope.isMemUseTrackingEnabled();
 562     }
 563 
 564     public boolean isDumpEnabledForMethod() {
 565         if (currentConfig == null) {
 566             return false;
 567         }
 568         return currentConfig.isDumpEnabledForMethod(currentScope);
 569     }
 570 
 571     public boolean isLogEnabledForMethod() {
 572         if (currentScope == null) {
 573             return false;
 574         }
 575         if (currentConfig == null) {
 576             return false;
 577         }
 578         return currentConfig.isLogEnabledForMethod(currentScope);
 579     }
 580 
 581     public boolean isLogEnabled() {
 582         return currentScope != null && isLogEnabled(BASIC_LEVEL);
 583     }
 584 
 585     public boolean isLogEnabled(int logLevel) {
 586         return currentScope != null && currentScope.isLogEnabled(logLevel);
 587     }
 588 
 589     /**
 590      * Gets a string composed of the names in the current nesting of debug
 591      * {@linkplain #scope(Object) scopes} separated by {@code '.'}.
 592      */
 593     public String getCurrentScopeName() {
 594         if (currentScope != null) {
 595             return currentScope.getQualifiedName();
 596         } else {
 597             return "";
 598         }
 599     }
 600 
 601     /**
 602      * Creates and enters a new debug scope which will be a child of the current debug scope.
 603      * <p>
 604      * It is recommended to use the try-with-resource statement for managing entering and leaving
 605      * debug scopes. For example:
 606      *
 607      * <pre>
 608      * try (Scope s = Debug.scope(&quot;InliningGraph&quot;, inlineeGraph)) {
 609      *     ...
 610      * } catch (Throwable e) {
 611      *     throw Debug.handle(e);
 612      * }
 613      * </pre>
 614      *
 615      * The {@code name} argument is subject to the following type based conversion before having
 616      * {@link Object#toString()} called on it:
 617      *
 618      * <pre>
 619      *     Type          | Conversion
 620      * ------------------+-----------------
 621      *  java.lang.Class  | arg.getSimpleName()
 622      *                   |
 623      * </pre>
 624      *
 625      * @param name the name of the new scope
 626      * @param contextObjects an array of object to be appended to the {@linkplain #context()
 627      *            current} debug context
 628      * @throws Throwable used to enforce a catch block.
 629      * @return the scope entered by this method which will be exited when its {@link Scope#close()}
 630      *         method is called
 631      */
 632     public DebugContext.Scope scope(Object name, Object[] contextObjects) throws Throwable {
 633         if (currentScope != null) {
 634             return enterScope(convertFormatArg(name).toString(), null, contextObjects);
 635         } else {
 636             return null;
 637         }
 638     }
 639 
 640     /**
 641      * Similar to {@link #scope(Object, Object[])} but without context objects. Therefore the catch
 642      * block can be omitted.
 643      *
 644      * @see #scope(Object, Object[])
 645      */
 646     public DebugContext.Scope scope(Object name) {
 647         if (currentScope != null) {
 648             return enterScope(convertFormatArg(name).toString(), null);
 649         } else {
 650             return null;
 651         }
 652     }
 653 
 654     private final Invariants invariants = Assertions.assertionsEnabled() ? new Invariants() : null;
 655 
 656     static StackTraceElement[] getStackTrace(Thread thread) {
 657         return thread.getStackTrace();
 658     }
 659 
 660     /**
 661      * Utility for enforcing {@link DebugContext} invariants via assertions.
 662      */
 663     static class Invariants {
 664         private final Thread thread;
 665         private final StackTraceElement[] origin;
 666 
 667         Invariants() {
 668             thread = Thread.currentThread();
 669             origin = getStackTrace(thread);
 670         }
 671 
 672         boolean checkNoConcurrentAccess() {
 673             Thread currentThread = Thread.currentThread();
 674             if (currentThread != thread) {
 675                 Formatter buf = new Formatter();
 676                 buf.format("Thread local %s object was created on thread %s but is being accessed by thread %s. The most likely cause is " +
 677                                 "that the object is being retrieved from a non-thread-local cache.",
 678                                 DebugContext.class.getName(), thread, currentThread);
 679                 int debugContextConstructors = 0;
 680                 boolean addedHeader = false;
 681                 for (StackTraceElement e : origin) {
 682                     if (e.getMethodName().equals("<init>") && e.getClassName().equals(DebugContext.class.getName())) {
 683                         debugContextConstructors++;
 684                     } else if (debugContextConstructors != 0) {
 685                         if (!addedHeader) {
 686                             addedHeader = true;
 687                             buf.format(" The object was instantiated here:");
 688                         }
 689                         // Distinguish from assertion stack trace by using double indent and
 690                         // "in" instead of "at" prefix.
 691                         buf.format("%n\t\tin %s", e);
 692                     }
 693                 }
 694                 if (addedHeader) {
 695                     buf.format("%n");
 696                 }
 697 
 698                 throw new AssertionError(buf.toString());
 699             }
 700             return true;
 701         }
 702     }
 703 
 704     boolean checkNoConcurrentAccess() {
 705         assert invariants == null || invariants.checkNoConcurrentAccess();
 706         return true;
 707     }
 708 
 709     private DebugContext.Scope enterScope(CharSequence name, DebugConfig sandboxConfig, Object... newContextObjects) {
 710         assert checkNoConcurrentAccess();
 711         currentScope = currentScope.scope(name, sandboxConfig, newContextObjects);
 712         return currentScope;
 713     }
 714 
 715     /**
 716      * @see #scope(Object, Object[])
 717      * @param context an object to be appended to the {@linkplain #context() current} debug context
 718      */
 719     public DebugContext.Scope scope(Object name, Object context) throws Throwable {
 720         if (currentScope != null) {
 721             return enterScope(convertFormatArg(name).toString(), null, context);
 722         } else {
 723             return null;
 724         }
 725     }
 726 
 727     /**
 728      * @see #scope(Object, Object[])
 729      * @param context1 first object to be appended to the {@linkplain #context() current} debug
 730      *            context
 731      * @param context2 second object to be appended to the {@linkplain #context() current} debug
 732      *            context
 733      */
 734     public DebugContext.Scope scope(Object name, Object context1, Object context2) throws Throwable {
 735         if (currentScope != null) {
 736             return enterScope(convertFormatArg(name).toString(), null, context1, context2);
 737         } else {
 738             return null;
 739         }
 740     }
 741 
 742     /**
 743      * @see #scope(Object, Object[])
 744      * @param context1 first object to be appended to the {@linkplain #context() current} debug
 745      *            context
 746      * @param context2 second object to be appended to the {@linkplain #context() current} debug
 747      *            context
 748      * @param context3 third object to be appended to the {@linkplain #context() current} debug
 749      *            context
 750      */
 751     public DebugContext.Scope scope(Object name, Object context1, Object context2, Object context3) throws Throwable {
 752         if (currentScope != null) {
 753             return enterScope(convertFormatArg(name).toString(), null, context1, context2, context3);
 754         } else {
 755             return null;
 756         }
 757     }
 758 
 759     /**
 760      * Create an unnamed scope that appends some context to the current scope.
 761      *
 762      * @param context an object to be appended to the {@linkplain #context() current} debug context
 763      */
 764     public DebugContext.Scope withContext(Object context) throws Throwable {
 765         if (currentScope != null) {
 766             return enterScope("", null, context);
 767         } else {
 768             return null;
 769         }
 770     }
 771 
 772     /**
 773      * Creates and enters a new debug scope which will be disjoint from the current debug scope.
 774      * <p>
 775      * It is recommended to use the try-with-resource statement for managing entering and leaving
 776      * debug scopes. For example:
 777      *
 778      * <pre>
 779      * try (Scope s = Debug.sandbox(&quot;CompilingStub&quot;, null, stubGraph)) {
 780      *     ...
 781      * } catch (Throwable e) {
 782      *     throw Debug.handle(e);
 783      * }
 784      * </pre>
 785      *
 786      * @param name the name of the new scope
 787      * @param config the debug configuration to use for the new scope or {@code null} to disable the
 788      *            scoping mechanism within the sandbox scope
 789      * @param context objects to be appended to the {@linkplain #context() current} debug context
 790      * @return the scope entered by this method which will be exited when its {@link Scope#close()}
 791      *         method is called
 792      */
 793     public DebugContext.Scope sandbox(CharSequence name, DebugConfig config, Object... context) throws Throwable {
 794         if (config == null) {
 795             return disable();
 796         }
 797         if (currentScope != null) {
 798             return enterScope(name, config, context);
 799         } else {
 800             return null;
 801         }
 802     }
 803 
 804     /**
 805      * Determines if scopes are enabled and this context is in a non-top-level scope.
 806      */
 807     public boolean inNestedScope() {
 808         if (immutable.scopesEnabled) {
 809             if (currentScope == null) {
 810                 // In an active DisabledScope
 811                 return true;
 812             }
 813             return !currentScope.isTopLevel();
 814         } else {
 815             return false;
 816         }
 817     }
 818 
 819     class DisabledScope implements DebugContext.Scope {
 820         final boolean savedMetricsEnabled;
 821         final ScopeImpl savedScope;
 822         final DebugConfigImpl savedConfig;
 823 
 824         DisabledScope() {
 825             this.savedMetricsEnabled = metricsEnabled;
 826             this.savedScope = currentScope;
 827             this.savedConfig = currentConfig;
 828             metricsEnabled = false;
 829             currentScope = null;
 830             currentConfig = null;
 831         }
 832 
 833         @Override
 834         public String getQualifiedName() {
 835             return "";
 836         }
 837 
 838         @Override
 839         public Iterable<Object> getCurrentContext() {
 840             return Collections.emptyList();
 841         }
 842 
 843         @Override
 844         public void close() {
 845             metricsEnabled = savedMetricsEnabled;
 846             currentScope = savedScope;
 847             currentConfig = savedConfig;
 848             lastClosedScope = this;
 849         }
 850     }
 851 
 852     /**
 853      * Disables all metrics and scope related functionality until {@code close()} is called on the
 854      * returned object.
 855      */
 856     public DebugContext.Scope disable() {
 857         if (currentScope != null) {
 858             return new DisabledScope();
 859         } else {
 860             return null;
 861         }
 862     }
 863 
 864     public DebugContext.Scope forceLog() throws Throwable {
 865         if (currentConfig != null) {
 866             ArrayList<Object> context = new ArrayList<>();
 867             for (Object obj : context()) {
 868                 context.add(obj);
 869             }
 870             DebugConfigImpl config = new DebugConfigImpl(new OptionValues(currentConfig.getOptions(), DebugOptions.Log, ":1000"));
 871             return sandbox("forceLog", config, context.toArray());
 872         }
 873         return null;
 874     }
 875 
 876     /**
 877      * Opens a scope in which exception
 878      * {@linkplain DebugConfig#interceptException(DebugContext, Throwable) interception} is
 879      * disabled. The current state of interception is restored when {@link DebugCloseable#close()}
 880      * is called on the returned object.
 881      *
 882      * This is particularly useful to suppress extraneous output in JUnit tests that are expected to
 883      * throw an exception.
 884      */
 885     public DebugCloseable disableIntercept() {
 886         if (currentScope != null) {
 887             return currentScope.disableIntercept();
 888         }
 889         return null;
 890     }
 891 
 892     /**
 893      * Handles an exception in the context of the debug scope just exited. The just exited scope
 894      * must have the current scope as its parent which will be the case if the try-with-resource
 895      * pattern recommended by {@link #scope(Object)} and
 896      * {@link #sandbox(CharSequence, DebugConfig, Object...)} is used
 897      *
 898      * @see #scope(Object, Object[])
 899      * @see #sandbox(CharSequence, DebugConfig, Object...)
 900      */
 901     public RuntimeException handle(Throwable exception) {
 902         if (currentScope != null) {
 903             return currentScope.handle(exception);
 904         } else {
 905             if (exception instanceof Error) {
 906                 throw (Error) exception;
 907             }
 908             if (exception instanceof RuntimeException) {
 909                 throw (RuntimeException) exception;
 910             }
 911             throw new RuntimeException(exception);
 912         }
 913     }
 914 
 915     public void log(String msg) {
 916         log(BASIC_LEVEL, msg);
 917     }
 918 
 919     /**
 920      * Prints a message to the current debug scope's logging stream if logging is enabled.
 921      *
 922      * @param msg the message to log
 923      */
 924     public void log(int logLevel, String msg) {
 925         if (currentScope != null) {
 926             currentScope.log(logLevel, msg);
 927         }
 928     }
 929 
 930     public void log(String format, Object arg) {
 931         log(BASIC_LEVEL, format, arg);
 932     }
 933 
 934     /**
 935      * Prints a message to the current debug scope's logging stream if logging is enabled.
 936      *
 937      * @param format a format string
 938      * @param arg the argument referenced by the format specifiers in {@code format}
 939      */
 940     public void log(int logLevel, String format, Object arg) {
 941         if (currentScope != null) {
 942             currentScope.log(logLevel, format, arg);
 943         }
 944     }
 945 
 946     public void log(String format, int arg) {
 947         log(BASIC_LEVEL, format, arg);
 948     }
 949 
 950     /**
 951      * Prints a message to the current debug scope's logging stream if logging is enabled.
 952      *
 953      * @param format a format string
 954      * @param arg the argument referenced by the format specifiers in {@code format}
 955      */
 956     public void log(int logLevel, String format, int arg) {
 957         if (currentScope != null) {
 958             currentScope.log(logLevel, format, arg);
 959         }
 960     }
 961 
 962     public void log(String format, Object arg1, Object arg2) {
 963         log(BASIC_LEVEL, format, arg1, arg2);
 964     }
 965 
 966     /**
 967      * @see #log(int, String, Object)
 968      */
 969     public void log(int logLevel, String format, Object arg1, Object arg2) {
 970         if (currentScope != null) {
 971             currentScope.log(logLevel, format, arg1, arg2);
 972         }
 973     }
 974 
 975     public void log(String format, int arg1, Object arg2) {
 976         log(BASIC_LEVEL, format, arg1, arg2);
 977     }
 978 
 979     /**
 980      * @see #log(int, String, Object)
 981      */
 982     public void log(int logLevel, String format, int arg1, Object arg2) {
 983         if (currentScope != null) {
 984             currentScope.log(logLevel, format, arg1, arg2);
 985         }
 986     }
 987 
 988     public void log(String format, Object arg1, int arg2) {
 989         log(BASIC_LEVEL, format, arg1, arg2);
 990     }
 991 
 992     /**
 993      * @see #log(int, String, Object)
 994      */
 995     public void log(int logLevel, String format, Object arg1, int arg2) {
 996         if (currentScope != null) {
 997             currentScope.log(logLevel, format, arg1, arg2);
 998         }
 999     }
1000 
1001     public void log(String format, int arg1, int arg2) {
1002         log(BASIC_LEVEL, format, arg1, arg2);
1003     }
1004 
1005     /**
1006      * @see #log(int, String, Object)
1007      */
1008     public void log(int logLevel, String format, int arg1, int arg2) {
1009         if (currentScope != null) {
1010             currentScope.log(logLevel, format, arg1, arg2);
1011         }
1012     }
1013 
1014     public void log(String format, Object arg1, Object arg2, Object arg3) {
1015         log(BASIC_LEVEL, format, arg1, arg2, arg3);
1016     }
1017 
1018     /**
1019      * @see #log(int, String, Object)
1020      */
1021     public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3) {
1022         if (currentScope != null) {
1023             currentScope.log(logLevel, format, arg1, arg2, arg3);
1024         }
1025     }
1026 
1027     public void log(String format, int arg1, int arg2, int arg3) {
1028         log(BASIC_LEVEL, format, arg1, arg2, arg3);
1029     }
1030 
1031     /**
1032      * @see #log(int, String, Object)
1033      */
1034     public void log(int logLevel, String format, int arg1, int arg2, int arg3) {
1035         if (currentScope != null) {
1036             currentScope.log(logLevel, format, arg1, arg2, arg3);
1037         }
1038     }
1039 
1040     public void log(String format, Object arg1, Object arg2, Object arg3, Object arg4) {
1041         log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4);
1042     }
1043 
1044     /**
1045      * @see #log(int, String, Object)
1046      */
1047     public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4) {
1048         if (currentScope != null) {
1049             currentScope.log(logLevel, format, arg1, arg2, arg3, arg4);
1050         }
1051     }
1052 
1053     public void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
1054         log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5);
1055     }
1056 
1057     /**
1058      * @see #log(int, String, Object)
1059      */
1060     public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
1061         if (currentScope != null) {
1062             currentScope.log(logLevel, format, arg1, arg2, arg3, arg4, arg5);
1063         }
1064     }
1065 
1066     public void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
1067         log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6);
1068     }
1069 
1070     /**
1071      * @see #log(int, String, Object)
1072      */
1073     public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
1074         if (currentScope != null) {
1075             currentScope.log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6);
1076         }
1077     }
1078 
1079     public void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7) {
1080         log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
1081     }
1082 
1083     public void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8) {
1084         log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
1085     }
1086 
1087     /**
1088      * @see #log(int, String, Object)
1089      */
1090     public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7) {
1091         if (currentScope != null) {
1092             currentScope.log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
1093         }
1094     }
1095 
1096     public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8) {
1097         if (currentScope != null) {
1098             currentScope.log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
1099         }
1100     }
1101 
1102     public void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9) {
1103         log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
1104     }
1105 
1106     public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9) {
1107         if (currentScope != null) {
1108             currentScope.log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
1109         }
1110     }
1111 
1112     public void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9, Object arg10) {
1113         log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
1114     }
1115 
1116     public 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) {
1117         if (currentScope != null) {
1118             currentScope.log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
1119         }
1120     }
1121 
1122     public void logv(String format, Object... args) {
1123         logv(BASIC_LEVEL, format, args);
1124     }
1125 
1126     /**
1127      * Prints a message to the current debug scope's logging stream. This method must only be called
1128      * if debugging scopes are {@linkplain DebugContext#areScopesEnabled() enabled} as it incurs
1129      * allocation at the call site. If possible, call one of the other {@code log()} methods in this
1130      * class that take a fixed number of parameters.
1131      *
1132      * @param format a format string
1133      * @param args the arguments referenced by the format specifiers in {@code format}
1134      */
1135     public void logv(int logLevel, String format, Object... args) {
1136         if (currentScope == null) {
1137             throw new InternalError("Use of Debug.logv() must be guarded by a test of Debug.isEnabled()");
1138         }
1139         currentScope.log(logLevel, format, args);
1140     }
1141 
1142     /**
1143      * This override exists to catch cases when {@link #log(String, Object)} is called with one
1144      * argument bound to a varargs method parameter. It will bind to this method instead of the
1145      * single arg variant and produce a deprecation warning instead of silently wrapping the
1146      * Object[] inside of another Object[].
1147      */
1148     @Deprecated
1149     public void log(String format, Object[] args) {
1150         assert false : "shouldn't use this";
1151         log(BASIC_LEVEL, format, args);
1152     }
1153 
1154     /**
1155      * This override exists to catch cases when {@link #log(int, String, Object)} is called with one
1156      * argument bound to a varargs method parameter. It will bind to this method instead of the
1157      * single arg variant and produce a deprecation warning instead of silently wrapping the
1158      * Object[] inside of another Object[].
1159      */
1160     @Deprecated
1161     public void log(int logLevel, String format, Object[] args) {
1162         assert false : "shouldn't use this";
1163         logv(logLevel, format, args);
1164     }
1165 
1166     /**
1167      * Forces an unconditional dump. This method exists mainly for debugging. It can also be used to
1168      * force a graph dump from IDEs that support invoking a Java method while at a breakpoint.
1169      */
1170     public void forceDump(Object object, String format, Object... args) {
1171         DebugConfig config = currentConfig;
1172         Collection<DebugDumpHandler> dumpHandlers;
1173         boolean closeAfterDump;
1174         if (config != null) {
1175             dumpHandlers = config.dumpHandlers();
1176             closeAfterDump = false;
1177         } else {
1178             OptionValues options = getOptions();
1179             dumpHandlers = new ArrayList<>();
1180             for (DebugHandlersFactory factory : DebugHandlersFactory.LOADER) {
1181                 for (DebugHandler handler : factory.createHandlers(options)) {
1182                     if (handler instanceof DebugDumpHandler) {
1183                         dumpHandlers.add((DebugDumpHandler) handler);
1184                     }
1185                 }
1186             }
1187             closeAfterDump = true;
1188         }
1189         for (DebugDumpHandler dumpHandler : dumpHandlers) {
1190             dumpHandler.dump(this, object, format, args);
1191             if (closeAfterDump) {
1192                 dumpHandler.close();
1193             }
1194         }
1195     }
1196 
1197     public void dump(int dumpLevel, Object object, String msg) {
1198         if (currentScope != null && currentScope.isDumpEnabled(dumpLevel)) {
1199             currentScope.dump(dumpLevel, object, msg);
1200         }
1201     }
1202 
1203     public void dump(int dumpLevel, Object object, String format, Object arg) {
1204         if (currentScope != null && currentScope.isDumpEnabled(dumpLevel)) {
1205             currentScope.dump(dumpLevel, object, format, arg);
1206         }
1207     }
1208 
1209     public void dump(int dumpLevel, Object object, String format, Object arg1, Object arg2) {
1210         if (currentScope != null && currentScope.isDumpEnabled(dumpLevel)) {
1211             currentScope.dump(dumpLevel, object, format, arg1, arg2);
1212         }
1213     }
1214 
1215     public void dump(int dumpLevel, Object object, String format, Object arg1, Object arg2, Object arg3) {
1216         if (currentScope != null && currentScope.isDumpEnabled(dumpLevel)) {
1217             currentScope.dump(dumpLevel, object, format, arg1, arg2, arg3);
1218         }
1219     }
1220 
1221     /**
1222      * This override exists to catch cases when {@link #dump(int, Object, String, Object)} is called
1223      * with one argument bound to a varargs method parameter. It will bind to this method instead of
1224      * the single arg variant and produce a deprecation warning instead of silently wrapping the
1225      * Object[] inside of another Object[].
1226      */
1227     @Deprecated
1228     public void dump(int dumpLevel, Object object, String format, Object[] args) {
1229         assert false : "shouldn't use this";
1230         if (currentScope != null && currentScope.isDumpEnabled(dumpLevel)) {
1231             currentScope.dump(dumpLevel, object, format, args);
1232         }
1233     }
1234 
1235     /**
1236      * Calls all {@link DebugVerifyHandler}s in the current {@linkplain #getConfig() config} to
1237      * perform verification on a given object.
1238      *
1239      * @param object object to verify
1240      * @param message description of verification context
1241      *
1242      * @see DebugVerifyHandler#verify
1243      */
1244     public void verify(Object object, String message) {
1245         if (currentScope != null && currentScope.isVerifyEnabled()) {
1246             currentScope.verify(object, message);
1247         }
1248     }
1249 
1250     /**
1251      * Calls all {@link DebugVerifyHandler}s in the current {@linkplain #getConfig() config} to
1252      * perform verification on a given object.
1253      *
1254      * @param object object to verify
1255      * @param format a format string for the description of the verification context
1256      * @param arg the argument referenced by the format specifiers in {@code format}
1257      *
1258      * @see DebugVerifyHandler#verify
1259      */
1260     public void verify(Object object, String format, Object arg) {
1261         if (currentScope != null && currentScope.isVerifyEnabled()) {
1262             currentScope.verify(object, format, arg);
1263         }
1264     }
1265 
1266     /**
1267      * This override exists to catch cases when {@link #verify(Object, String, Object)} is called
1268      * with one argument bound to a varargs method parameter. It will bind to this method instead of
1269      * the single arg variant and produce a deprecation warning instead of silently wrapping the
1270      * Object[] inside of another Object[].
1271      */
1272     @Deprecated
1273     public void verify(Object object, String format, Object[] args) {
1274         assert false : "shouldn't use this";
1275         if (currentScope != null && currentScope.isVerifyEnabled()) {
1276             currentScope.verify(object, format, args);
1277         }
1278     }
1279 
1280     /**
1281      * Opens a new indentation level (by adding some spaces) based on the current indentation level.
1282      * This should be used in a {@linkplain Indent try-with-resources} pattern.
1283      *
1284      * @return an object that reverts to the current indentation level when
1285      *         {@linkplain Indent#close() closed} or null if debugging is disabled
1286      * @see #logAndIndent(int, String)
1287      * @see #logAndIndent(int, String, Object)
1288      */
1289     public Indent indent() {
1290         if (currentScope != null) {
1291             return currentScope.pushIndentLogger();
1292         }
1293         return null;
1294     }
1295 
1296     public Indent logAndIndent(String msg) {
1297         return logAndIndent(BASIC_LEVEL, msg);
1298     }
1299 
1300     /**
1301      * A convenience function which combines {@link #log(String)} and {@link #indent()}.
1302      *
1303      * @param msg the message to log
1304      * @return an object that reverts to the current indentation level when
1305      *         {@linkplain Indent#close() closed} or null if debugging is disabled
1306      */
1307     public Indent logAndIndent(int logLevel, String msg) {
1308         if (currentScope != null && isLogEnabled(logLevel)) {
1309             return logvAndIndentInternal(logLevel, msg);
1310         }
1311         return null;
1312     }
1313 
1314     public Indent logAndIndent(String format, Object arg) {
1315         return logAndIndent(BASIC_LEVEL, format, arg);
1316     }
1317 
1318     /**
1319      * A convenience function which combines {@link #log(String, Object)} and {@link #indent()}.
1320      *
1321      * @param format a format string
1322      * @param arg the argument referenced by the format specifiers in {@code format}
1323      * @return an object that reverts to the current indentation level when
1324      *         {@linkplain Indent#close() closed} or null if debugging is disabled
1325      */
1326     public Indent logAndIndent(int logLevel, String format, Object arg) {
1327         if (currentScope != null && isLogEnabled(logLevel)) {
1328             return logvAndIndentInternal(logLevel, format, arg);
1329         }
1330         return null;
1331     }
1332 
1333     public Indent logAndIndent(String format, int arg) {
1334         return logAndIndent(BASIC_LEVEL, format, arg);
1335     }
1336 
1337     /**
1338      * A convenience function which combines {@link #log(String, Object)} and {@link #indent()}.
1339      *
1340      * @param format a format string
1341      * @param arg the argument referenced by the format specifiers in {@code format}
1342      * @return an object that reverts to the current indentation level when
1343      *         {@linkplain Indent#close() closed} or null if debugging is disabled
1344      */
1345     public Indent logAndIndent(int logLevel, String format, int arg) {
1346         if (currentScope != null && isLogEnabled(logLevel)) {
1347             return logvAndIndentInternal(logLevel, format, arg);
1348         }
1349         return null;
1350     }
1351 
1352     public Indent logAndIndent(String format, int arg1, Object arg2) {
1353         return logAndIndent(BASIC_LEVEL, format, arg1, arg2);
1354     }
1355 
1356     /**
1357      * @see #logAndIndent(int, String, Object)
1358      */
1359     public Indent logAndIndent(int logLevel, String format, int arg1, Object arg2) {
1360         if (currentScope != null && isLogEnabled(logLevel)) {
1361             return logvAndIndentInternal(logLevel, format, arg1, arg2);
1362         }
1363         return null;
1364     }
1365 
1366     public Indent logAndIndent(String format, Object arg1, int arg2) {
1367         return logAndIndent(BASIC_LEVEL, format, arg1, arg2);
1368     }
1369 
1370     /**
1371      * @see #logAndIndent(int, String, Object)
1372      */
1373     public Indent logAndIndent(int logLevel, String format, Object arg1, int arg2) {
1374         if (currentScope != null && isLogEnabled(logLevel)) {
1375             return logvAndIndentInternal(logLevel, format, arg1, arg2);
1376         }
1377         return null;
1378     }
1379 
1380     public Indent logAndIndent(String format, int arg1, int arg2) {
1381         return logAndIndent(BASIC_LEVEL, format, arg1, arg2);
1382     }
1383 
1384     /**
1385      * @see #logAndIndent(int, String, Object)
1386      */
1387     public Indent logAndIndent(int logLevel, String format, int arg1, int arg2) {
1388         if (currentScope != null && isLogEnabled(logLevel)) {
1389             return logvAndIndentInternal(logLevel, format, arg1, arg2);
1390         }
1391         return null;
1392     }
1393 
1394     public Indent logAndIndent(String format, Object arg1, Object arg2) {
1395         return logAndIndent(BASIC_LEVEL, format, arg1, arg2);
1396     }
1397 
1398     /**
1399      * @see #logAndIndent(int, String, Object)
1400      */
1401     public Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2) {
1402         if (currentScope != null && isLogEnabled(logLevel)) {
1403             return logvAndIndentInternal(logLevel, format, arg1, arg2);
1404         }
1405         return null;
1406     }
1407 
1408     public Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3) {
1409         return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3);
1410     }
1411 
1412     /**
1413      * @see #logAndIndent(int, String, Object)
1414      */
1415     public Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3) {
1416         if (currentScope != null && isLogEnabled(logLevel)) {
1417             return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3);
1418         }
1419         return null;
1420     }
1421 
1422     public Indent logAndIndent(String format, int arg1, int arg2, int arg3) {
1423         return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3);
1424     }
1425 
1426     /**
1427      * @see #logAndIndent(int, String, Object)
1428      */
1429     public Indent logAndIndent(int logLevel, String format, int arg1, int arg2, int arg3) {
1430         if (currentScope != null && isLogEnabled(logLevel)) {
1431             return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3);
1432         }
1433         return null;
1434     }
1435 
1436     public Indent logAndIndent(String format, Object arg1, int arg2, int arg3) {
1437         return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3);
1438     }
1439 
1440     /**
1441      * @see #logAndIndent(int, String, Object)
1442      */
1443     public Indent logAndIndent(int logLevel, String format, Object arg1, int arg2, int arg3) {
1444         if (currentScope != null && isLogEnabled(logLevel)) {
1445             return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3);
1446         }
1447         return null;
1448     }
1449 
1450     public Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4) {
1451         return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3, arg4);
1452     }
1453 
1454     /**
1455      * @see #logAndIndent(int, String, Object)
1456      */
1457     public Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4) {
1458         if (currentScope != null && isLogEnabled(logLevel)) {
1459             return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3, arg4);
1460         }
1461         return null;
1462     }
1463 
1464     public Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
1465         return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5);
1466     }
1467 
1468     /**
1469      * @see #logAndIndent(int, String, Object)
1470      */
1471     public Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
1472         if (currentScope != null && isLogEnabled(logLevel)) {
1473             return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3, arg4, arg5);
1474         }
1475         return null;
1476     }
1477 
1478     public Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
1479         return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6);
1480     }
1481 
1482     /**
1483      * @see #logAndIndent(int, String, Object)
1484      */
1485     public Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
1486         if (currentScope != null && isLogEnabled(logLevel)) {
1487             return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6);
1488         }
1489         return null;
1490     }
1491 
1492     /**
1493      * A convenience function which combines {@link #logv(int, String, Object...)} and
1494      * {@link #indent()}.
1495      *
1496      * @param format a format string
1497      * @param args the arguments referenced by the format specifiers in {@code format}
1498      * @return an object that reverts to the current indentation level when
1499      *         {@linkplain Indent#close() closed} or null if debugging is disabled
1500      */
1501     public Indent logvAndIndent(int logLevel, String format, Object... args) {
1502         if (currentScope != null) {
1503             if (isLogEnabled(logLevel)) {
1504                 return logvAndIndentInternal(logLevel, format, args);
1505             }
1506             return null;
1507         }
1508         throw new InternalError("Use of Debug.logvAndIndent() must be guarded by a test of Debug.isEnabled()");
1509     }
1510 
1511     private Indent logvAndIndentInternal(int logLevel, String format, Object... args) {
1512         assert currentScope != null && isLogEnabled(logLevel) : "must have checked Debug.isLogEnabled()";
1513         currentScope.log(logLevel, format, args);
1514         return currentScope.pushIndentLogger();
1515     }
1516 
1517     /**
1518      * This override exists to catch cases when {@link #logAndIndent(String, Object)} is called with
1519      * one argument bound to a varargs method parameter. It will bind to this method instead of the
1520      * single arg variant and produce a deprecation warning instead of silently wrapping the
1521      * Object[] inside of another Object[].
1522      */
1523     @Deprecated
1524     public void logAndIndent(String format, Object[] args) {
1525         assert false : "shouldn't use this";
1526         logAndIndent(BASIC_LEVEL, format, args);
1527     }
1528 
1529     /**
1530      * This override exists to catch cases when {@link #logAndIndent(int, String, Object)} is called
1531      * with one argument bound to a varargs method parameter. It will bind to this method instead of
1532      * the single arg variant and produce a deprecation warning instead of silently wrapping the
1533      * Object[] inside of another Object[].
1534      */
1535     @Deprecated
1536     public void logAndIndent(int logLevel, String format, Object[] args) {
1537         assert false : "shouldn't use this";
1538         logvAndIndent(logLevel, format, args);
1539     }
1540 
1541     public Iterable<Object> context() {
1542         if (currentScope != null) {
1543             return currentScope.getCurrentContext();
1544         } else {
1545             return Collections.emptyList();
1546         }
1547     }
1548 
1549     @SuppressWarnings("unchecked")
1550     public <T> List<T> contextSnapshot(Class<T> clazz) {
1551         if (currentScope != null) {
1552             List<T> result = new ArrayList<>();
1553             for (Object o : context()) {
1554                 if (clazz.isInstance(o)) {
1555                     result.add((T) o);
1556                 }
1557             }
1558             return result;
1559         } else {
1560             return Collections.emptyList();
1561         }
1562     }
1563 
1564     /**
1565      * Searches the current debug scope, bottom up, for a context object that is an instance of a
1566      * given type. The first such object found is returned.
1567      */
1568     @SuppressWarnings("unchecked")
1569     public <T> T contextLookup(Class<T> clazz) {
1570         if (currentScope != null) {
1571             for (Object o : context()) {
1572                 if (clazz.isInstance(o)) {
1573                     return ((T) o);
1574                 }
1575             }
1576         }
1577         return null;
1578     }
1579 
1580     /**
1581      * Searches the current debug scope, top down, for a context object that is an instance of a
1582      * given type. The first such object found is returned.
1583      */
1584     @SuppressWarnings("unchecked")
1585     public <T> T contextLookupTopdown(Class<T> clazz) {
1586         if (currentScope != null) {
1587             T found = null;
1588             for (Object o : context()) {
1589                 if (clazz.isInstance(o)) {
1590                     found = (T) o;
1591                 }
1592             }
1593             return found;
1594         }
1595         return null;
1596     }
1597 
1598     /**
1599      * Creates a {@linkplain MemUseTrackerKey memory use tracker}.
1600      */
1601     public static MemUseTrackerKey memUseTracker(CharSequence name) {
1602         return createMemUseTracker("%s", name, null);
1603     }
1604 
1605     /**
1606      * Creates a debug memory use tracker. Invoking this method is equivalent to:
1607      *
1608      * <pre>
1609      * Debug.memUseTracker(format, arg, null)
1610      * </pre>
1611      *
1612      * except that the string formatting only happens if mem tracking is enabled.
1613      *
1614      * @see #counter(String, Object, Object)
1615      */
1616     public static MemUseTrackerKey memUseTracker(String format, Object arg) {
1617         return createMemUseTracker(format, arg, null);
1618     }
1619 
1620     /**
1621      * Creates a debug memory use tracker. Invoking this method is equivalent to:
1622      *
1623      * <pre>
1624      * Debug.memUseTracker(String.format(format, arg1, arg2))
1625      * </pre>
1626      *
1627      * except that the string formatting only happens if memory use tracking is enabled. In
1628      * addition, each argument is subject to the following type based conversion before being passed
1629      * as an argument to {@link String#format(String, Object...)}:
1630      *
1631      * <pre>
1632      *     Type          | Conversion
1633      * ------------------+-----------------
1634      *  java.lang.Class  | arg.getSimpleName()
1635      *                   |
1636      * </pre>
1637      *
1638      * @see #memUseTracker(CharSequence)
1639      */
1640     public static MemUseTrackerKey memUseTracker(String format, Object arg1, Object arg2) {
1641         return createMemUseTracker(format, arg1, arg2);
1642     }
1643 
1644     private static MemUseTrackerKey createMemUseTracker(String format, Object arg1, Object arg2) {
1645         return new MemUseTrackerKeyImpl(format, arg1, arg2);
1646     }
1647 
1648     /**
1649      * Creates a {@linkplain CounterKey counter}.
1650      */
1651     public static CounterKey counter(CharSequence name) {
1652         return createCounter("%s", name, null);
1653     }
1654 
1655     /**
1656      * Gets a tally of the metric values in this context and a given tally.
1657      *
1658      * @param tally the tally to which the metrics should be added
1659      * @return a tally of the metric values in this context and {@code tally}. This will be
1660      *         {@code tally} if this context has no metric values or {@code tally} is wide enough to
1661      *         hold all the metric values in this context otherwise it will be a new array.
1662      */
1663     public long[] addValuesTo(long[] tally) {
1664         if (metricValues == null) {
1665             return tally;
1666         }
1667         if (tally == null) {
1668             return metricValues.clone();
1669         } else if (metricValues.length >= tally.length) {
1670             long[] newTally = metricValues.clone();
1671             for (int i = 0; i < tally.length; i++) {
1672                 newTally[i] += tally[i];
1673             }
1674             return newTally;
1675         } else {
1676             for (int i = 0; i < metricValues.length; i++) {
1677                 tally[i] += metricValues[i];
1678             }
1679             return tally;
1680         }
1681     }
1682 
1683     /**
1684      * Creates and returns a sorted map from metric names to their values in {@code values}.
1685      *
1686      * @param values values for metrics in the {@link KeyRegistry}.
1687      */
1688     public static EconomicMap<MetricKey, Long> convertValuesToKeyValueMap(long[] values) {
1689         List<MetricKey> keys = KeyRegistry.getKeys();
1690         Collections.sort(keys, MetricKey.NAME_COMPARATOR);
1691         EconomicMap<MetricKey, Long> res = EconomicMap.create(keys.size());
1692         for (MetricKey key : keys) {
1693             int index = ((AbstractKey) key).getIndex();
1694             if (index >= values.length) {
1695                 res.put(key, 0L);
1696             } else {
1697                 res.put(key, values[index]);
1698             }
1699         }
1700         return res;
1701     }
1702 
1703     void setMetricValue(int keyIndex, long l) {
1704         ensureMetricValuesSize(keyIndex);
1705         metricValues[keyIndex] = l;
1706     }
1707 
1708     long getMetricValue(int keyIndex) {
1709         if (metricValues == null || metricValues.length <= keyIndex) {
1710             return 0L;
1711         }
1712         return metricValues[keyIndex];
1713     }
1714 
1715     private void ensureMetricValuesSize(int index) {
1716         if (metricValues == null) {
1717             metricValues = new long[index + 1];
1718         }
1719         if (metricValues.length <= index) {
1720             metricValues = Arrays.copyOf(metricValues, index + 1);
1721         }
1722     }
1723 
1724     public static String applyFormattingFlagsAndWidth(String s, int flags, int width) {
1725         if (flags == 0 && width < 0) {
1726             return s;
1727         }
1728         StringBuilder sb = new StringBuilder(s);
1729 
1730         // apply width and justification
1731         int len = sb.length();
1732         if (len < width) {
1733             for (int i = 0; i < width - len; i++) {
1734                 if ((flags & LEFT_JUSTIFY) == LEFT_JUSTIFY) {
1735                     sb.append(' ');
1736                 } else {
1737                     sb.insert(0, ' ');
1738                 }
1739             }
1740         }
1741 
1742         String res = sb.toString();
1743         if ((flags & UPPERCASE) == UPPERCASE) {
1744             res = res.toUpperCase();
1745         }
1746         return res;
1747     }
1748 
1749     /**
1750      * Creates a debug counter. Invoking this method is equivalent to:
1751      *
1752      * <pre>
1753      * Debug.counter(format, arg, null)
1754      * </pre>
1755      *
1756      * except that the string formatting only happens if count is enabled.
1757      *
1758      * @see #counter(String, Object, Object)
1759      */
1760     public static CounterKey counter(String format, Object arg) {
1761         return createCounter(format, arg, null);
1762     }
1763 
1764     /**
1765      * Creates a debug counter. Invoking this method is equivalent to:
1766      *
1767      * <pre>
1768      * Debug.counter(String.format(format, arg1, arg2))
1769      * </pre>
1770      *
1771      * except that the string formatting only happens if count is enabled. In addition, each
1772      * argument is subject to the following type based conversion before being passed as an argument
1773      * to {@link String#format(String, Object...)}:
1774      *
1775      * <pre>
1776      *     Type          | Conversion
1777      * ------------------+-----------------
1778      *  java.lang.Class  | arg.getSimpleName()
1779      *                   |
1780      * </pre>
1781      *
1782      * @see #counter(CharSequence)
1783      */
1784     public static CounterKey counter(String format, Object arg1, Object arg2) {
1785         return createCounter(format, arg1, arg2);
1786     }
1787 
1788     private static CounterKey createCounter(String format, Object arg1, Object arg2) {
1789         return new CounterKeyImpl(format, arg1, arg2);
1790     }
1791 
1792     public DebugConfig getConfig() {
1793         return currentConfig;
1794     }
1795 
1796     /**
1797      * Creates a {@linkplain TimerKey timer}.
1798      * <p>
1799      * A disabled timer has virtually no overhead.
1800      */
1801     public static TimerKey timer(CharSequence name) {
1802         return createTimer("%s", name, null);
1803     }
1804 
1805     /**
1806      * Creates a debug timer. Invoking this method is equivalent to:
1807      *
1808      * <pre>
1809      * Debug.timer(format, arg, null)
1810      * </pre>
1811      *
1812      * except that the string formatting only happens if timing is enabled.
1813      *
1814      * @see #timer(String, Object, Object)
1815      */
1816     public static TimerKey timer(String format, Object arg) {
1817         return createTimer(format, arg, null);
1818     }
1819 
1820     /**
1821      * Creates a debug timer. Invoking this method is equivalent to:
1822      *
1823      * <pre>
1824      * Debug.timer(String.format(format, arg1, arg2))
1825      * </pre>
1826      *
1827      * except that the string formatting only happens if timing is enabled. In addition, each
1828      * argument is subject to the following type based conversion before being passed as an argument
1829      * to {@link String#format(String, Object...)}:
1830      *
1831      * <pre>
1832      *     Type          | Conversion
1833      * ------------------+-----------------
1834      *  java.lang.Class  | arg.getSimpleName()
1835      *                   |
1836      * </pre>
1837      *
1838      * @see #timer(CharSequence)
1839      */
1840     public static TimerKey timer(String format, Object arg1, Object arg2) {
1841         return createTimer(format, arg1, arg2);
1842     }
1843 
1844     /**
1845      * There are paths where construction of formatted class names are common and the code below is
1846      * surprisingly expensive, so compute it once and cache it.
1847      */
1848     private static final ClassValue<String> formattedClassName = new ClassValue<String>() {
1849         @Override
1850         protected String computeValue(Class<?> c) {
1851             final String simpleName = c.getSimpleName();
1852             Class<?> enclosingClass = c.getEnclosingClass();
1853             if (enclosingClass != null) {
1854                 String prefix = "";
1855                 while (enclosingClass != null) {
1856                     prefix = enclosingClass.getSimpleName() + "_" + prefix;
1857                     enclosingClass = enclosingClass.getEnclosingClass();
1858                 }
1859                 return prefix + simpleName;
1860             } else {
1861                 return simpleName;
1862             }
1863         }
1864     };
1865 
1866     public static Object convertFormatArg(Object arg) {
1867         if (arg instanceof Class) {
1868             return formattedClassName.get((Class<?>) arg);
1869         }
1870         return arg;
1871     }
1872 
1873     static String formatDebugName(String format, Object arg1, Object arg2) {
1874         return String.format(format, convertFormatArg(arg1), convertFormatArg(arg2));
1875     }
1876 
1877     private static TimerKey createTimer(String format, Object arg1, Object arg2) {
1878         return new TimerKeyImpl(format, arg1, arg2);
1879     }
1880 
1881     /**
1882      * Represents a debug scope entered by {@link DebugContext#scope(Object)} or
1883      * {@link DebugContext#sandbox(CharSequence, DebugConfig, Object...)}. Leaving the scope is
1884      * achieved via {@link #close()}.
1885      */
1886     public interface Scope extends AutoCloseable {
1887         /**
1888          * Gets the names of this scope and its ancestors separated by {@code '.'}.
1889          */
1890         String getQualifiedName();
1891 
1892         Iterable<Object> getCurrentContext();
1893 
1894         @Override
1895         void close();
1896     }
1897 
1898     boolean isTimerEnabled(TimerKeyImpl key) {
1899         if (!metricsEnabled) {
1900             // Pulling this common case out of `isTimerEnabledSlow`
1901             // gives C1 a better chance to inline this method.
1902             return false;
1903         }
1904         return isTimerEnabledSlow(key);
1905     }
1906 
1907     private boolean isTimerEnabledSlow(AbstractKey key) {
1908         if (currentScope != null && currentScope.isTimeEnabled()) {
1909             return true;
1910         }
1911         if (immutable.listMetrics) {
1912             key.ensureInitialized();
1913         }
1914         assert checkNoConcurrentAccess();
1915         EconomicSet<String> unscoped = immutable.unscopedTimers;
1916         return unscoped != null && (unscoped.isEmpty() || unscoped.contains(key.getName()));
1917     }
1918 
1919     /**
1920      * Determines if a given timer is enabled in the current scope.
1921      */
1922     boolean isCounterEnabled(CounterKeyImpl key) {
1923         if (!metricsEnabled) {
1924             // Pulling this common case out of `isCounterEnabledSlow`
1925             // gives C1 a better chance to inline this method.
1926             return false;
1927         }
1928         return isCounterEnabledSlow(key);
1929     }
1930 
1931     private boolean isCounterEnabledSlow(AbstractKey key) {
1932         if (currentScope != null && currentScope.isCountEnabled()) {
1933             return true;
1934         }
1935         if (immutable.listMetrics) {
1936             key.ensureInitialized();
1937         }
1938         assert checkNoConcurrentAccess();
1939         EconomicSet<String> unscoped = immutable.unscopedCounters;
1940         return unscoped != null && (unscoped.isEmpty() || unscoped.contains(key.getName()));
1941     }
1942 
1943     boolean isMemUseTrackerEnabled(MemUseTrackerKeyImpl key) {
1944         if (!metricsEnabled) {
1945             // Pulling this common case out of `isMemUseTrackerEnabledSlow`
1946             // gives C1 a better chance to inline this method.
1947             return false;
1948         }
1949         return isMemUseTrackerEnabledSlow(key);
1950     }
1951 
1952     private boolean isMemUseTrackerEnabledSlow(AbstractKey key) {
1953         if (currentScope != null && currentScope.isMemUseTrackingEnabled()) {
1954             return true;
1955         }
1956         if (immutable.listMetrics) {
1957             key.ensureInitialized();
1958         }
1959         assert checkNoConcurrentAccess();
1960         EconomicSet<String> unscoped = immutable.unscopedMemUseTrackers;
1961         return unscoped != null && (unscoped.isEmpty() || unscoped.contains(key.getName()));
1962     }
1963 
1964     public boolean areMetricsEnabled() {
1965         return metricsEnabled;
1966     }
1967 
1968     @Override
1969     public void close() {
1970         closeDumpHandlers(false);
1971         if (description != null) {
1972             printMetrics(description);
1973         }
1974         if (metricsEnabled && metricValues != null && globalMetrics != null) {
1975             globalMetrics.add(this);
1976         }
1977         metricValues = null;
1978         if (sharedChannel != null) {
1979             try {
1980                 sharedChannel.realClose();
1981             } catch (IOException ex) {
1982                 // ignore.
1983             }
1984         }
1985     }
1986 
1987     public void closeDumpHandlers(boolean ignoreErrors) {
1988         if (currentConfig != null) {
1989             currentConfig.closeDumpHandlers(ignoreErrors);
1990         }
1991     }
1992 
1993     /**
1994      * Records how many times a given method has been compiled.
1995      */
1996     private static EconomicMap<Integer, Integer> compilations;
1997 
1998     /**
1999      * Maintains maximum buffer size used by {@link #printMetrics(Description)} to minimize buffer
2000      * resizing during subsequent calls to this method.
2001      */
2002     private static int metricsBufSize = 50_000;
2003 
2004     /**
2005      * Flag that allows the first call to {@link #printMetrics(Description)} to delete the file that
2006      * will be appended to.
2007      */
2008     private static boolean metricsFileDeleteCheckPerformed;
2009 
2010     /**
2011      * Prints metric values in this object to the file (if any) specified by
2012      * {@link DebugOptions#MetricsFile}.
2013      */
2014     public void printMetrics(Description desc) {
2015         if (metricValues == null) {
2016             return;
2017         }
2018         String metricsFile = DebugOptions.MetricsFile.getValue(getOptions());
2019         if (metricsFile != null) {
2020             // Use identity to distinguish methods that have been redefined
2021             // or loaded by different class loaders.
2022             Object compilable = desc.compilable;
2023             Integer identity = System.identityHashCode(compilable);
2024             int compilationNr;
2025             synchronized (PRINT_METRICS_LOCK) {
2026                 if (!metricsFileDeleteCheckPerformed) {
2027                     metricsFileDeleteCheckPerformed = true;
2028                     File file = new File(metricsFile);
2029                     if (file.exists()) {
2030                         // This can return false in case something like /dev/stdout
2031                         // is specified. If the file is unwriteable, the file open
2032                         // below will fail.
2033                         file.delete();
2034                     }
2035                 }
2036                 if (compilations == null) {
2037                     compilationNr = 0;
2038                     compilations = EconomicMap.create();
2039                 } else {
2040                     Integer value = compilations.get(identity);
2041                     compilationNr = value == null ? 0 : value + 1;
2042                 }
2043                 compilations.put(identity, compilationNr);
2044             }
2045 
2046             // Release the lock while generating the content to reduce contention.
2047             // This means `compilationNr` fields may show up out of order in the file.
2048             ByteArrayOutputStream baos = new ByteArrayOutputStream(metricsBufSize);
2049             PrintStream out = new PrintStream(baos);
2050             if (metricsFile.endsWith(".csv") || metricsFile.endsWith(".CSV")) {
2051                 printMetricsCSV(out, compilable, identity, compilationNr, desc.identifier);
2052             } else {
2053                 printMetrics(out, compilable, identity, compilationNr, desc.identifier);
2054             }
2055 
2056             byte[] content = baos.toByteArray();
2057             Path path = Paths.get(metricsFile);
2058             synchronized (PRINT_METRICS_LOCK) {
2059                 metricsBufSize = Math.max(metricsBufSize, content.length);
2060                 try {
2061                     Files.write(path, content, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
2062                 } catch (IOException e) {
2063                 }
2064             }
2065         }
2066     }
2067 
2068     /**
2069      * Lock to serialize writes to {@link DebugOptions#MetricsFile}.
2070      */
2071     private static final Object PRINT_METRICS_LOCK = new Object();
2072 
2073     /**
2074      * Appends metrics in CSV format to {@code out} for a single method compilation.
2075      *
2076      * @param identity the identity hash code of {@code compilable}
2077      * @param compilationNr where this compilation lies in the ordered sequence of all compilations
2078      *            identified by {@code identity}
2079      * @param compilationId the runtime issued identifier for the compilation
2080      */
2081     private void printMetricsCSV(PrintStream out, Object compilable, Integer identity, int compilationNr, String compilationId) {
2082         String compilableName = compilable instanceof JavaMethod ? ((JavaMethod) compilable).format("%H.%n(%p)%R") : String.valueOf(compilable);
2083         String csvFormat = CSVUtil.buildFormatString("%s", "%s", "%d", "%s");
2084         String format = String.format(csvFormat, CSVUtil.Escape.escapeArgs(compilableName, identity, compilationNr, compilationId));
2085         char sep = CSVUtil.SEPARATOR;
2086         format += sep + "%s" + sep + "%s" + sep + "%s";
2087         for (MetricKey key : KeyRegistry.getKeys()) {
2088             int index = ((AbstractKey) key).getIndex();
2089             if (index < metricValues.length) {
2090                 Pair<String, String> valueAndUnit = key.toCSVFormat(metricValues[index]);
2091                 CSVUtil.Escape.println(out, format, CSVUtil.Escape.escape(key.getName()), valueAndUnit.getLeft(), valueAndUnit.getRight());
2092             }
2093         }
2094     }
2095 
2096     /**
2097      * Appends metrics in a human readable format to {@code out} for a single method compilation.
2098      *
2099      * @param identity the identity hash code of {@code compilable}
2100      * @param compilationNr where this compilation lies in the ordered sequence of all compilations
2101      *            identified by {@code identity}
2102      * @param compilationId the runtime issued identifier for the compilation
2103      */
2104     private void printMetrics(PrintStream out, Object compilable, Integer identity, int compilationNr, String compilationId) {
2105         String compilableName = compilable instanceof JavaMethod ? ((JavaMethod) compilable).format("%H.%n(%p)%R") : String.valueOf(compilable);
2106         int maxKeyWidth = compilableName.length();
2107         SortedMap<String, String> res = new TreeMap<>();
2108         for (MetricKey key : KeyRegistry.getKeys()) {
2109             int index = ((AbstractKey) key).getIndex();
2110             if (index < metricValues.length && metricValues[index] != 0) {
2111                 String name = key.getName();
2112                 long value = metricValues[index];
2113                 String valueString;
2114                 if (key instanceof TimerKey) {
2115                     // Report timers in ms
2116                     TimerKey timer = (TimerKey) key;
2117                     long ms = timer.getTimeUnit().toMillis(value);
2118                     if (ms == 0) {
2119                         continue;
2120                     }
2121                     valueString = ms + "ms";
2122                 } else {
2123                     valueString = String.valueOf(value);
2124                 }
2125                 res.put(name, valueString);
2126                 maxKeyWidth = Math.max(maxKeyWidth, name.length());
2127             }
2128         }
2129 
2130         String title = String.format("%s [id:%s compilation:%d compilation_id:%s]", compilableName, identity, compilationNr, compilationId);
2131         out.println(new String(new char[title.length()]).replace('\0', '#'));
2132         out.printf("%s%n", title);
2133         out.println(new String(new char[title.length()]).replace('\0', '~'));
2134 
2135         for (Map.Entry<String, String> e : res.entrySet()) {
2136             out.printf("%-" + String.valueOf(maxKeyWidth) + "s = %20s%n", e.getKey(), e.getValue());
2137         }
2138         out.println();
2139     }
2140 
2141     @SuppressWarnings({"unused", "unchecked"})
2142     private static <E extends Exception> E rethrowSilently(Class<E> type, Throwable ex) throws E {
2143         throw (E) ex;
2144     }
2145 }