EMMA Coverage Report (generated Fri Aug 23 16:39:17 PDT 2013)
[all classes][org.chromium.content.common]

COVERAGE SUMMARY FOR SOURCE FILE [TraceEvent.java]

nameclass, %method, %block, %line, %
TraceEvent.java25%  (1/4)25%  (9/36)7%   (35/524)13%  (16.4/129)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class TraceEvent$10%   (0/1)0%   (0/2)0%   (0/5)0%   (0/3)
TraceEvent$1 (): void 0%   (0/1)0%   (0/3)0%   (0/1)
run (): void 0%   (0/1)0%   (0/2)0%   (0/2)
     
class TraceEvent$LooperMonitor0%   (0/1)0%   (0/10)0%   (0/225)0%   (0/52)
<static initializer> 0%   (0/1)0%   (0/8)0%   (0/1)
TraceEvent$LooperMonitor (): void 0%   (0/1)0%   (0/21)0%   (0/8)
TraceEvent$LooperMonitor (TraceEvent$1): void 0%   (0/1)0%   (0/3)0%   (0/1)
begin (String): void 0%   (0/1)0%   (0/14)0%   (0/6)
end (String): void 0%   (0/1)0%   (0/40)0%   (0/8)
getInstance (): TraceEvent$LooperMonitor 0%   (0/1)0%   (0/2)0%   (0/1)
println (String): void 0%   (0/1)0%   (0/22)0%   (0/5)
queueIdle (): boolean 0%   (0/1)0%   (0/71)0%   (0/11)
syncIdleMonitoring (): void 0%   (0/1)0%   (0/35)0%   (0/10)
traceAndLog (int, String): void 0%   (0/1)0%   (0/9)0%   (0/3)
     
class TraceEvent$LooperMonitor$Holder0%   (0/1)0%   (0/3)0%   (0/11)0%   (0/2)
<static initializer> 0%   (0/1)0%   (0/6)0%   (0/1)
TraceEvent$LooperMonitor$Holder (): void 0%   (0/1)0%   (0/3)0%   (0/1)
access$200 (): TraceEvent$LooperMonitor 0%   (0/1)0%   (0/2)0%   (0/1)
     
class TraceEvent100% (1/1)43%  (9/21)12%  (35/283)22%  (16.4/75)
TraceEvent (): void 0%   (0/1)0%   (0/3)0%   (0/2)
access$000 (): boolean 0%   (0/1)0%   (0/2)0%   (0/1)
begin (String, String): void 0%   (0/1)0%   (0/6)0%   (0/2)
enabled (): boolean 0%   (0/1)0%   (0/2)0%   (0/1)
end (String, String): void 0%   (0/1)0%   (0/6)0%   (0/2)
finishAsync (String, long): void 0%   (0/1)0%   (0/7)0%   (0/2)
finishAsync (String, long, String): void 0%   (0/1)0%   (0/7)0%   (0/2)
finishAsync (long): void 0%   (0/1)0%   (0/7)0%   (0/2)
getCallerName (): String 0%   (0/1)0%   (0/20)0%   (0/2)
startAsync (String, long): void 0%   (0/1)0%   (0/7)0%   (0/2)
startAsync (String, long, String): void 0%   (0/1)0%   (0/7)0%   (0/2)
startAsync (long): void 0%   (0/1)0%   (0/7)0%   (0/2)
<static initializer> 100% (1/1)6%   (6/96)14%  (3/22)
setEnabledToMatchNative (): void 100% (1/1)12%  (7/56)25%  (4/16)
setEnabled (boolean): void 100% (1/1)29%  (4/14)25%  (1/4)
begin (): void 100% (1/1)50%  (3/6)70%  (1.4/2)
begin (String): void 100% (1/1)50%  (3/6)70%  (1.4/2)
end (): void 100% (1/1)50%  (3/6)70%  (1.4/2)
end (String): void 100% (1/1)50%  (3/6)70%  (1.4/2)
instant (String): void 100% (1/1)50%  (3/6)70%  (1.4/2)
instant (String, String): void 100% (1/1)50%  (3/6)70%  (1.4/2)

