1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.util;
18
19import java.util.ArrayList;
20
21import android.os.SystemClock;
22
23/**
24 * A utility class to help log timings splits throughout a method call.
25 * Typical usage is:
26 *
27 * <pre>
28 *     TimingLogger timings = new TimingLogger(TAG, "methodA");
29 *     // ... do some work A ...
30 *     timings.addSplit("work A");
31 *     // ... do some work B ...
32 *     timings.addSplit("work B");
33 *     // ... do some work C ...
34 *     timings.addSplit("work C");
35 *     timings.dumpToLog();
36 * </pre>
37 *
38 * <p>The dumpToLog call would add the following to the log:</p>
39 *
40 * <pre>
41 *     D/TAG     ( 3459): methodA: begin
42 *     D/TAG     ( 3459): methodA:      9 ms, work A
43 *     D/TAG     ( 3459): methodA:      1 ms, work B
44 *     D/TAG     ( 3459): methodA:      6 ms, work C
45 *     D/TAG     ( 3459): methodA: end, 16 ms
46 * </pre>
47 */
48public class TimingLogger {
49
50    /**
51     * The Log tag to use for checking Log.isLoggable and for
52     * logging the timings.
53     */
54    private String mTag;
55
56    /** A label to be included in every log. */
57    private String mLabel;
58
59    /** Used to track whether Log.isLoggable was enabled at reset time. */
60    private boolean mDisabled;
61
62    /** Stores the time of each split. */
63    ArrayList<Long> mSplits;
64
65    /** Stores the labels for each split. */
66    ArrayList<String> mSplitLabels;
67
68    /**
69     * Create and initialize a TimingLogger object that will log using
70     * the specific tag. If the Log.isLoggable is not enabled to at
71     * least the Log.VERBOSE level for that tag at creation time then
72     * the addSplit and dumpToLog call will do nothing.
73     * @param tag the log tag to use while logging the timings
74     * @param label a string to be displayed with each log
75     */
76    public TimingLogger(String tag, String label) {
77        reset(tag, label);
78    }
79
80    /**
81     * Clear and initialize a TimingLogger object that will log using
82     * the specific tag. If the Log.isLoggable is not enabled to at
83     * least the Log.VERBOSE level for that tag at creation time then
84     * the addSplit and dumpToLog call will do nothing.
85     * @param tag the log tag to use while logging the timings
86     * @param label a string to be displayed with each log
87     */
88    public void reset(String tag, String label) {
89        mTag = tag;
90        mLabel = label;
91        reset();
92    }
93
94    /**
95     * Clear and initialize a TimingLogger object that will log using
96     * the tag and label that was specified previously, either via
97     * the constructor or a call to reset(tag, label). If the
98     * Log.isLoggable is not enabled to at least the Log.VERBOSE
99     * level for that tag at creation time then the addSplit and
100     * dumpToLog call will do nothing.
101     */
102    public void reset() {
103        mDisabled = !Log.isLoggable(mTag, Log.VERBOSE);
104        if (mDisabled) return;
105        if (mSplits == null) {
106            mSplits = new ArrayList<Long>();
107            mSplitLabels = new ArrayList<String>();
108        } else {
109            mSplits.clear();
110            mSplitLabels.clear();
111        }
112        addSplit(null);
113    }
114
115    /**
116     * Add a split for the current time, labeled with splitLabel. If
117     * Log.isLoggable was not enabled to at least the Log.VERBOSE for
118     * the specified tag at construction or reset() time then this
119     * call does nothing.
120     * @param splitLabel a label to associate with this split.
121     */
122    public void addSplit(String splitLabel) {
123        if (mDisabled) return;
124        long now = SystemClock.elapsedRealtime();
125        mSplits.add(now);
126        mSplitLabels.add(splitLabel);
127    }
128
129    /**
130     * Dumps the timings to the log using Log.d(). If Log.isLoggable was
131     * not enabled to at least the Log.VERBOSE for the specified tag at
132     * construction or reset() time then this call does nothing.
133     */
134    public void dumpToLog() {
135        if (mDisabled) return;
136        Log.d(mTag, mLabel + ": begin");
137        final long first = mSplits.get(0);
138        long now = first;
139        for (int i = 1; i < mSplits.size(); i++) {
140            now = mSplits.get(i);
141            final String splitLabel = mSplitLabels.get(i);
142            final long prev = mSplits.get(i - 1);
143
144            Log.d(mTag, mLabel + ":      " + (now - prev) + " ms, " + splitLabel);
145        }
146        Log.d(mTag, mLabel + ": end, " + (now - first) + " ms");
147    }
148}
149