• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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 
5 package org.chromium.base;
6 
7 import android.os.Looper;
8 import android.os.MessageQueue;
9 import android.os.SystemClock;
10 import android.util.Log;
11 import android.util.Printer;
12 
13 import org.chromium.base.annotations.CalledByNative;
14 import org.chromium.base.annotations.JNINamespace;
15 /**
16  * Java mirror of Chrome trace event API. See base/trace_event/trace_event.h. Unlike the native
17  * version, Java does not have stack objects, so a TRACE_EVENT() which does both TRACE_EVENT_BEGIN()
18  * and TRACE_EVENT_END() in ctor/dtor is not possible.
19  * It is OK to use tracing before the native library has loaded, but such traces will
20  * be ignored. (Perhaps we could devise to buffer them up in future?).
21  */
22 @JNINamespace("base::android")
23 public class TraceEvent {
24 
25     private static volatile boolean sEnabled = false;
26     private static volatile boolean sATraceEnabled = false; // True when taking an Android systrace.
27 
28     private static class BasicLooperMonitor implements Printer {
29         @Override
println(final String line)30         public void println(final String line) {
31             if (line.startsWith(">")) {
32                 beginHandling(line);
33             } else {
34                 assert line.startsWith("<");
35                 endHandling(line);
36             }
37         }
38 
beginHandling(final String line)39         void beginHandling(final String line) {
40             if (sEnabled) nativeBeginToplevel();
41         }
42 
endHandling(final String line)43         void endHandling(final String line) {
44             if (sEnabled) nativeEndToplevel();
45         }
46     }
47 
48     /**
49      * A class that records, traces and logs statistics about the UI thead's Looper.
50      * The output of this class can be used in a number of interesting ways:
51      * <p>
52      * <ol><li>
53      * When using chrometrace, there will be a near-continuous line of
54      * measurements showing both event dispatches as well as idles;
55      * </li><li>
56      * Logging messages are output for events that run too long on the
57      * event dispatcher, making it easy to identify problematic areas;
58      * </li><li>
59      * Statistics are output whenever there is an idle after a non-trivial
60      * amount of activity, allowing information to be gathered about task
61      * density and execution cadence on the Looper;
62      * </li></ol>
63      * <p>
64      * The class attaches itself as an idle handler to the main Looper, and
65      * monitors the execution of events and idle notifications. Task counters
66      * accumulate between idle notifications and get reset when a new idle
67      * notification is received.
68      */
69     private static final class IdleTracingLooperMonitor extends BasicLooperMonitor
70             implements MessageQueue.IdleHandler {
71         // Tags for dumping to logcat or TraceEvent
72         private static final String TAG = "TraceEvent.LooperMonitor";
73         private static final String IDLE_EVENT_NAME = "Looper.queueIdle";
74 
75         // Calculation constants
76         private static final long FRAME_DURATION_MILLIS = 1000L / 60L; // 60 FPS
77         // A reasonable threshold for defining a Looper event as "long running"
78         private static final long MIN_INTERESTING_DURATION_MILLIS =
79                 FRAME_DURATION_MILLIS;
80         // A reasonable threshold for a "burst" of tasks on the Looper
81         private static final long MIN_INTERESTING_BURST_DURATION_MILLIS =
82                 MIN_INTERESTING_DURATION_MILLIS * 3;
83 
84         // Stats tracking
85         private long mLastIdleStartedAt = 0L;
86         private long mLastWorkStartedAt = 0L;
87         private int mNumTasksSeen = 0;
88         private int mNumIdlesSeen = 0;
89         private int mNumTasksSinceLastIdle = 0;
90 
91         // State
92         private boolean mIdleMonitorAttached = false;
93 
94         // Called from within the begin/end methods only.
95         // This method can only execute on the looper thread, because that is
96         // the only thread that is permitted to call Looper.myqueue().
syncIdleMonitoring()97         private final void syncIdleMonitoring() {
98             if (sEnabled && !mIdleMonitorAttached) {
99                 // approximate start time for computational purposes
100                 mLastIdleStartedAt = SystemClock.elapsedRealtime();
101                 Looper.myQueue().addIdleHandler(this);
102                 mIdleMonitorAttached = true;
103                 Log.v(TAG, "attached idle handler");
104             } else if (mIdleMonitorAttached && !sEnabled) {
105                 Looper.myQueue().removeIdleHandler(this);
106                 mIdleMonitorAttached = false;
107                 Log.v(TAG, "detached idle handler");
108             }
109         }
110 
111         @Override
beginHandling(final String line)112         final void beginHandling(final String line) {
113             // Close-out any prior 'idle' period before starting new task.
114             if (mNumTasksSinceLastIdle == 0) {
115                 TraceEvent.end(IDLE_EVENT_NAME);
116             }
117             mLastWorkStartedAt = SystemClock.elapsedRealtime();
118             syncIdleMonitoring();
119             super.beginHandling(line);
120         }
121 
122         @Override
endHandling(final String line)123         final void endHandling(final String line) {
124             final long elapsed = SystemClock.elapsedRealtime()
125                     - mLastWorkStartedAt;
126             if (elapsed > MIN_INTERESTING_DURATION_MILLIS) {
127                 traceAndLog(Log.WARN, "observed a task that took "
128                         + elapsed + "ms: " + line);
129             }
130             super.endHandling(line);
131             syncIdleMonitoring();
132             mNumTasksSeen++;
133             mNumTasksSinceLastIdle++;
134         }
135 
traceAndLog(int level, String message)136         private static void traceAndLog(int level, String message) {
137             TraceEvent.instant("TraceEvent.LooperMonitor:IdleStats", message);
138             Log.println(level, TAG, message);
139         }
140 
141         @Override
queueIdle()142         public final boolean queueIdle() {
143             final long now =  SystemClock.elapsedRealtime();
144             if (mLastIdleStartedAt == 0) mLastIdleStartedAt = now;
145             final long elapsed = now - mLastIdleStartedAt;
146             mNumIdlesSeen++;
147             TraceEvent.begin(IDLE_EVENT_NAME, mNumTasksSinceLastIdle + " tasks since last idle.");
148             if (elapsed > MIN_INTERESTING_BURST_DURATION_MILLIS) {
149                 // Dump stats
150                 String statsString = mNumTasksSeen + " tasks and "
151                         + mNumIdlesSeen + " idles processed so far, "
152                         + mNumTasksSinceLastIdle + " tasks bursted and "
153                         + elapsed + "ms elapsed since last idle";
154                 traceAndLog(Log.DEBUG, statsString);
155             }
156             mLastIdleStartedAt = now;
157             mNumTasksSinceLastIdle = 0;
158             return true; // stay installed
159         }
160     }
161 
162     // Holder for monitor avoids unnecessary construction on non-debug runs
163     private static final class LooperMonitorHolder {
164         private static final BasicLooperMonitor sInstance =
165                 CommandLine.getInstance().hasSwitch(BaseSwitches.ENABLE_IDLE_TRACING)
166                 ? new IdleTracingLooperMonitor() : new BasicLooperMonitor();
167     }
168 
169 
170     /**
171      * Register an enabled observer, such that java traces are always enabled with native.
172      */
registerNativeEnabledObserver()173     public static void registerNativeEnabledObserver() {
174         nativeRegisterEnabledObserver();
175     }
176 
177     /**
178      * Notification from native that tracing is enabled/disabled.
179      */
180     @CalledByNative
setEnabled(boolean enabled)181     public static void setEnabled(boolean enabled) {
182         sEnabled = enabled;
183         // Android M+ systrace logs this on its own. Only log it if not writing to Android systrace.
184         if (sATraceEnabled) return;
185         ThreadUtils.getUiThreadLooper().setMessageLogging(
186                 enabled ? LooperMonitorHolder.sInstance : null);
187     }
188 
189     /**
190      * Enables or disabled Android systrace path of Chrome tracing. If enabled, all Chrome
191      * traces will be also output to Android systrace. Because of the overhead of Android
192      * systrace, this is for WebView only.
193      */
setATraceEnabled(boolean enabled)194     public static void setATraceEnabled(boolean enabled) {
195         if (sATraceEnabled == enabled) return;
196         sATraceEnabled = enabled;
197         if (enabled) {
198             // Calls TraceEvent.setEnabled(true) via
199             // TraceLog::EnabledStateObserver::OnTraceLogEnabled
200             nativeStartATrace();
201         } else {
202             // Calls TraceEvent.setEnabled(false) via
203             // TraceLog::EnabledStateObserver::OnTraceLogDisabled
204             nativeStopATrace();
205         }
206     }
207 
208     /**
209      * @return True if tracing is enabled, false otherwise.
210      * It is safe to call trace methods without checking if TraceEvent
211      * is enabled.
212      */
enabled()213     public static boolean enabled() {
214         return sEnabled;
215     }
216 
217     /**
218      * Triggers the 'instant' native trace event with no arguments.
219      * @param name The name of the event.
220      */
instant(String name)221     public static void instant(String name) {
222         if (sEnabled) nativeInstant(name, null);
223     }
224 
225     /**
226      * Triggers the 'instant' native trace event.
227      * @param name The name of the event.
228      * @param arg  The arguments of the event.
229      */
instant(String name, String arg)230     public static void instant(String name, String arg) {
231         if (sEnabled) nativeInstant(name, arg);
232     }
233 
234     /**
235      * Triggers the 'start' native trace event with no arguments.
236      * @param name The name of the event.
237      * @param id   The id of the asynchronous event.
238      */
startAsync(String name, long id)239     public static void startAsync(String name, long id) {
240         if (sEnabled) nativeStartAsync(name, id);
241     }
242 
243     /**
244      * Triggers the 'finish' native trace event with no arguments.
245      * @param name The name of the event.
246      * @param id   The id of the asynchronous event.
247      */
finishAsync(String name, long id)248     public static void finishAsync(String name, long id) {
249         if (sEnabled) nativeFinishAsync(name, id);
250     }
251 
252     /**
253      * Triggers the 'begin' native trace event with no arguments.
254      * @param name The name of the event.
255      */
begin(String name)256     public static void begin(String name) {
257         if (sEnabled) nativeBegin(name, null);
258     }
259 
260     /**
261      * Triggers the 'begin' native trace event.
262      * @param name The name of the event.
263      * @param arg  The arguments of the event.
264      */
begin(String name, String arg)265     public static void begin(String name, String arg) {
266         if (sEnabled) nativeBegin(name, arg);
267     }
268 
269     /**
270      * Triggers the 'end' native trace event with no arguments.
271      * @param name The name of the event.
272      */
end(String name)273     public static void end(String name) {
274         if (sEnabled) nativeEnd(name, null);
275     }
276 
277     /**
278      * Triggers the 'end' native trace event.
279      * @param name The name of the event.
280      * @param arg  The arguments of the event.
281      */
end(String name, String arg)282     public static void end(String name, String arg) {
283         if (sEnabled) nativeEnd(name, arg);
284     }
285 
nativeRegisterEnabledObserver()286     private static native void nativeRegisterEnabledObserver();
nativeStartATrace()287     private static native void nativeStartATrace();
nativeStopATrace()288     private static native void nativeStopATrace();
nativeInstant(String name, String arg)289     private static native void nativeInstant(String name, String arg);
nativeBegin(String name, String arg)290     private static native void nativeBegin(String name, String arg);
nativeEnd(String name, String arg)291     private static native void nativeEnd(String name, String arg);
nativeBeginToplevel()292     private static native void nativeBeginToplevel();
nativeEndToplevel()293     private static native void nativeEndToplevel();
nativeStartAsync(String name, long id)294     private static native void nativeStartAsync(String name, long id);
nativeFinishAsync(String name, long id)295     private static native void nativeFinishAsync(String name, long id);
296 }
297