| 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 CommandLine commandLine = CommandLine.getInstance(); | |
| 87 if (commandLine != null && commandLine.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 |