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