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("InliningGraph", 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("CompilingStub", 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 }