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(&quot;InliningGraph&quot;, 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(&quot;CompilingStub&quot;, 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}