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