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