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