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