1 /* 2 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.graalvm.compiler.debug; 24 25 import static java.util.FormattableFlags.LEFT_JUSTIFY; 26 import static java.util.FormattableFlags.UPPERCASE; 27 import static org.graalvm.compiler.debug.DelegatingDebugConfig.Feature.INTERCEPT; 28 import static org.graalvm.compiler.debug.DelegatingDebugConfig.Feature.LOG_METHOD; 29 30 import java.io.PrintStream; 31 import java.util.ArrayList; 32 import java.util.Collection; 33 import java.util.Collections; 34 import java.util.ConcurrentModificationException; 35 import java.util.HashSet; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Objects; 39 import java.util.Set; 40 import java.util.concurrent.Callable; 41 import java.util.concurrent.TimeUnit; 42 43 import org.graalvm.compiler.debug.DelegatingDebugConfig.Level; 44 import org.graalvm.compiler.debug.internal.CounterImpl; 45 import org.graalvm.compiler.debug.internal.DebugHistogramImpl; 46 import org.graalvm.compiler.debug.internal.DebugScope; 47 import org.graalvm.compiler.debug.internal.MemUseTrackerImpl; 48 import org.graalvm.compiler.debug.internal.TimerImpl; 49 import org.graalvm.compiler.debug.internal.method.MethodMetricsImpl; 50 import org.graalvm.compiler.options.OptionValues; 51 import org.graalvm.compiler.options.OptionValuesAccess; 52 import org.graalvm.compiler.serviceprovider.GraalServices; 53 54 import jdk.vm.ci.meta.ResolvedJavaMethod; 55 56 /** 57 * Scope based debugging facility. 58 * 59 * This facility is {@linkplain #isEnabled() enabled} if any of the following hold when the 60 * {@link Debug} class is initialized: 61 * <ul> 62 * <li>assertions are enabled for the {@link Debug} class</li> 63 * <li>{@link Debug#params}{@code .enable} is {@code true}</li> 64 * </ul> 65 */ 66 public class Debug { 67 68 /** 69 * The option values available in this package. 70 */ 71 static final OptionValues DEBUG_OPTIONS = GraalServices.loadSingle(OptionValuesAccess.class, true).getOptions(); 72 73 static final Params params = new Params(); 74 75 static { 76 // Load the service providers that may want to modify any of the 77 // parameters encapsulated by the Initialization class below. 78 for (DebugInitializationParticipant p : GraalServices.load(DebugInitializationParticipant.class)) { 79 p.apply(params); 80 } 81 } 82 83 /** 84 * The parameters for configuring the initialization of {@link Debug} class. 85 */ 86 public static final class Params { 87 public boolean enable; 88 public boolean enableMethodFilter; 89 public boolean enableUnscopedTimers; 90 public boolean enableUnscopedCounters; 91 public boolean enableUnscopedMethodMetrics; 92 public boolean enableUnscopedMemUseTrackers; 93 public boolean interceptCount; 94 public boolean interceptTime; 95 public boolean interceptMem; 96 97 @SuppressWarnings("static-method") 98 public OptionValues getOptions() { 99 return DEBUG_OPTIONS; 100 } 101 } 102 103 @SuppressWarnings("all") 104 private static boolean initialize() { 105 return Assertions.ENABLED || params.enable || GraalDebugConfig.Options.ForceDebugEnable.getValue(DEBUG_OPTIONS); 106 } 107 108 private static final boolean ENABLED = initialize(); 109 110 public static boolean isEnabled() { 111 return ENABLED; 112 } 113 114 public static boolean isDumpEnabledForMethod() { 115 if (!ENABLED) { 116 return false; 117 } 118 DebugConfig config = DebugScope.getConfig(); 119 if (config == null) { 120 return false; 121 } 122 return config.isDumpEnabledForMethod(); 123 } 124 125 /** 126 * A special dump level that indicates the dumping machinery is enabled but no dumps will be 127 * produced except through other options. 128 */ 129 public static final int ENABLED_LEVEL = 0; 130 131 /** 132 * Basic debug level. 133 * 134 * For HIR dumping, only ~5 graphs per method: after parsing, after inlining, after high tier, 135 * after mid tier, after low tier. 136 * 137 * LIR dumping: After LIR generation, after each pre-allocation, allocation and post allocation 138 * stage, and after code installation. 139 */ 140 public static final int BASIC_LEVEL = 1; 141 142 /** 143 * Informational debug level. 144 * 145 * HIR dumping: One graph after each applied top-level phase. 146 * 147 * LIR dumping: After each applied phase. 148 */ 149 public static final int INFO_LEVEL = 2; 150 151 /** 152 * Verbose debug level. 153 * 154 * HIR dumping: One graph after each phase (including sub phases). 155 * 156 * LIR dumping: After each phase including sub phases. 157 */ 158 public static final int VERBOSE_LEVEL = 3; 159 160 /** 161 * Detailed debug level. 162 * 163 * HIR dumping: Graphs within phases where interesting for a phase, max ~5 per phase. 164 * 165 * LIR dumping: Dump CFG within phases where interesting. 166 */ 167 public static final int DETAILED_LEVEL = 4; 168 169 /** 170 * Very detailed debug level. 171 * 172 * HIR dumping: Graphs per node granularity graph change (before/after change). 173 * 174 * LIR dumping: Intermediate CFGs of phases where interesting. 175 */ 176 public static final int VERY_DETAILED_LEVEL = 5; 177 178 public static boolean isDumpEnabled(int dumpLevel) { 179 return ENABLED && DebugScope.getInstance().isDumpEnabled(dumpLevel); 180 } 181 182 /** 183 * Determines if verification is enabled in the current method, regardless of the 184 * {@linkplain Debug#currentScope() current debug scope}. 185 * 186 * @see Debug#verify(Object, String) 187 */ 188 public static boolean isVerifyEnabledForMethod() { 189 if (!ENABLED) { 190 return false; 191 } 192 DebugConfig config = DebugScope.getConfig(); 193 if (config == null) { 194 return false; 195 } 196 return config.isVerifyEnabledForMethod(); 197 } 198 199 /** 200 * Determines if verification is enabled in the {@linkplain Debug#currentScope() current debug 201 * scope}. 202 * 203 * @see Debug#verify(Object, String) 204 */ 205 public static boolean isVerifyEnabled() { 206 return ENABLED && DebugScope.getInstance().isVerifyEnabled(); 207 } 208 209 public static boolean isCountEnabled() { 210 return ENABLED && DebugScope.getInstance().isCountEnabled(); 211 } 212 213 public static boolean isTimeEnabled() { 214 return ENABLED && DebugScope.getInstance().isTimeEnabled(); 215 } 216 217 public static boolean isMemUseTrackingEnabled() { 218 return ENABLED && DebugScope.getInstance().isMemUseTrackingEnabled(); 219 } 220 221 public static boolean isLogEnabledForMethod() { 222 if (!ENABLED) { 223 return false; 224 } 225 DebugConfig config = DebugScope.getConfig(); 226 if (config == null) { 227 return false; 228 } 229 return config.isLogEnabledForMethod(); 230 } 231 232 public static boolean isLogEnabled() { 233 return isLogEnabled(BASIC_LEVEL); 234 } 235 236 public static boolean isLogEnabled(int logLevel) { 237 return ENABLED && DebugScope.getInstance().isLogEnabled(logLevel); 238 } 239 240 public static boolean isMethodMeterEnabled() { 241 return ENABLED && DebugScope.getInstance().isMethodMeterEnabled(); 242 } 243 244 @SuppressWarnings("unused") 245 public static Runnable decorateDebugRoot(Runnable runnable, String name, DebugConfig config) { 246 return runnable; 247 } 248 249 @SuppressWarnings("unused") 250 public static <T> Callable<T> decorateDebugRoot(Callable<T> callable, String name, DebugConfig config) { 251 return callable; 252 } 253 254 @SuppressWarnings("unused") 255 public static Runnable decorateScope(Runnable runnable, String name, Object... context) { 256 return runnable; 257 } 258 259 @SuppressWarnings("unused") 260 public static <T> Callable<T> decorateScope(Callable<T> callable, String name, Object... context) { 261 return callable; 262 } 263 264 /** 265 * Gets a string composed of the names in the current nesting of debug 266 * {@linkplain #scope(Object) scopes} separated by {@code '.'}. 267 */ 268 public static String currentScope() { 269 if (ENABLED) { 270 return DebugScope.getInstance().getQualifiedName(); 271 } else { 272 return ""; 273 } 274 } 275 276 /** 277 * Represents a debug scope entered by {@link Debug#scope(Object)} or 278 * {@link Debug#sandbox(CharSequence, DebugConfig, Object...)}. Leaving the scope is achieved 279 * via {@link #close()}. 280 */ 281 public interface Scope extends AutoCloseable { 282 @Override 283 void close(); 284 } 285 286 /** 287 * Creates and enters a new debug scope which will be a child of the current debug scope. 288 * <p> 289 * It is recommended to use the try-with-resource statement for managing entering and leaving 290 * debug scopes. For example: 291 * 292 * <pre> 293 * try (Scope s = Debug.scope("InliningGraph", inlineeGraph)) { 294 * ... 295 * } catch (Throwable e) { 296 * throw Debug.handle(e); 297 * } 298 * </pre> 299 * 300 * The {@code name} argument is subject to the following type based conversion before having 301 * {@link Object#toString()} called on it: 302 * 303 * <pre> 304 * Type | Conversion 305 * ------------------+----------------- 306 * java.lang.Class | arg.getSimpleName() 307 * | 308 * </pre> 309 * 310 * @param name the name of the new scope 311 * @param contextObjects an array of object to be appended to the {@linkplain #context() 312 * current} debug context 313 * @throws Throwable used to enforce a catch block. 314 * @return the scope entered by this method which will be exited when its {@link Scope#close()} 315 * method is called 316 */ 317 public static Scope scope(Object name, Object[] contextObjects) throws Throwable { 318 if (ENABLED) { 319 return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null, contextObjects); 320 } else { 321 return null; 322 } 323 } 324 325 /** 326 * Similar to {@link #scope(Object, Object[])} but without context objects. Therefore the catch 327 * block can be omitted. 328 * 329 * @see #scope(Object, Object[]) 330 */ 331 public static Scope scope(Object name) { 332 if (ENABLED) { 333 return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null); 334 } else { 335 return null; 336 } 337 } 338 339 public static Scope methodMetricsScope(Object name, DebugScope.ExtraInfo metaInfo, boolean newId, Object... context) { 340 if (ENABLED) { 341 return DebugScope.getInstance().enhanceWithExtraInfo(convertFormatArg(name).toString(), metaInfo, newId, context); 342 } else { 343 return null; 344 } 345 } 346 347 /** 348 * @see #scope(Object, Object[]) 349 * @param context an object to be appended to the {@linkplain #context() current} debug context 350 */ 351 public static Scope scope(Object name, Object context) throws Throwable { 352 if (ENABLED) { 353 return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null, context); 354 } else { 355 return null; 356 } 357 } 358 359 /** 360 * @see #scope(Object, Object[]) 361 * @param context1 first object to be appended to the {@linkplain #context() current} debug 362 * context 363 * @param context2 second object to be appended to the {@linkplain #context() current} debug 364 * context 365 */ 366 public static Scope scope(Object name, Object context1, Object context2) throws Throwable { 367 if (ENABLED) { 368 return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null, context1, context2); 369 } else { 370 return null; 371 } 372 } 373 374 /** 375 * @see #scope(Object, Object[]) 376 * @param context1 first object to be appended to the {@linkplain #context() current} debug 377 * context 378 * @param context2 second object to be appended to the {@linkplain #context() current} debug 379 * context 380 * @param context3 third object to be appended to the {@linkplain #context() current} debug 381 * context 382 */ 383 public static Scope scope(Object name, Object context1, Object context2, Object context3) throws Throwable { 384 if (ENABLED) { 385 return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null, context1, context2, context3); 386 } else { 387 return null; 388 } 389 } 390 391 /** 392 * Creates and enters a new debug scope which will be disjoint from the current debug scope. 393 * <p> 394 * It is recommended to use the try-with-resource statement for managing entering and leaving 395 * debug scopes. For example: 396 * 397 * <pre> 398 * try (Scope s = Debug.sandbox("CompilingStub", null, stubGraph)) { 399 * ... 400 * } catch (Throwable e) { 401 * throw Debug.handle(e); 402 * } 403 * </pre> 404 * 405 * @param name the name of the new scope 406 * @param config the debug configuration to use for the new scope 407 * @param context objects to be appended to the {@linkplain #context() current} debug context 408 * @return the scope entered by this method which will be exited when its {@link Scope#close()} 409 * method is called 410 */ 411 public static Scope sandbox(CharSequence name, DebugConfig config, Object... context) throws Throwable { 412 if (ENABLED) { 413 DebugConfig sandboxConfig = config == null ? silentConfig() : config; 414 return DebugScope.getInstance().scope(name, sandboxConfig, context); 415 } else { 416 return null; 417 } 418 } 419 420 public static Scope forceLog() throws Throwable { 421 ArrayList<Object> context = new ArrayList<>(); 422 for (Object obj : context()) { 423 context.add(obj); 424 } 425 return Debug.sandbox("forceLog", new DelegatingDebugConfig().override(Level.LOG, Integer.MAX_VALUE).enable(LOG_METHOD), context.toArray()); 426 } 427 428 /** 429 * Opens a scope in which exception {@linkplain DebugConfig#interceptException(Throwable) 430 * interception} is disabled. It is recommended to use the try-with-resource statement for 431 * managing entering and leaving such scopes: 432 * 433 * <pre> 434 * try (DebugConfigScope s = Debug.disableIntercept()) { 435 * ... 436 * } 437 * </pre> 438 * 439 * This is particularly useful to suppress extraneous output in JUnit tests that are expected to 440 * throw an exception. 441 */ 442 public static DebugConfigScope disableIntercept() { 443 return Debug.setConfig(new DelegatingDebugConfig().disable(INTERCEPT)); 444 } 445 446 /** 447 * Handles an exception in the context of the debug scope just exited. The just exited scope 448 * must have the current scope as its parent which will be the case if the try-with-resource 449 * pattern recommended by {@link #scope(Object)} and 450 * {@link #sandbox(CharSequence, DebugConfig, Object...)} is used 451 * 452 * @see #scope(Object, Object[]) 453 * @see #sandbox(CharSequence, DebugConfig, Object...) 454 */ 455 public static RuntimeException handle(Throwable exception) { 456 if (ENABLED) { 457 return DebugScope.getInstance().handle(exception); 458 } else { 459 if (exception instanceof Error) { 460 throw (Error) exception; 461 } 462 if (exception instanceof RuntimeException) { 463 throw (RuntimeException) exception; 464 } 465 throw new RuntimeException(exception); 466 } 467 } 468 469 public static void log(String msg) { 470 log(BASIC_LEVEL, msg); 471 } 472 473 /** 474 * Prints a message to the current debug scope's logging stream if logging is enabled. 475 * 476 * @param msg the message to log 477 */ 478 public static void log(int logLevel, String msg) { 479 if (ENABLED) { 480 DebugScope.getInstance().log(logLevel, msg); 481 } 482 } 483 484 public static void log(String format, Object arg) { 485 log(BASIC_LEVEL, format, arg); 486 } 487 488 /** 489 * Prints a message to the current debug scope's logging stream if logging is enabled. 490 * 491 * @param format a format string 492 * @param arg the argument referenced by the format specifiers in {@code format} 493 */ 494 public static void log(int logLevel, String format, Object arg) { 495 if (ENABLED) { 496 DebugScope.getInstance().log(logLevel, format, arg); 497 } 498 } 499 500 public static void log(String format, int arg) { 501 log(BASIC_LEVEL, format, arg); 502 } 503 504 /** 505 * Prints a message to the current debug scope's logging stream if logging is enabled. 506 * 507 * @param format a format string 508 * @param arg the argument referenced by the format specifiers in {@code format} 509 */ 510 public static void log(int logLevel, String format, int arg) { 511 if (ENABLED) { 512 DebugScope.getInstance().log(logLevel, format, arg); 513 } 514 } 515 516 public static void log(String format, Object arg1, Object arg2) { 517 log(BASIC_LEVEL, format, arg1, arg2); 518 } 519 520 /** 521 * @see #log(int, String, Object) 522 */ 523 public static void log(int logLevel, String format, Object arg1, Object arg2) { 524 if (ENABLED) { 525 DebugScope.getInstance().log(logLevel, format, arg1, arg2); 526 } 527 } 528 529 public static void log(String format, int arg1, Object arg2) { 530 log(BASIC_LEVEL, format, arg1, arg2); 531 } 532 533 /** 534 * @see #log(int, String, Object) 535 */ 536 public static void log(int logLevel, String format, int arg1, Object arg2) { 537 if (ENABLED) { 538 DebugScope.getInstance().log(logLevel, format, arg1, arg2); 539 } 540 } 541 542 public static void log(String format, Object arg1, int arg2) { 543 log(BASIC_LEVEL, format, arg1, arg2); 544 } 545 546 /** 547 * @see #log(int, String, Object) 548 */ 549 public static void log(int logLevel, String format, Object arg1, int arg2) { 550 if (ENABLED) { 551 DebugScope.getInstance().log(logLevel, format, arg1, arg2); 552 } 553 } 554 555 public static void log(String format, int arg1, int arg2) { 556 log(BASIC_LEVEL, format, arg1, arg2); 557 } 558 559 /** 560 * @see #log(int, String, Object) 561 */ 562 public static void log(int logLevel, String format, int arg1, int arg2) { 563 if (ENABLED) { 564 DebugScope.getInstance().log(logLevel, format, arg1, arg2); 565 } 566 } 567 568 public static void log(String format, Object arg1, Object arg2, Object arg3) { 569 log(BASIC_LEVEL, format, arg1, arg2, arg3); 570 } 571 572 /** 573 * @see #log(int, String, Object) 574 */ 575 public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3) { 576 if (ENABLED) { 577 DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3); 578 } 579 } 580 581 public static void log(String format, int arg1, int arg2, int arg3) { 582 log(BASIC_LEVEL, format, arg1, arg2, arg3); 583 } 584 585 /** 586 * @see #log(int, String, Object) 587 */ 588 public static void log(int logLevel, String format, int arg1, int arg2, int arg3) { 589 if (ENABLED) { 590 DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3); 591 } 592 } 593 594 public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4) { 595 log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4); 596 } 597 598 /** 599 * @see #log(int, String, Object) 600 */ 601 public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4) { 602 if (ENABLED) { 603 DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4); 604 } 605 } 606 607 public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) { 608 log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5); 609 } 610 611 /** 612 * @see #log(int, String, Object) 613 */ 614 public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) { 615 if (ENABLED) { 616 DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4, arg5); 617 } 618 } 619 620 public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) { 621 log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6); 622 } 623 624 /** 625 * @see #log(int, String, Object) 626 */ 627 public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) { 628 if (ENABLED) { 629 DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6); 630 } 631 } 632 633 public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7) { 634 log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7); 635 } 636 637 public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8) { 638 log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); 639 } 640 641 /** 642 * @see #log(int, String, Object) 643 */ 644 public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7) { 645 if (ENABLED) { 646 DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7); 647 } 648 } 649 650 public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8) { 651 if (ENABLED) { 652 DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); 653 } 654 } 655 656 public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9) { 657 log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); 658 } 659 660 public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9) { 661 if (ENABLED) { 662 DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); 663 } 664 } 665 666 public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9, Object arg10) { 667 log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); 668 } 669 670 public static 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) { 671 if (ENABLED) { 672 DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); 673 } 674 } 675 676 public static void logv(String format, Object... args) { 677 logv(BASIC_LEVEL, format, args); 678 } 679 680 /** 681 * Prints a message to the current debug scope's logging stream. This method must only be called 682 * if debugging is {@linkplain Debug#isEnabled() enabled} as it incurs allocation at the call 683 * site. If possible, call one of the other {@code log()} methods in this class that take a 684 * fixed number of parameters. 685 * 686 * @param format a format string 687 * @param args the arguments referenced by the format specifiers in {@code format} 688 */ 689 public static void logv(int logLevel, String format, Object... args) { 690 if (!ENABLED) { 691 throw new InternalError("Use of Debug.logv() must be guarded by a test of Debug.isEnabled()"); 692 } 693 DebugScope.getInstance().log(logLevel, format, args); 694 } 695 696 /** 697 * This override exists to catch cases when {@link #log(String, Object)} is called with one 698 * argument bound to a varargs method parameter. It will bind to this method instead of the 699 * single arg variant and produce a deprecation warning instead of silently wrapping the 700 * Object[] inside of another Object[]. 701 */ 702 @Deprecated 703 public static void log(String format, Object[] args) { 704 assert false : "shouldn't use this"; 705 log(BASIC_LEVEL, format, args); 706 } 707 708 /** 709 * This override exists to catch cases when {@link #log(int, String, Object)} is called with one 710 * argument bound to a varargs method parameter. It will bind to this method instead of the 711 * single arg variant and produce a deprecation warning instead of silently wrapping the 712 * Object[] inside of another Object[]. 713 */ 714 @Deprecated 715 public static void log(int logLevel, String format, Object[] args) { 716 assert false : "shouldn't use this"; 717 logv(logLevel, format, args); 718 } 719 720 /** 721 * Forces an unconditional dump. This method exists mainly for debugging. It can also be used to 722 * force a graph dump from IDEs that support invoking a Java method while at a breakpoint. 723 */ 724 public static void forceDump(Object object, String format, Object... args) { 725 DebugScope.forceDump(object, format, args); 726 } 727 728 public static void dump(int dumpLevel, Object object, String msg) { 729 if (ENABLED && DebugScope.getInstance().isDumpEnabled(dumpLevel)) { 730 DebugScope.getInstance().dump(dumpLevel, object, msg); 731 } 732 } 733 734 public static void dump(int dumpLevel, Object object, String format, Object arg) { 735 if (ENABLED && DebugScope.getInstance().isDumpEnabled(dumpLevel)) { 736 DebugScope.getInstance().dump(dumpLevel, object, format, arg); 737 } 738 } 739 740 public static void dump(int dumpLevel, Object object, String format, Object arg1, Object arg2) { 741 if (ENABLED && DebugScope.getInstance().isDumpEnabled(dumpLevel)) { 742 DebugScope.getInstance().dump(dumpLevel, object, format, arg1, arg2); 743 } 744 } 745 746 public static void dump(int dumpLevel, Object object, String format, Object arg1, Object arg2, Object arg3) { 747 if (ENABLED && DebugScope.getInstance().isDumpEnabled(dumpLevel)) { 748 DebugScope.getInstance().dump(dumpLevel, object, format, arg1, arg2, arg3); 749 } 750 } 751 752 /** 753 * This override exists to catch cases when {@link #dump(int, Object, String, Object)} is called 754 * with one argument bound to a varargs method parameter. It will bind to this method instead of 755 * the single arg variant and produce a deprecation warning instead of silently wrapping the 756 * Object[] inside of another Object[]. 757 */ 758 @Deprecated 759 public static void dump(int dumpLevel, Object object, String format, Object[] args) { 760 assert false : "shouldn't use this"; 761 if (ENABLED && DebugScope.getInstance().isDumpEnabled(dumpLevel)) { 762 DebugScope.getInstance().dump(dumpLevel, object, format, args); 763 } 764 } 765 766 /** 767 * Calls all {@link DebugVerifyHandler}s in the current {@linkplain DebugScope#getConfig() 768 * config} to perform verification on a given object. 769 * 770 * @param object object to verify 771 * @param message description of verification context 772 * 773 * @see DebugVerifyHandler#verify(java.lang.Object, java.lang.String, java.lang.Object...) 774 */ 775 public static void verify(Object object, String message) { 776 if (ENABLED && DebugScope.getInstance().isVerifyEnabled()) { 777 DebugScope.getInstance().verify(object, message); 778 } 779 } 780 781 /** 782 * Calls all {@link DebugVerifyHandler}s in the current {@linkplain DebugScope#getConfig() 783 * config} to perform verification on a given object. 784 * 785 * @param object object to verify 786 * @param format a format string for the description of the verification context 787 * @param arg the argument referenced by the format specifiers in {@code format} 788 * 789 * @see DebugVerifyHandler#verify(java.lang.Object, java.lang.String, java.lang.Object...) 790 */ 791 public static void verify(Object object, String format, Object arg) { 792 if (ENABLED && DebugScope.getInstance().isVerifyEnabled()) { 793 DebugScope.getInstance().verify(object, format, arg); 794 } 795 } 796 797 /** 798 * This override exists to catch cases when {@link #verify(Object, String, Object)} is called 799 * with one argument bound to a varargs method parameter. It will bind to this method instead of 800 * the single arg variant and produce a deprecation warning instead of silently wrapping the 801 * Object[] inside of another Object[]. 802 */ 803 @Deprecated 804 public static void verify(Object object, String format, Object[] args) { 805 assert false : "shouldn't use this"; 806 if (ENABLED && DebugScope.getInstance().isVerifyEnabled()) { 807 DebugScope.getInstance().verify(object, format, args); 808 } 809 } 810 811 /** 812 * Opens a new indentation level (by adding some spaces) based on the current indentation level. 813 * This should be used in a {@linkplain Indent try-with-resources} pattern. 814 * 815 * @return an object that reverts to the current indentation level when 816 * {@linkplain Indent#close() closed} or null if debugging is disabled 817 * @see #logAndIndent(int, String) 818 * @see #logAndIndent(int, String, Object) 819 */ 820 public static Indent indent() { 821 if (ENABLED) { 822 DebugScope scope = DebugScope.getInstance(); 823 return scope.pushIndentLogger(); 824 } 825 return null; 826 } 827 828 public static Indent logAndIndent(String msg) { 829 return logAndIndent(BASIC_LEVEL, msg); 830 } 831 832 /** 833 * A convenience function which combines {@link #log(String)} and {@link #indent()}. 834 * 835 * @param msg the message to log 836 * @return an object that reverts to the current indentation level when 837 * {@linkplain Indent#close() closed} or null if debugging is disabled 838 */ 839 public static Indent logAndIndent(int logLevel, String msg) { 840 if (ENABLED && Debug.isLogEnabled(logLevel)) { 841 return logvAndIndentInternal(logLevel, msg); 842 } 843 return null; 844 } 845 846 public static Indent logAndIndent(String format, Object arg) { 847 return logAndIndent(BASIC_LEVEL, format, arg); 848 } 849 850 /** 851 * A convenience function which combines {@link #log(String, Object)} and {@link #indent()}. 852 * 853 * @param format a format string 854 * @param arg the argument referenced by the format specifiers in {@code format} 855 * @return an object that reverts to the current indentation level when 856 * {@linkplain Indent#close() closed} or null if debugging is disabled 857 */ 858 public static Indent logAndIndent(int logLevel, String format, Object arg) { 859 if (ENABLED && Debug.isLogEnabled(logLevel)) { 860 return logvAndIndentInternal(logLevel, format, arg); 861 } 862 return null; 863 } 864 865 public static Indent logAndIndent(String format, int arg) { 866 return logAndIndent(BASIC_LEVEL, format, arg); 867 } 868 869 /** 870 * A convenience function which combines {@link #log(String, Object)} and {@link #indent()}. 871 * 872 * @param format a format string 873 * @param arg the argument referenced by the format specifiers in {@code format} 874 * @return an object that reverts to the current indentation level when 875 * {@linkplain Indent#close() closed} or null if debugging is disabled 876 */ 877 public static Indent logAndIndent(int logLevel, String format, int arg) { 878 if (ENABLED && Debug.isLogEnabled(logLevel)) { 879 return logvAndIndentInternal(logLevel, format, arg); 880 } 881 return null; 882 } 883 884 public static Indent logAndIndent(String format, int arg1, Object arg2) { 885 return logAndIndent(BASIC_LEVEL, format, arg1, arg2); 886 } 887 888 /** 889 * @see #logAndIndent(int, String, Object) 890 */ 891 public static Indent logAndIndent(int logLevel, String format, int arg1, Object arg2) { 892 if (ENABLED && Debug.isLogEnabled(logLevel)) { 893 return logvAndIndentInternal(logLevel, format, arg1, arg2); 894 } 895 return null; 896 } 897 898 public static Indent logAndIndent(String format, Object arg1, int arg2) { 899 return logAndIndent(BASIC_LEVEL, format, arg1, arg2); 900 } 901 902 /** 903 * @see #logAndIndent(int, String, Object) 904 */ 905 public static Indent logAndIndent(int logLevel, String format, Object arg1, int arg2) { 906 if (ENABLED && Debug.isLogEnabled(logLevel)) { 907 return logvAndIndentInternal(logLevel, format, arg1, arg2); 908 } 909 return null; 910 } 911 912 public static Indent logAndIndent(String format, int arg1, int arg2) { 913 return logAndIndent(BASIC_LEVEL, format, arg1, arg2); 914 } 915 916 /** 917 * @see #logAndIndent(int, String, Object) 918 */ 919 public static Indent logAndIndent(int logLevel, String format, int arg1, int arg2) { 920 if (ENABLED && Debug.isLogEnabled(logLevel)) { 921 return logvAndIndentInternal(logLevel, format, arg1, arg2); 922 } 923 return null; 924 } 925 926 public static Indent logAndIndent(String format, Object arg1, Object arg2) { 927 return logAndIndent(BASIC_LEVEL, format, arg1, arg2); 928 } 929 930 /** 931 * @see #logAndIndent(int, String, Object) 932 */ 933 public static Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2) { 934 if (ENABLED && Debug.isLogEnabled(logLevel)) { 935 return logvAndIndentInternal(logLevel, format, arg1, arg2); 936 } 937 return null; 938 } 939 940 public static Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3) { 941 return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3); 942 } 943 944 /** 945 * @see #logAndIndent(int, String, Object) 946 */ 947 public static Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3) { 948 if (ENABLED && Debug.isLogEnabled(logLevel)) { 949 return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3); 950 } 951 return null; 952 } 953 954 public static Indent logAndIndent(String format, int arg1, int arg2, int arg3) { 955 return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3); 956 } 957 958 /** 959 * @see #logAndIndent(int, String, Object) 960 */ 961 public static Indent logAndIndent(int logLevel, String format, int arg1, int arg2, int arg3) { 962 if (ENABLED && Debug.isLogEnabled(logLevel)) { 963 return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3); 964 } 965 return null; 966 } 967 968 public static Indent logAndIndent(String format, Object arg1, int arg2, int arg3) { 969 return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3); 970 } 971 972 /** 973 * @see #logAndIndent(int, String, Object) 974 */ 975 public static Indent logAndIndent(int logLevel, String format, Object arg1, int arg2, int arg3) { 976 if (ENABLED && Debug.isLogEnabled(logLevel)) { 977 return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3); 978 } 979 return null; 980 } 981 982 public static Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4) { 983 return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3, arg4); 984 } 985 986 /** 987 * @see #logAndIndent(int, String, Object) 988 */ 989 public static Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4) { 990 if (ENABLED && Debug.isLogEnabled(logLevel)) { 991 return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3, arg4); 992 } 993 return null; 994 } 995 996 public static Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) { 997 return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5); 998 } 999 1000 /** 1001 * @see #logAndIndent(int, String, Object) 1002 */ 1003 public static Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) { 1004 if (ENABLED && Debug.isLogEnabled(logLevel)) { 1005 return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3, arg4, arg5); 1006 } 1007 return null; 1008 } 1009 1010 public static Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) { 1011 return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6); 1012 } 1013 1014 /** 1015 * @see #logAndIndent(int, String, Object) 1016 */ 1017 public static Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) { 1018 if (ENABLED && Debug.isLogEnabled(logLevel)) { 1019 return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6); 1020 } 1021 return null; 1022 } 1023 1024 /** 1025 * A convenience function which combines {@link #logv(int, String, Object...)} and 1026 * {@link #indent()}. 1027 * 1028 * @param format a format string 1029 * @param args the arguments referenced by the format specifiers in {@code format} 1030 * @return an object that reverts to the current indentation level when 1031 * {@linkplain Indent#close() closed} or null if debugging is disabled 1032 */ 1033 public static Indent logvAndIndent(int logLevel, String format, Object... args) { 1034 if (ENABLED) { 1035 if (Debug.isLogEnabled(logLevel)) { 1036 return logvAndIndentInternal(logLevel, format, args); 1037 } 1038 return null; 1039 } 1040 throw new InternalError("Use of Debug.logvAndIndent() must be guarded by a test of Debug.isEnabled()"); 1041 } 1042 1043 private static Indent logvAndIndentInternal(int logLevel, String format, Object... args) { 1044 assert ENABLED && Debug.isLogEnabled(logLevel) : "must have checked Debug.isLogEnabled()"; 1045 DebugScope scope = DebugScope.getInstance(); 1046 scope.log(logLevel, format, args); 1047 return scope.pushIndentLogger(); 1048 } 1049 1050 /** 1051 * This override exists to catch cases when {@link #logAndIndent(String, Object)} is called with 1052 * one argument bound to a varargs method parameter. It will bind to this method instead of the 1053 * single arg variant and produce a deprecation warning instead of silently wrapping the 1054 * Object[] inside of another Object[]. 1055 */ 1056 @Deprecated 1057 public static void logAndIndent(String format, Object[] args) { 1058 assert false : "shouldn't use this"; 1059 logAndIndent(BASIC_LEVEL, format, args); 1060 } 1061 1062 /** 1063 * This override exists to catch cases when {@link #logAndIndent(int, String, Object)} is called 1064 * with one argument bound to a varargs method parameter. It will bind to this method instead of 1065 * the single arg variant and produce a deprecation warning instead of silently wrapping the 1066 * Object[] inside of another Object[]. 1067 */ 1068 @Deprecated 1069 public static void logAndIndent(int logLevel, String format, Object[] args) { 1070 assert false : "shouldn't use this"; 1071 logvAndIndent(logLevel, format, args); 1072 } 1073 1074 public static Iterable<Object> context() { 1075 if (ENABLED) { 1076 return DebugScope.getInstance().getCurrentContext(); 1077 } else { 1078 return Collections.emptyList(); 1079 } 1080 } 1081 1082 @SuppressWarnings("unchecked") 1083 public static <T> List<T> contextSnapshot(Class<T> clazz) { 1084 if (ENABLED) { 1085 List<T> result = new ArrayList<>(); 1086 for (Object o : context()) { 1087 if (clazz.isInstance(o)) { 1088 result.add((T) o); 1089 } 1090 } 1091 return result; 1092 } else { 1093 return Collections.emptyList(); 1094 } 1095 } 1096 1097 /** 1098 * Searches the current debug scope, bottom up, for a context object that is an instance of a 1099 * given type. The first such object found is returned. 1100 */ 1101 @SuppressWarnings("unchecked") 1102 public static <T> T contextLookup(Class<T> clazz) { 1103 if (ENABLED) { 1104 for (Object o : context()) { 1105 if (clazz.isInstance(o)) { 1106 return ((T) o); 1107 } 1108 } 1109 } 1110 return null; 1111 } 1112 1113 /** 1114 * Creates a {@linkplain DebugMemUseTracker memory use tracker} that is enabled iff debugging is 1115 * {@linkplain #isEnabled() enabled}. 1116 * <p> 1117 * A disabled tracker has virtually no overhead. 1118 */ 1119 public static DebugMemUseTracker memUseTracker(CharSequence name) { 1120 if (!isUnconditionalMemUseTrackingEnabled && !ENABLED) { 1121 return VOID_MEM_USE_TRACKER; 1122 } 1123 return createMemUseTracker("%s", name, null); 1124 } 1125 1126 /** 1127 * Creates a debug memory use tracker. Invoking this method is equivalent to: 1128 * 1129 * <pre> 1130 * Debug.memUseTracker(format, arg, null) 1131 * </pre> 1132 * 1133 * except that the string formatting only happens if mem tracking is enabled. 1134 * 1135 * @see #counter(String, Object, Object) 1136 */ 1137 public static DebugMemUseTracker memUseTracker(String format, Object arg) { 1138 if (!isUnconditionalMemUseTrackingEnabled && !ENABLED) { 1139 return VOID_MEM_USE_TRACKER; 1140 } 1141 return createMemUseTracker(format, arg, null); 1142 } 1143 1144 /** 1145 * Creates a debug memory use tracker. Invoking this method is equivalent to: 1146 * 1147 * <pre> 1148 * Debug.memUseTracker(String.format(format, arg1, arg2)) 1149 * </pre> 1150 * 1151 * except that the string formatting only happens if memory use tracking is enabled. In 1152 * addition, each argument is subject to the following type based conversion before being passed 1153 * as an argument to {@link String#format(String, Object...)}: 1154 * 1155 * <pre> 1156 * Type | Conversion 1157 * ------------------+----------------- 1158 * java.lang.Class | arg.getSimpleName() 1159 * | 1160 * </pre> 1161 * 1162 * @see #memUseTracker(CharSequence) 1163 */ 1164 public static DebugMemUseTracker memUseTracker(String format, Object arg1, Object arg2) { 1165 if (!isUnconditionalMemUseTrackingEnabled && !ENABLED) { 1166 return VOID_MEM_USE_TRACKER; 1167 } 1168 return createMemUseTracker(format, arg1, arg2); 1169 } 1170 1171 private static DebugMemUseTracker createMemUseTracker(String format, Object arg1, Object arg2) { 1172 String name = formatDebugName(format, arg1, arg2); 1173 return DebugValueFactory.createMemUseTracker(name, !isUnconditionalMemUseTrackingEnabled); 1174 } 1175 1176 /** 1177 * Creates a {@linkplain DebugCounter counter} that is enabled iff debugging is 1178 * {@linkplain #isEnabled() enabled} or the system property whose name is formed by adding 1179 * {@value #ENABLE_COUNTER_PROPERTY_NAME_PREFIX} to {@code name} is 1180 * {@linkplain Boolean#getBoolean(String) true}. If the latter condition is true, then the 1181 * returned counter is {@linkplain DebugCounter#isConditional() unconditional} otherwise it is 1182 * conditional. 1183 * <p> 1184 * A disabled counter has virtually no overhead. 1185 */ 1186 public static DebugCounter counter(CharSequence name) { 1187 if (!areUnconditionalCountersEnabled() && !ENABLED) { 1188 return VOID_COUNTER; 1189 } 1190 return createCounter("%s", name, null); 1191 } 1192 1193 /** 1194 * Creates a {@link DebugMethodMetrics metric} that is enabled iff debugging is 1195 * {@link #isEnabled() enabled}. 1196 */ 1197 public static DebugMethodMetrics methodMetrics(ResolvedJavaMethod method) { 1198 if (isMethodMeterEnabled() && method != null) { 1199 return MethodMetricsImpl.getMethodMetrics(method); 1200 } 1201 return VOID_MM; 1202 } 1203 1204 public static String applyFormattingFlagsAndWidth(String s, int flags, int width) { 1205 if (flags == 0 && width < 0) { 1206 return s; 1207 } 1208 StringBuilder sb = new StringBuilder(s); 1209 1210 // apply width and justification 1211 int len = sb.length(); 1212 if (len < width) { 1213 for (int i = 0; i < width - len; i++) { 1214 if ((flags & LEFT_JUSTIFY) == LEFT_JUSTIFY) { 1215 sb.append(' '); 1216 } else { 1217 sb.insert(0, ' '); 1218 } 1219 } 1220 } 1221 1222 String res = sb.toString(); 1223 if ((flags & UPPERCASE) == UPPERCASE) { 1224 res = res.toUpperCase(); 1225 } 1226 return res; 1227 } 1228 1229 /** 1230 * Creates a debug counter. Invoking this method is equivalent to: 1231 * 1232 * <pre> 1233 * Debug.counter(format, arg, null) 1234 * </pre> 1235 * 1236 * except that the string formatting only happens if count is enabled. 1237 * 1238 * @see #counter(String, Object, Object) 1239 */ 1240 public static DebugCounter counter(String format, Object arg) { 1241 if (!areUnconditionalCountersEnabled() && !ENABLED) { 1242 return VOID_COUNTER; 1243 } 1244 return createCounter(format, arg, null); 1245 } 1246 1247 /** 1248 * Creates a debug counter. Invoking this method is equivalent to: 1249 * 1250 * <pre> 1251 * Debug.counter(String.format(format, arg1, arg2)) 1252 * </pre> 1253 * 1254 * except that the string formatting only happens if count is enabled. In addition, each 1255 * argument is subject to the following type based conversion before being passed as an argument 1256 * to {@link String#format(String, Object...)}: 1257 * 1258 * <pre> 1259 * Type | Conversion 1260 * ------------------+----------------- 1261 * java.lang.Class | arg.getSimpleName() 1262 * | 1263 * </pre> 1264 * 1265 * @see #counter(CharSequence) 1266 */ 1267 public static DebugCounter counter(String format, Object arg1, Object arg2) { 1268 if (!areUnconditionalCountersEnabled() && !ENABLED) { 1269 return VOID_COUNTER; 1270 } 1271 return createCounter(format, arg1, arg2); 1272 } 1273 1274 private static DebugCounter createCounter(String format, Object arg1, Object arg2) { 1275 String name = formatDebugName(format, arg1, arg2); 1276 boolean conditional = enabledCounters == null || !findMatch(enabledCounters, enabledCountersSubstrings, name); 1277 if (!ENABLED && conditional) { 1278 return VOID_COUNTER; 1279 } 1280 return DebugValueFactory.createCounter(name, conditional); 1281 } 1282 1283 /** 1284 * Changes the debug configuration for the current thread. 1285 * 1286 * @param config new configuration to use for the current thread 1287 * @return an object that when {@linkplain DebugConfigScope#close() closed} will restore the 1288 * debug configuration for the current thread to what it was before this method was 1289 * called 1290 */ 1291 public static DebugConfigScope setConfig(DebugConfig config) { 1292 if (ENABLED) { 1293 return new DebugConfigScope(config); 1294 } else { 1295 return null; 1296 } 1297 } 1298 1299 /** 1300 * Creates an object for counting value frequencies. 1301 */ 1302 public static DebugHistogram createHistogram(String name) { 1303 return new DebugHistogramImpl(name); 1304 } 1305 1306 public static DebugConfig silentConfig() { 1307 return fixedConfig(new OptionValues(OptionValues.newOptionMap()), 0, 0, false, false, false, false, false, Collections.<DebugDumpHandler> emptyList(), 1308 Collections.<DebugVerifyHandler> emptyList(), null); 1309 } 1310 1311 public static DebugConfig fixedConfig(OptionValues options, final int logLevel, final int dumpLevel, final boolean isCountEnabled, final boolean isMemUseTrackingEnabled, 1312 final boolean isTimerEnabled, 1313 final boolean isVerifyEnabled, final boolean isMMEnabled, final Collection<DebugDumpHandler> dumpHandlers, final Collection<DebugVerifyHandler> verifyHandlers, 1314 final PrintStream output) { 1315 return new DebugConfig() { 1316 1317 @Override 1318 public OptionValues getOptions() { 1319 return options; 1320 } 1321 1322 @Override 1323 public int getLogLevel() { 1324 return logLevel; 1325 } 1326 1327 @Override 1328 public boolean isLogEnabledForMethod() { 1329 return logLevel > 0; 1330 } 1331 1332 @Override 1333 public boolean isCountEnabled() { 1334 return isCountEnabled; 1335 } 1336 1337 @Override 1338 public boolean isMemUseTrackingEnabled() { 1339 return isMemUseTrackingEnabled; 1340 } 1341 1342 @Override 1343 public int getDumpLevel() { 1344 return dumpLevel; 1345 } 1346 1347 @Override 1348 public boolean isDumpEnabledForMethod() { 1349 return dumpLevel > 0; 1350 } 1351 1352 @Override 1353 public boolean isVerifyEnabled() { 1354 return isVerifyEnabled; 1355 } 1356 1357 @Override 1358 public boolean isVerifyEnabledForMethod() { 1359 return isVerifyEnabled; 1360 } 1361 1362 @Override 1363 public boolean isMethodMeterEnabled() { 1364 return isMMEnabled; 1365 } 1366 1367 @Override 1368 public boolean isTimeEnabled() { 1369 return isTimerEnabled; 1370 } 1371 1372 @Override 1373 public RuntimeException interceptException(Throwable e) { 1374 return null; 1375 } 1376 1377 @Override 1378 public Collection<DebugDumpHandler> dumpHandlers() { 1379 return dumpHandlers; 1380 } 1381 1382 @Override 1383 public Collection<DebugVerifyHandler> verifyHandlers() { 1384 return verifyHandlers; 1385 } 1386 1387 @Override 1388 public PrintStream output() { 1389 return output; 1390 } 1391 1392 @Override 1393 public void addToContext(Object o) { 1394 } 1395 1396 @Override 1397 public void removeFromContext(Object o) { 1398 } 1399 }; 1400 } 1401 1402 private static final DebugCounter VOID_COUNTER = new DebugCounter() { 1403 1404 @Override 1405 public void increment() { 1406 } 1407 1408 @Override 1409 public void add(long value) { 1410 } 1411 1412 @Override 1413 public void setConditional(boolean flag) { 1414 throw new InternalError("Cannot make void counter conditional"); 1415 } 1416 1417 @Override 1418 public boolean isConditional() { 1419 return false; 1420 } 1421 1422 @Override 1423 public long getCurrentValue() { 1424 return 0L; 1425 } 1426 }; 1427 1428 private static final DebugMethodMetrics VOID_MM = new DebugMethodMetrics() { 1429 1430 @Override 1431 public void addToMetric(long value, String metricName) { 1432 } 1433 1434 @Override 1435 public void addToMetric(long value, String format, Object arg1) { 1436 } 1437 1438 @Override 1439 public void addToMetric(long value, String format, Object arg1, Object arg2) { 1440 } 1441 1442 @Override 1443 public void addToMetric(long value, String format, Object arg1, Object arg2, Object arg3) { 1444 } 1445 1446 @Override 1447 public void incrementMetric(String metricName) { 1448 } 1449 1450 @Override 1451 public void incrementMetric(String format, Object arg1) { 1452 } 1453 1454 @Override 1455 public void incrementMetric(String format, Object arg1, Object arg2) { 1456 } 1457 1458 @Override 1459 public void incrementMetric(String format, Object arg1, Object arg2, Object arg3) { 1460 } 1461 1462 @Override 1463 public long getCurrentMetricValue(String metricName) { 1464 return 0; 1465 } 1466 1467 @Override 1468 public long getCurrentMetricValue(String format, Object arg1) { 1469 return 0; 1470 } 1471 1472 @Override 1473 public long getCurrentMetricValue(String format, Object arg1, Object arg2) { 1474 return 0; 1475 } 1476 1477 @Override 1478 public long getCurrentMetricValue(String format, Object arg1, Object arg2, Object arg3) { 1479 return 0; 1480 } 1481 1482 @Override 1483 public ResolvedJavaMethod getMethod() { 1484 return null; 1485 } 1486 1487 }; 1488 1489 private static final DebugMemUseTracker VOID_MEM_USE_TRACKER = new DebugMemUseTracker() { 1490 1491 @Override 1492 public DebugCloseable start() { 1493 return DebugCloseable.VOID_CLOSEABLE; 1494 } 1495 1496 @Override 1497 public long getCurrentValue() { 1498 return 0; 1499 } 1500 }; 1501 1502 /** 1503 * @see #timer(CharSequence) 1504 */ 1505 public static final String ENABLE_TIMER_PROPERTY_NAME_PREFIX = "graaldebug.timer."; 1506 1507 /** 1508 * @see #counter(CharSequence) 1509 */ 1510 public static final String ENABLE_COUNTER_PROPERTY_NAME_PREFIX = "graaldebug.counter."; 1511 1512 /** 1513 * Set of unconditionally enabled counters. Possible values and their meanings: 1514 * <ul> 1515 * <li>{@code null}: no unconditionally enabled counters</li> 1516 * <li>{@code isEmpty()}: all counters are unconditionally enabled</li> 1517 * <li>{@code !isEmpty()}: use {@link #findMatch(Set, Set, String)} on this set and 1518 * {@link #enabledCountersSubstrings} to determine which counters are unconditionally enabled 1519 * </li> 1520 * </ul> 1521 */ 1522 private static final Set<String> enabledCounters; 1523 1524 /** 1525 * Set of unconditionally enabled timers. Same interpretation of values as for 1526 * {@link #enabledCounters}. 1527 */ 1528 private static final Set<String> enabledTimers; 1529 1530 private static final Set<String> enabledCountersSubstrings = new HashSet<>(); 1531 private static final Set<String> enabledTimersSubstrings = new HashSet<>(); 1532 1533 /** 1534 * Specifies if all mem use trackers are unconditionally enabled. 1535 */ 1536 private static final boolean isUnconditionalMemUseTrackingEnabled; 1537 1538 static { 1539 Set<String> counters = new HashSet<>(); 1540 Set<String> timers = new HashSet<>(); 1541 parseCounterAndTimerSystemProperties(counters, timers, enabledCountersSubstrings, enabledTimersSubstrings); 1542 counters = counters.isEmpty() && enabledCountersSubstrings.isEmpty() ? null : counters; 1543 timers = timers.isEmpty() && enabledTimersSubstrings.isEmpty() ? null : timers; 1544 if (counters == null && params.enableUnscopedCounters && !params.enableMethodFilter) { 1545 counters = Collections.emptySet(); 1546 } 1547 if (timers == null && params.enableUnscopedTimers && !params.enableMethodFilter) { 1548 timers = Collections.emptySet(); 1549 } 1550 enabledCounters = counters; 1551 enabledTimers = timers; 1552 isUnconditionalMemUseTrackingEnabled = params.enableUnscopedMemUseTrackers; 1553 DebugValueFactory = initDebugValueFactory(); 1554 } 1555 1556 private static DebugValueFactory initDebugValueFactory() { 1557 return new DebugValueFactory() { 1558 1559 @Override 1560 public DebugTimer createTimer(String name, boolean conditional) { 1561 return new TimerImpl(name, conditional, params.interceptTime); 1562 } 1563 1564 @Override 1565 public DebugCounter createCounter(String name, boolean conditional) { 1566 return CounterImpl.create(name, conditional, params.interceptCount); 1567 } 1568 1569 @Override 1570 public DebugMethodMetrics createMethodMetrics(ResolvedJavaMethod method) { 1571 return MethodMetricsImpl.getMethodMetrics(method); 1572 } 1573 1574 @Override 1575 public DebugMemUseTracker createMemUseTracker(String name, boolean conditional) { 1576 return new MemUseTrackerImpl(name, conditional, params.interceptMem); 1577 } 1578 }; 1579 } 1580 1581 private static DebugValueFactory DebugValueFactory; 1582 1583 public static void setDebugValueFactory(DebugValueFactory factory) { 1584 Objects.requireNonNull(factory); 1585 DebugValueFactory = factory; 1586 } 1587 1588 public static DebugValueFactory getDebugValueFactory() { 1589 return DebugValueFactory; 1590 } 1591 1592 private static boolean findMatch(Set<String> haystack, Set<String> haystackSubstrings, String needle) { 1593 if (haystack.isEmpty() && haystackSubstrings.isEmpty()) { 1594 // Empty haystack means match all 1595 return true; 1596 } 1597 if (haystack.contains(needle)) { 1598 return true; 1599 } 1600 if (!haystackSubstrings.isEmpty()) { 1601 for (String h : haystackSubstrings) { 1602 if (needle.startsWith(h)) { 1603 return true; 1604 } 1605 } 1606 } 1607 return false; 1608 } 1609 1610 public static boolean areUnconditionalTimersEnabled() { 1611 return enabledTimers != null; 1612 } 1613 1614 public static boolean areUnconditionalCountersEnabled() { 1615 return enabledCounters != null; 1616 } 1617 1618 public static boolean isMethodFilteringEnabled() { 1619 return params.enableMethodFilter; 1620 } 1621 1622 public static boolean areUnconditionalMethodMetricsEnabled() { 1623 // we do not collect mm substrings 1624 return params.enableUnscopedMethodMetrics; 1625 } 1626 1627 protected static void parseCounterAndTimerSystemProperties(Set<String> counters, Set<String> timers, Set<String> countersSubstrings, Set<String> timersSubstrings) { 1628 do { 1629 try { 1630 for (Map.Entry<Object, Object> e : System.getProperties().entrySet()) { 1631 String name = e.getKey().toString(); 1632 if (name.startsWith(ENABLE_COUNTER_PROPERTY_NAME_PREFIX) && Boolean.parseBoolean(e.getValue().toString())) { 1633 if (name.endsWith("*")) { 1634 countersSubstrings.add(name.substring(ENABLE_COUNTER_PROPERTY_NAME_PREFIX.length(), name.length() - 1)); 1635 } else { 1636 counters.add(name.substring(ENABLE_COUNTER_PROPERTY_NAME_PREFIX.length())); 1637 } 1638 } 1639 if (name.startsWith(ENABLE_TIMER_PROPERTY_NAME_PREFIX) && Boolean.parseBoolean(e.getValue().toString())) { 1640 if (name.endsWith("*")) { 1641 timersSubstrings.add(name.substring(ENABLE_TIMER_PROPERTY_NAME_PREFIX.length(), name.length() - 1)); 1642 } else { 1643 timers.add(name.substring(ENABLE_TIMER_PROPERTY_NAME_PREFIX.length())); 1644 } 1645 } 1646 } 1647 return; 1648 } catch (ConcurrentModificationException e) { 1649 // Iterating over the system properties may race with another thread that is 1650 // updating the system properties. Simply try again in this case. 1651 } 1652 } while (true); 1653 } 1654 1655 /** 1656 * Creates a {@linkplain DebugTimer timer} that is enabled iff debugging is 1657 * {@linkplain #isEnabled() enabled} or the system property whose name is formed by adding 1658 * {@value #ENABLE_TIMER_PROPERTY_NAME_PREFIX} to {@code name} is 1659 * {@linkplain Boolean#getBoolean(String) true}. If the latter condition is true, then the 1660 * returned timer is {@linkplain DebugCounter#isConditional() unconditional} otherwise it is 1661 * conditional. 1662 * <p> 1663 * A disabled timer has virtually no overhead. 1664 */ 1665 public static DebugTimer timer(CharSequence name) { 1666 if (!areUnconditionalTimersEnabled() && !ENABLED) { 1667 return VOID_TIMER; 1668 } 1669 return createTimer("%s", name, null); 1670 } 1671 1672 /** 1673 * Creates a debug timer. Invoking this method is equivalent to: 1674 * 1675 * <pre> 1676 * Debug.timer(format, arg, null) 1677 * </pre> 1678 * 1679 * except that the string formatting only happens if timing is enabled. 1680 * 1681 * @see #timer(String, Object, Object) 1682 */ 1683 public static DebugTimer timer(String format, Object arg) { 1684 if (!areUnconditionalTimersEnabled() && !ENABLED) { 1685 return VOID_TIMER; 1686 } 1687 return createTimer(format, arg, null); 1688 } 1689 1690 /** 1691 * Creates a debug timer. Invoking this method is equivalent to: 1692 * 1693 * <pre> 1694 * Debug.timer(String.format(format, arg1, arg2)) 1695 * </pre> 1696 * 1697 * except that the string formatting only happens if timing is enabled. In addition, each 1698 * argument is subject to the following type based conversion before being passed as an argument 1699 * to {@link String#format(String, Object...)}: 1700 * 1701 * <pre> 1702 * Type | Conversion 1703 * ------------------+----------------- 1704 * java.lang.Class | arg.getSimpleName() 1705 * | 1706 * </pre> 1707 * 1708 * @see #timer(CharSequence) 1709 */ 1710 public static DebugTimer timer(String format, Object arg1, Object arg2) { 1711 if (!areUnconditionalTimersEnabled() && !ENABLED) { 1712 return VOID_TIMER; 1713 } 1714 return createTimer(format, arg1, arg2); 1715 } 1716 1717 /** 1718 * There are paths where construction of formatted class names are common and the code below is 1719 * surprisingly expensive, so compute it once and cache it. 1720 */ 1721 private static final ClassValue<String> formattedClassName = new ClassValue<String>() { 1722 @Override 1723 protected String computeValue(Class<?> c) { 1724 final String simpleName = c.getSimpleName(); 1725 Class<?> enclosingClass = c.getEnclosingClass(); 1726 if (enclosingClass != null) { 1727 String prefix = ""; 1728 while (enclosingClass != null) { 1729 prefix = enclosingClass.getSimpleName() + "_" + prefix; 1730 enclosingClass = enclosingClass.getEnclosingClass(); 1731 } 1732 return prefix + simpleName; 1733 } else { 1734 return simpleName; 1735 } 1736 } 1737 }; 1738 1739 public static Object convertFormatArg(Object arg) { 1740 if (arg instanceof Class) { 1741 return formattedClassName.get((Class<?>) arg); 1742 } 1743 return arg; 1744 } 1745 1746 private static String formatDebugName(String format, Object arg1, Object arg2) { 1747 return String.format(format, convertFormatArg(arg1), convertFormatArg(arg2)); 1748 } 1749 1750 private static DebugTimer createTimer(String format, Object arg1, Object arg2) { 1751 String name = formatDebugName(format, arg1, arg2); 1752 boolean conditional = enabledTimers == null || !findMatch(enabledTimers, enabledTimersSubstrings, name); 1753 if (!ENABLED && conditional) { 1754 return VOID_TIMER; 1755 } 1756 return DebugValueFactory.createTimer(name, conditional); 1757 } 1758 1759 private static final DebugTimer VOID_TIMER = new DebugTimer() { 1760 1761 @Override 1762 public DebugCloseable start() { 1763 return DebugCloseable.VOID_CLOSEABLE; 1764 } 1765 1766 @Override 1767 public void setConditional(boolean flag) { 1768 throw new InternalError("Cannot make void timer conditional"); 1769 } 1770 1771 @Override 1772 public boolean isConditional() { 1773 return false; 1774 } 1775 1776 @Override 1777 public long getCurrentValue() { 1778 return 0L; 1779 } 1780 1781 @Override 1782 public TimeUnit getTimeUnit() { 1783 return null; 1784 } 1785 }; 1786 }