Chromium Code Reviews| 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..d525cd19d017226be5fed012588c9d4b092d5d0d |
| --- /dev/null |
| +++ b/base/android/java/src/org/chromium/base/EarlyTraceEvent.java |
| @@ -0,0 +1,202 @@ |
| +// 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. |
| + */ |
| +@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. |
| + assert false; |
|
dsinclair
2015/03/16 14:41:23
Should this be removed?
Benoit L
2015/03/19 16:50:47
Done.
|
| + Log.e(TAG, "Multiple pending trace events can't have the same name, the generated " |
| + + "trace will be incorrect."); |
| + } |
| + } |
| + |
| + /** |
| + * @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) { |
| + assert sPendingEvents.size() == 0; |
| + return EarlyTraceEventsNativeReader.create(sCompletedEvents); |
| + } |
| + } |
| +} |