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