| 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);
|
| + }
|
| + }
|
| +}
|
|
|