Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 } | |
| OLD | NEW |