OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 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.base; |
| 6 |
| 7 import android.os.Process; |
| 8 import android.os.StrictMode; |
| 9 import android.os.SystemClock; |
| 10 |
| 11 import org.chromium.base.annotations.JNINamespace; |
| 12 import org.chromium.base.annotations.SuppressFBWarnings; |
| 13 |
| 14 import java.io.File; |
| 15 import java.util.ArrayList; |
| 16 import java.util.HashMap; |
| 17 import java.util.List; |
| 18 import java.util.Map; |
| 19 |
| 20 /** Support for early tracing, before the native library is loaded. |
| 21 * |
| 22 * This is limited, as: |
| 23 * - Arguments are not supported |
| 24 * - Thread time is not reported |
| 25 * - Two events with the same name cannot be in progress at the same time. |
| 26 * |
| 27 * Events recorded here are buffered in Java until the native library is availab
le. Then it waits |
| 28 * for the completion of pending events, and sends the events to the native side
. |
| 29 * |
| 30 * Locking: This class is threadsafe. It is enabled when general tracing is, and
then disabled when |
| 31 * tracing is enabled from the native side. Event completions are still
processed as long |
| 32 * as some are pending, then early tracing is permanently disabled afte
r dumping the |
| 33 * events. This means that if any early event is still pending when tr
acing is disabled, |
| 34 * all early events are dropped. |
| 35 */ |
| 36 @JNINamespace("base::android") |
| 37 public class EarlyTraceEvent { |
| 38 // Must be kept in sync with the native kAndroidTraceConfigFile. |
| 39 private static final String TRACE_CONFIG_FILENAME = "/data/local/chrome-trac
e-config.json"; |
| 40 |
| 41 /** Single trace event. */ |
| 42 @VisibleForTesting |
| 43 static final class Event { |
| 44 final String mName; |
| 45 final int mThreadId; |
| 46 final long mBeginTimeMs; |
| 47 long mEndTimeMs; |
| 48 |
| 49 Event(String name) { |
| 50 mName = name; |
| 51 mThreadId = Process.myTid(); |
| 52 mBeginTimeMs = SystemClock.elapsedRealtime(); |
| 53 } |
| 54 |
| 55 void end() { |
| 56 assert mEndTimeMs == 0; |
| 57 mEndTimeMs = SystemClock.elapsedRealtime(); |
| 58 } |
| 59 } |
| 60 |
| 61 // State transitions are: |
| 62 // - enable(): DISABLED -> ENABLED |
| 63 // - disable(): ENABLED -> FINISHING |
| 64 // - Once there are no pending events: FINISHING -> FINISHED. |
| 65 @VisibleForTesting static final int STATE_DISABLED = 0; |
| 66 @VisibleForTesting static final int STATE_ENABLED = 1; |
| 67 @VisibleForTesting static final int STATE_FINISHING = 2; |
| 68 @VisibleForTesting static final int STATE_FINISHED = 3; |
| 69 |
| 70 // Locks the fields below. |
| 71 private static final Object sLock = new Object(); |
| 72 |
| 73 @VisibleForTesting static volatile int sState = STATE_DISABLED; |
| 74 // Not final as these object are not likely to be used at all. |
| 75 @VisibleForTesting static List<Event> sCompletedEvents; |
| 76 @VisibleForTesting static Map<String, Event> sPendingEvents; |
| 77 |
| 78 /** @see TraceEvent#MaybeEnableEarlyTracing(). |
| 79 */ |
| 80 @SuppressFBWarnings("DMI_HARDCODED_ABSOLUTE_FILENAME") |
| 81 static void maybeEnable() { |
| 82 boolean shouldEnable = false; |
| 83 // Checking for the trace config filename touches the disk. |
| 84 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); |
| 85 try { |
| 86 if (CommandLine.isInitialized() |
| 87 && CommandLine.getInstance().hasSwitch("trace-startup")) { |
| 88 shouldEnable = true; |
| 89 } else { |
| 90 try { |
| 91 shouldEnable = (new File(TRACE_CONFIG_FILENAME)).exists(); |
| 92 } catch (SecurityException e) { |
| 93 // Access denied, not enabled. |
| 94 } |
| 95 } |
| 96 } finally { |
| 97 StrictMode.setThreadPolicy(oldPolicy); |
| 98 } |
| 99 if (shouldEnable) enable(); |
| 100 } |
| 101 |
| 102 @VisibleForTesting |
| 103 static void enable() { |
| 104 synchronized (sLock) { |
| 105 if (sState != STATE_DISABLED) return; |
| 106 sCompletedEvents = new ArrayList<Event>(); |
| 107 sPendingEvents = new HashMap<String, Event>(); |
| 108 sState = STATE_ENABLED; |
| 109 } |
| 110 } |
| 111 |
| 112 /** |
| 113 * Disables Early tracing. |
| 114 * |
| 115 * Once this is called, no new event will be registered. However, end() call
s are still recorded |
| 116 * as long as there are pending events. Once there are none left, pass the e
vents to the native |
| 117 * side. |
| 118 */ |
| 119 static void disable() { |
| 120 synchronized (sLock) { |
| 121 if (sState != STATE_ENABLED) return; |
| 122 sState = STATE_FINISHING; |
| 123 maybeFinishLocked(); |
| 124 } |
| 125 } |
| 126 |
| 127 /** @see {@link TraceEvent#begin()}. */ |
| 128 public static void begin(String name) { |
| 129 // begin() and end() are going to be called once per TraceEvent, this av
oids entering a |
| 130 // synchronized block at each and every call. |
| 131 if (sState != STATE_ENABLED) return; |
| 132 Event event = new Event(name); |
| 133 Event conflictingEvent; |
| 134 synchronized (sLock) { |
| 135 if (sState != STATE_ENABLED) return; |
| 136 conflictingEvent = sPendingEvents.put(name, event); |
| 137 } |
| 138 if (conflictingEvent != null) { |
| 139 throw new IllegalArgumentException( |
| 140 "Multiple pending trace events can't have the same name"); |
| 141 } |
| 142 } |
| 143 |
| 144 /** @see {@link TraceEvent#end()}. */ |
| 145 public static void end(String name) { |
| 146 int state = sState; |
| 147 if (state != STATE_ENABLED && state != STATE_FINISHING) return; |
| 148 synchronized (sLock) { |
| 149 if (sState != STATE_ENABLED && sState != STATE_FINISHING) return; |
| 150 Event event = sPendingEvents.remove(name); |
| 151 if (event == null) return; |
| 152 event.end(); |
| 153 sCompletedEvents.add(event); |
| 154 if (sState == STATE_FINISHING) maybeFinishLocked(); |
| 155 } |
| 156 } |
| 157 |
| 158 private static void maybeFinishLocked() { |
| 159 if (!sPendingEvents.isEmpty()) return; |
| 160 sState = STATE_FINISHED; |
| 161 dumpEvents(sCompletedEvents); |
| 162 sCompletedEvents = null; |
| 163 sPendingEvents = null; |
| 164 } |
| 165 |
| 166 private static void dumpEvents(List<Event> events) { |
| 167 long nativeNowUs = nativeGetTimeTicksNowUs(); |
| 168 long javaNowUs = SystemClock.elapsedRealtime() * 1000; |
| 169 long offsetMs = (nativeNowUs - javaNowUs) / 1000; |
| 170 for (Event event : events) { |
| 171 nativeRecordEarlyEvent(event.mName, event.mBeginTimeMs + offsetMs, |
| 172 event.mEndTimeMs + offsetMs, event.mThreadId); |
| 173 } |
| 174 } |
| 175 |
| 176 private static native long nativeGetTimeTicksNowUs(); |
| 177 private static native void nativeRecordEarlyEvent( |
| 178 String name, long beginTimeMs, long endTimeMs, int threadId); |
| 179 } |
OLD | NEW |