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