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..57f072e90f429977390690f81ae93d2f7cb13295 |
--- /dev/null |
+++ b/base/android/java/src/org/chromium/base/EarlyTraceEvent.java |
@@ -0,0 +1,210 @@ |
+// 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 android.util.Log; |
+ |
+import java.util.ArrayList; |
+import java.util.HashMap; |
+ |
+/** |
+ * Implements support for early tracing. Early tracing is used before the native |
+ * library is loaded. Recorded events are stored here on the Java side and are |
+ * later sent to the native side when trace events gets written to disk. They |
+ * will be part of the "EarlyJava" category in traces. Note that this |
+ * implementation has additional limitations. In particular, it is not possible |
+ * to have several events with the same name in flight at the same time, and the |
+ * implementation uses locks internally, so don't use it in performance-critical |
+ * sections. |
+ */ |
+@JNINamespace("base::android") |
+final class EarlyTraceEvent { |
+ private static final String TAG = "EarlyTraceEvent"; |
+ |
+ private static long sInitializionTimeMsec; |
+ private static long sInitializionThreadTimeMsec; |
+ |
+ /** |
+ * Must be kept in sync with the EarlyTraceEvent struct in early_trace_event.h. |
+ */ |
+ private static final class Event { |
+ Event(String name) { |
+ mName = name; |
+ mThreadId = Process.myTid(); |
+ mBeginTimeSinceBootMsec = SystemClock.elapsedRealtime(); |
+ mBeginThreadTimeMsec = SystemClock.currentThreadTimeMillis(); |
+ } |
+ |
+ @Override |
+ public boolean equals(Object other) { |
+ return other == null ? false : ((Event) other).mName.equals(mName); |
+ } |
+ |
+ @Override |
+ public int hashCode() { |
+ return mName.hashCode(); |
+ } |
+ |
+ void setEndTimes() { |
+ assert mEndTimeSinceBootMsec == 0 && mEndThreadTimeMsec == 0; |
+ mEndTimeSinceBootMsec = SystemClock.elapsedRealtime(); |
+ mEndThreadTimeMsec = SystemClock.currentThreadTimeMillis(); |
+ } |
+ |
+ final String mName; |
+ final long mThreadId; |
+ final long mBeginTimeSinceBootMsec; |
+ final long mBeginThreadTimeMsec; |
+ long mEndTimeSinceBootMsec; |
+ long mEndThreadTimeMsec; |
+ } |
+ |
+ /** |
+ * Allows the native side to read the trace events that were recorded early. |
+ */ |
+ static final class EarlyTraceEventsNativeReader { |
+ static EarlyTraceEventsNativeReader create(ArrayList<Event> events) { |
+ int size = events.size(); |
+ EarlyTraceEventsNativeReader reader = new EarlyTraceEventsNativeReader(size); |
+ |
+ for (int i = 0; i < size; i++) { |
+ Event event = events.get(i); |
+ reader.mNames[i] = event.mName; |
+ reader.mThreadIds[i] = event.mThreadId; |
+ reader.mBeginTimesSinceBootMsec[i] = event.mBeginTimeSinceBootMsec; |
+ reader.mBeginThreadTimesMsec[i] = event.mBeginThreadTimeMsec; |
+ reader.mEndTimesSinceBootMsec[i] = event.mEndTimeSinceBootMsec; |
+ reader.mEndThreadTimesMsec[i] = event.mEndThreadTimeMsec; |
+ } |
+ |
+ return reader; |
+ } |
+ |
+ @CalledByNative("EarlyTraceEventsNativeReader") |
+ String[] readNames() { |
+ return mNames; |
+ } |
+ |
+ @CalledByNative("EarlyTraceEventsNativeReader") |
+ long[] readThreadIds() { |
+ return mThreadIds; |
+ } |
+ |
+ @CalledByNative("EarlyTraceEventsNativeReader") |
+ long[] readBeginTimesSinceBootMsec() { |
+ return mBeginTimesSinceBootMsec; |
+ } |
+ |
+ @CalledByNative("EarlyTraceEventsNativeReader") |
+ long[] readBeginThreadTimesMsec() { |
+ return mBeginThreadTimesMsec; |
+ } |
+ |
+ @CalledByNative("EarlyTraceEventsNativeReader") |
+ long[] readEndTimesSinceBootMsec() { |
+ return mEndTimesSinceBootMsec; |
+ } |
+ |
+ @CalledByNative("EarlyTraceEventsNativeReader") |
+ long[] readEndThreadTimesMsec() { |
+ return mEndThreadTimesMsec; |
+ } |
+ |
+ private EarlyTraceEventsNativeReader(int size) { |
+ mNames = new String[size]; |
+ mThreadIds = new long[size]; |
+ mBeginTimesSinceBootMsec = new long[size]; |
+ mBeginThreadTimesMsec = new long[size]; |
+ mEndTimesSinceBootMsec = new long[size]; |
+ mEndThreadTimesMsec = new long[size]; |
+ } |
+ |
+ private final String[] mNames; |
+ private final long[] mThreadIds; |
+ private final long[] mBeginTimesSinceBootMsec; |
+ private final long[] mBeginThreadTimesMsec; |
+ private final long[] mEndTimesSinceBootMsec; |
+ private final long[] mEndThreadTimesMsec; |
+ } |
+ |
+ // Protects the state below. |
+ private static final Object sLock = new Object(); |
+ |
+ // Keeps track of all the early trace events that ended. |
+ private static final ArrayList<Event> sCompletedEvents = new ArrayList<Event>(); |
+ |
+ // Early trace events that began but haven't ended yet. Note that this assumes that there can't |
+ // be multiple pending events with the same name. |
+ private static final HashMap<String, Event> sPendingEvents = new HashMap<String, Event>(); |
+ |
+ static void enable() { |
+ sInitializionTimeMsec = SystemClock.elapsedRealtime(); |
+ sInitializionThreadTimeMsec = SystemClock.currentThreadTimeMillis(); |
+ } |
+ |
+ static boolean isEnabled() { |
+ return sInitializionTimeMsec > 0; |
+ } |
+ |
+ static void disable() { |
+ sInitializionTimeMsec = 0; |
+ } |
+ |
+ @CalledByNative |
+ static long getMsecTimeSinceBoot() { |
+ return SystemClock.elapsedRealtime(); |
+ } |
+ |
+ static void begin(String name, String arg) { |
+ if (!isEnabled()) return; |
+ |
+ Event newEvent = new Event(name); |
+ Event conflictingEvent; |
+ |
+ synchronized (sLock) { |
+ conflictingEvent = sPendingEvents.put(name, newEvent); |
+ } |
+ |
+ if (conflictingEvent != null) { |
+ // TODO(pliard): support multiple events with the same name if this appears to be a |
+ // requirement for early tracing. |
+ throw new IllegalArgumentException("Multiple pending trace events can't have the same" |
+ + " name"); |
+ } |
+ } |
+ |
+ /** |
+ * @return true iff the event with the provided name existed. |
+ */ |
+ static boolean end(String name, String arg) { |
+ if (!isEnabled()) return false; |
+ |
+ long nowMsec = SystemClock.elapsedRealtime(); |
+ |
+ synchronized (sLock) { |
+ Event event = sPendingEvents.remove(name); |
+ if (event == null) |
+ return false; |
+ |
+ event.setEndTimes(); |
+ sCompletedEvents.add(event); |
+ } |
+ return true; |
+ } |
+ |
+ @CalledByNative |
+ static EarlyTraceEventsNativeReader getAll() { |
+ synchronized (sLock) { |
+ long pendingEventsCount = sPendingEvents.size(); |
+ if (pendingEventsCount != 0) { |
+ Log.w(TAG, String.valueOf(pendingEventsCount) |
+ + " were dropped because they are still pending termination."); |
+ } |
+ return EarlyTraceEventsNativeReader.create(sCompletedEvents); |
+ } |
+ } |
+} |