1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4 
5package org.chromium.content.common;
6 
7import android.os.Build;
8import android.os.Looper;
9import android.os.MessageQueue;
10import android.os.SystemClock;
11import android.util.Log;
12import android.util.Printer;
13 
14import java.lang.reflect.InvocationTargetException;
15import java.lang.reflect.Method;
16 
17// Java mirror of Chrome trace event API.  See
18// base/debug/trace_event.h.  Unlike the native version, Java does not
19// have stack objects, so a TRACE_EVENT() which does both
20// TRACE_EVENT_BEGIN() and TRACE_EVENT_END() in ctor/dtor is not
21// possible.
22// It is OK to use tracing before the native library has loaded, but such traces will
23// be ignored. (Perhaps we could devise to buffer them up in future?).
24public class TraceEvent {
25 
26    private static volatile boolean sEnabled = false;
27 
28    /**
29     * A class that records, traces and logs statistics about the main Looper.
30     * The output of this class can be used in a number of interesting ways:
31     * <p>
32     * <ol><li>
33     * When using chrometrace, there will be a near-continuous line of
34     * measurements showing both event dispatches as well as idles;
35     * </li><li>
36     * Logging messages are output for events that run too long on the
37     * event dispatcher, making it easy to identify problematic areas;
38     * </li><li>
39     * Statistics are output whenever there is an idle after a non-trivial
40     * amount of activity, allowing information to be gathered about task
41     * density and execution cadence on the Looper;
42     * </li></ol>
43     * <p>
44     * The class attaches itself as an idle handler to the main Looper, and
45     * monitors the execution of events and idle notifications. Task counters
46     * accumulate between idle notifications and get reset when a new idle
47     * notification is received.
48     */
49    private final static class LooperMonitor implements Printer, MessageQueue.IdleHandler {
50        // Tags for dumping to logcat or TraceEvent
51        private final static String TAG = "TraceEvent.LooperMonitor";
52        private final static String IDLE_EVENT_NAME = "Looper.queueIdle";
53        private final static String DISPATCH_EVENT_NAME =
54                "Looper.dispatchMessage";
55 
56        // Calculation constants
57        private final static long FRAME_DURATION_MILLIS = 1000L / 60L; // 60 FPS
58        // A reasonable threshold for defining a Looper event as "long running"
59        private final static long MIN_INTERESTING_DURATION_MILLIS =
60                FRAME_DURATION_MILLIS;
61        // A reasonable threshold for a "burst" of tasks on the Looper
62        private final static long MIN_INTERESTING_BURST_DURATION_MILLIS =
63                MIN_INTERESTING_DURATION_MILLIS * 3;
64 
65        // Stats tracking
66        private long mLastIdleStartedAt = 0L;
67        private long mLastWorkStartedAt = 0L;
68        private int mNumTasksSeen = 0;
69        private int mNumIdlesSeen = 0;
70        private int mNumTasksSinceLastIdle = 0;
71 
72        // State
73        private boolean mIdleMonitorAttached = false;
74 
75        @Override
76        public void println(final String line) {
77            if (line.startsWith(">")) {
78                begin(line);
79            } else {
80                assert line.startsWith("<");
81                end(line);
82            }
83        }
84 
85        // Called from within the begin/end methods only.
86        // This method can only execute on the looper thread, because that is
87        // the only thread that is permitted to call Looper.myqueue().
88        private final void syncIdleMonitoring() {
89            if (sEnabled && !mIdleMonitorAttached) {
90                // approximate start time for computational purposes
91                mLastIdleStartedAt = SystemClock.elapsedRealtime();
92                Looper.myQueue().addIdleHandler(
93                        LooperMonitor.getInstance());
94                mIdleMonitorAttached = true;
95                Log.v(TAG, "attached idle handler");
96            } else if (mIdleMonitorAttached && !sEnabled) {
97                Looper.myQueue().removeIdleHandler(
98                        LooperMonitor.getInstance());
99                mIdleMonitorAttached = false;
100                Log.v(TAG, "detached idle handler");
101            }
102        }
103 
104        private final void begin(final String line) {
105            // Close-out any prior 'idle' period before starting new task.
106            if (mNumTasksSinceLastIdle == 0) {
107                TraceEvent.end(IDLE_EVENT_NAME);
108            }
109            TraceEvent.begin(DISPATCH_EVENT_NAME, line);
110            mLastWorkStartedAt = SystemClock.elapsedRealtime();
111            syncIdleMonitoring();
112        }
113 
114        private final void end(final String line) {
115            final long elapsed = SystemClock.elapsedRealtime()
116                    - mLastWorkStartedAt;
117            if (elapsed > MIN_INTERESTING_DURATION_MILLIS) {
118                traceAndLog(Log.WARN, "observed a task that took "
119                        + elapsed + "ms: " + line);
120            }
121            TraceEvent.end(DISPATCH_EVENT_NAME);
122            syncIdleMonitoring();
123            mNumTasksSeen++;
124            mNumTasksSinceLastIdle++;
125        }
126 
127        private static void traceAndLog(int level, String message) {
128            TraceEvent.instant("TraceEvent.LooperMonitor:IdleStats", message);
129            Log.println(level, TAG, message);
130        }
131 
132        @Override
133        public final boolean queueIdle() {
134            final long now =  SystemClock.elapsedRealtime();
135            if (mLastIdleStartedAt == 0) mLastIdleStartedAt = now;
136            final long elapsed = now - mLastIdleStartedAt;
137            mNumIdlesSeen++;
138            TraceEvent.begin(IDLE_EVENT_NAME, mNumTasksSinceLastIdle + " tasks since last idle.");
139            if (elapsed > MIN_INTERESTING_BURST_DURATION_MILLIS) {
140                // Dump stats
141                String statsString = mNumTasksSeen + " tasks and "
142                        + mNumIdlesSeen + " idles processed so far, "
143                        + mNumTasksSinceLastIdle + " tasks bursted and "
144                        + elapsed + "ms elapsed since last idle";
145                traceAndLog(Log.DEBUG, statsString);
146            }
147            mLastIdleStartedAt = now;
148            mNumTasksSinceLastIdle = 0;
149            return true; // stay installed
150        }
151 
152        // Holder for monitor avoids unnecessary construction on non-debug runs
153        private final static class Holder {
154            private final static LooperMonitor sInstance = new LooperMonitor();
155        }
156        public final static LooperMonitor getInstance() {
157            return Holder.sInstance;
158        }
159    }
160 
161    private static long sTraceTagView;
162    private static Method sSystemPropertiesGetLongMethod;
163    private static final String PROPERTY_TRACE_TAG_ENABLEFLAGS = "debug.atrace.tags.enableflags";
164 
165    static {
166        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
167            try {
168                Class<?> traceClass = Class.forName("android.os.Trace");
169                sTraceTagView = traceClass.getField("TRACE_TAG_WEBVIEW").getLong(null);
170 
171                Class<?> systemPropertiesClass = Class.forName("android.os.SystemProperties");
172                sSystemPropertiesGetLongMethod = systemPropertiesClass.getDeclaredMethod(
173                        "getLong", String.class, Long.TYPE);
174                Method addChangeCallbackMethod = systemPropertiesClass.getDeclaredMethod(
175                        "addChangeCallback", Runnable.class);
176 
177                // Won't reach here if any of the above reflect lookups fail.
178                addChangeCallbackMethod.invoke(null, new Runnable() {
179                    @Override
180                    public void run() {
181                        setEnabledToMatchNative();
182                    }
183                });
184            } catch (ClassNotFoundException e) {
185                Log.e("TraceEvent", "init", e);
186            } catch (NoSuchMethodException e) {
187                Log.e("TraceEvent", "init", e);
188            } catch (IllegalArgumentException e) {
189                Log.e("TraceEvent", "init", e);
190            } catch (IllegalAccessException e) {
191                Log.e("TraceEvent", "init", e);
192            } catch (InvocationTargetException e) {
193                Log.e("TraceEvent", "init", e);
194            } catch (NoSuchFieldException e) {
195                Log.e("TraceEvent", "init", e);
196            }
197        }
198    }
199 
200    /**
201     * Calling this will cause enabled() to be updated to match that set on the native side.
202     * The native library must be loaded before calling this method.
203     */
204    public static void setEnabledToMatchNative() {
205        boolean enabled = nativeTraceEnabled();
206 
207        if (sSystemPropertiesGetLongMethod != null) {
208            try {
209                long enabledFlags = (Long) sSystemPropertiesGetLongMethod.invoke(
210                        null, PROPERTY_TRACE_TAG_ENABLEFLAGS, 0);
211                if ((enabledFlags & sTraceTagView) != 0) {
212                    nativeStartATrace();
213                    enabled = true;
214                } else {
215                    nativeStopATrace();
216                }
217            } catch (IllegalArgumentException e) {
218                Log.e("TraceEvent", "setEnabledToMatchNative", e);
219            } catch (IllegalAccessException e) {
220                Log.e("TraceEvent", "setEnabledToMatchNative", e);
221            } catch (InvocationTargetException e) {
222                Log.e("TraceEvent", "setEnabledToMatchNative", e);
223            }
224        }
225 
226        setEnabled(enabled);
227    }
228 
229    /**
230     * Enables or disables tracing.
231     * The native library must be loaded before the first call with enabled == true.
232     */
233    public static synchronized void setEnabled(boolean enabled) {
234        if (sEnabled == enabled) return;
235        sEnabled = enabled;
236        Looper.getMainLooper().setMessageLogging(
237                enabled ? LooperMonitor.getInstance() : null);
238    }
239 
240    /**
241     * @return True if tracing is enabled, false otherwise.
242     * It is safe to call trace methods without checking if TraceEvent
243     * is enabled.
244     */
245    public static boolean enabled() {
246        return sEnabled;
247    }
248 
249    /**
250     * Triggers the 'instant' native trace event with no arguments.
251     * @param name The name of the event.
252     */
253    public static void instant(String name) {
254        if (sEnabled) nativeInstant(name, null);
255    }
256 
257    /**
258     * Triggers the 'instant' native trace event.
259     * @param name The name of the event.
260     * @param arg  The arguments of the event.
261     */
262    public static void instant(String name, String arg) {
263        if (sEnabled) nativeInstant(name, arg);
264    }
265 
266    /**
267     * Convenience wrapper around the versions of startAsync() that take string parameters.
268     * @param id The id of the asynchronous event.  Will automatically figure out the name from
269     *           calling {@link #getCallerName()}.
270     * @see #begin()
271     */
272    public static void startAsync(long id) {
273        if (sEnabled) nativeStartAsync(getCallerName(), id, null);
274    }
275 
276    /**
277     * Triggers the 'start' native trace event with no arguments.
278     * @param name The name of the event.
279     * @param id   The id of the asynchronous event.
280     * @see #begin()
281     */
282    public static void startAsync(String name, long id) {
283        if (sEnabled) nativeStartAsync(name, id, null);
284    }
285 
286    /**
287     * Triggers the 'start' native trace event.
288     * @param name The name of the event.
289     * @param id   The id of the asynchronous event.
290     * @param arg  The arguments of the event.
291     * @see #begin()
292     */
293    public static void startAsync(String name, long id, String arg) {
294        if (sEnabled) nativeStartAsync(name, id, arg);
295    }
296 
297    /**
298     * Convenience wrapper around the versions of finishAsync() that take string parameters.
299     * @param id The id of the asynchronous event.  Will automatically figure out the name from
300     *           calling {@link #getCallerName()}.
301     * @see #finish()
302     */
303    public static void finishAsync(long id) {
304        if (sEnabled) nativeFinishAsync(getCallerName(), id, null);
305    }
306 
307    /**
308     * Triggers the 'finish' native trace event with no arguments.
309     * @param name The name of the event.
310     * @param id   The id of the asynchronous event.
311     * @see #begin()
312     */
313    public static void finishAsync(String name, long id) {
314        if (sEnabled) nativeFinishAsync(name, id, null);
315    }
316 
317    /**
318     * Triggers the 'finish' native trace event.
319     * @param name The name of the event.
320     * @param id   The id of the asynchronous event.
321     * @param arg  The arguments of the event.
322     * @see #begin()
323     */
324    public static void finishAsync(String name, long id, String arg) {
325        if (sEnabled) nativeFinishAsync(name, id, arg);
326    }
327 
328    /**
329     * Convenience wrapper around the versions of begin() that take string parameters.
330     * The name of the event will be derived from the class and function name that call this.
331     * IMPORTANT: if using this version, ensure end() (no parameters) is always called from the
332     * same calling context.
333     */
334    public static void begin() {
335        if (sEnabled) nativeBegin(getCallerName(), null);
336    }
337 
338    /**
339     * Triggers the 'begin' native trace event with no arguments.
340     * @param name The name of the event.
341     */
342    public static void begin(String name) {
343        if (sEnabled) nativeBegin(name, null);
344    }
345 
346    /**
347     * Triggers the 'begin' native trace event.
348     * @param name The name of the event.
349     * @param arg  The arguments of the event.
350     */
351    public static void begin(String name, String arg) {
352        if (sEnabled) nativeBegin(name, arg);
353    }
354 
355    /**
356     * Convenience wrapper around the versions of end() that take string parameters. See begin()
357     * for more information.
358     */
359    public static void end() {
360        if (sEnabled) nativeEnd(getCallerName(), null);
361    }
362 
363    /**
364     * Triggers the 'end' native trace event with no arguments.
365     * @param name The name of the event.
366     */
367    public static void end(String name) {
368        if (sEnabled) nativeEnd(name, null);
369    }
370 
371    /**
372     * Triggers the 'end' native trace event.
373     * @param name The name of the event.
374     * @param arg  The arguments of the event.
375     */
376    public static void end(String name, String arg) {
377        if (sEnabled) nativeEnd(name, arg);
378    }
379 
380    private static String getCallerName() {
381        // This was measured to take about 1ms on Trygon device.
382        StackTraceElement[] stack = java.lang.Thread.currentThread().getStackTrace();
383 
384        // Commented out to avoid excess call overhead, but these lines can be useful to debug
385        // exactly where the TraceEvent's client is on the callstack.
386        //  int index = 0;
387        //  while (!stack[index].getClassName().equals(TraceEvent.class.getName())) ++index;
388        //  while (stack[index].getClassName().equals(TraceEvent.class.getName())) ++index;
389        //  System.logW("TraceEvent caller is at stack index " + index);
390 
391        // '4' Was derived using the above commented out code snippet.
392        return stack[4].getClassName() + "." + stack[4].getMethodName();
393    }
394 
395    private static native boolean nativeTraceEnabled();
396    private static native void nativeStartATrace();
397    private static native void nativeStopATrace();
398    private static native void nativeInstant(String name, String arg);
399    private static native void nativeBegin(String name, String arg);
400    private static native void nativeEnd(String name, String arg);
401    private static native void nativeStartAsync(String name, long id, String arg);
402    private static native void nativeFinishAsync(String name, long id, String arg);
403}

[all classes][org.chromium.content.common]
EMMA 2.0.5312 (C) Vladimir Roubtsov