Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(62)

Side by Side Diff: content/public/android/java/src/org/chromium/content/common/TraceEvent.java

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

Powered by Google App Engine
This is Rietveld 408576698