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