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