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

Unified Diff: base/android/java/src/org/chromium/base/EarlyTraceEvent.java

Issue 1486053003: Android: Add support for TraceEvent before the native library is loaded. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Proper locking. Created 5 years 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 side-by-side diff with in-line comments
Download patch
Index: base/android/java/src/org/chromium/base/EarlyTraceEvent.java
diff --git a/base/android/java/src/org/chromium/base/EarlyTraceEvent.java b/base/android/java/src/org/chromium/base/EarlyTraceEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..f3ef20754ea3dbd95d45933b37aabf39d9c92e88
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/EarlyTraceEvent.java
@@ -0,0 +1,148 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.os.Process;
+import android.os.SystemClock;
+
+import org.chromium.base.annotations.JNINamespace;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Support for early tracing, before the native library is loaded.
+ *
+ * This is limited, as:
+ * - Arguments are not supported
+ * - Thread time is not reported
+ * - Two events with the same name can be in progress at the same time.
pasko 2015/12/15 17:52:59 s/can/cannot/
Benoit L 2016/01/06 12:50:05 Good catch, thanks! Done.
+ *
+ * Events recorded here are buffered in Java until the native library is available. Then it waits
+ * for the completion of pending events, and sends the events to the native side.
+ */
+@JNINamespace("base::android")
+public class EarlyTraceEvent {
+ /** Single trace event. */
+ @VisibleForTesting
+ static final class Event {
+ final String mName;
+ final int mThreadId;
+ final long mBeginTimeMs;
+ long mEndTimeMs;
+
+ Event(String name) {
+ mName = name;
+ mThreadId = Process.myTid();
+ mBeginTimeMs = SystemClock.elapsedRealtime();
+ }
+
+ void end() {
+ assert mEndTimeMs == 0;
+ mEndTimeMs = SystemClock.elapsedRealtime();
+ }
+ }
+
+ // State transitions are:
+ // - enable(): DISABLED -> ENABLED
+ // - disable(): ENABLED -> FINISHING
+ // - Once there are no pending events: FINISHING -> FINISHED.
+ @VisibleForTesting static final int STATE_DISABLED = 0;
+ @VisibleForTesting static final int STATE_ENABLED = 1;
+ @VisibleForTesting static final int STATE_FINISHING = 2;
+ @VisibleForTesting static final int STATE_FINISHED = 3;
+
+ // Locks the fields below.
+ private static final Object sLock = new Object();
+
+ @VisibleForTesting static volatile int sState = STATE_DISABLED;
+ // Not final as these object are not likely to be used at all.
+ @VisibleForTesting static List<Event> sCompletedEvents;
+ @VisibleForTesting static Map<String, Event> sPendingEvents;
+
+ /**
+ * Enables Early Tracing.
+ *
+ * Early tracing can only be enabled once (subsequent calls are ignored).
+ */
+ static void enable() {
+ synchronized (sLock) {
+ if (sState != STATE_DISABLED) return;
+ sCompletedEvents = new ArrayList<Event>();
+ sPendingEvents = new HashMap<String, Event>();
+ sState = STATE_ENABLED;
+ }
+ }
+
+ /**
+ * Disables Early tracing.
+ *
+ * Once this is called, no new event will be registered. However, end() calls are still recorded
+ * as long as there are pending events. Once there are none left, pass the events to the native
+ * side.
+ */
+ static void disable() {
+ synchronized (sLock) {
+ if (sState != STATE_ENABLED) return;
+ sState = STATE_FINISHING;
+ maybeFinishLocked();
+ }
+ }
+
+ /** @see {@link TraceEvent#begin()}. */
+ public static void begin(String name) {
+ // begin() and end() are going to be called once per TraceEvent, this avoids entering a
+ // synchronized block at each and every call.
+ if (sState != STATE_ENABLED) return;
+ Event event = new Event(name);
+ Event conflictingEvent;
+ synchronized (sLock) {
+ if (sState != STATE_ENABLED) return;
+ conflictingEvent = sPendingEvents.put(name, event);
+ }
+ if (conflictingEvent != null) {
+ // TODO(lizeb): support multiple events with the same name if necessary.
pasko 2015/12/15 17:52:59 We do not support it for "EarlyJava", and we think
Benoit L 2016/01/06 12:50:05 Done.
+ throw new IllegalArgumentException(
+ "Multiple pending trace events can't have the same name");
+ }
+ }
+
+ /** @see {@link TraceEvent#end()}. */
+ public static void end(String name) {
+ int state = sState;
+ if (state != STATE_ENABLED && state != STATE_FINISHING) return;
+ synchronized (sLock) {
+ if (sState != STATE_ENABLED && sState != STATE_FINISHING) return;
+ Event event = sPendingEvents.remove(name);
+ if (event == null) return;
+ event.end();
+ sCompletedEvents.add(event);
+ if (sState == STATE_FINISHING) maybeFinishLocked();
+ }
+ }
+
+ private static void maybeFinishLocked() {
+ if (!sPendingEvents.isEmpty()) return;
+ sState = STATE_FINISHED;
+ dumpEvents(sCompletedEvents);
+ sCompletedEvents = null;
+ sPendingEvents = null;
+ }
+
+ private static void dumpEvents(List<Event> events) {
+ long nativeNowUs = nativeGetTimeTicksNowUs();
+ long javaNowUs = SystemClock.elapsedRealtime() * 1000;
+ long offsetMs = (nativeNowUs - javaNowUs) / 1000;
+ for (Event event : events) {
+ nativeRecordEarlyEvent(event.mName, event.mBeginTimeMs + offsetMs,
+ event.mEndTimeMs + offsetMs, event.mThreadId);
+ }
+ }
+
+ private static native long nativeGetTimeTicksNowUs();
+ private static native void nativeRecordEarlyEvent(
+ String name, long beginTimeMs, long endTimeMs, int threadId);
+}

Powered by Google App Engine
This is Rietveld 408576698