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