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