Commit c6d31e97 authored by Dmitry Skiba's avatar Dmitry Skiba Committed by Commit Bot

Key pending EarlyTraceEvents by name+tid.

Chrome tracing allows starting similarly named events from different
threads. EarlyTraceEvents throws an exception in this case because it
keys pending events by name only.

This CL changes pending events key to be name+tid, separating events
from different threads.

Bug: 807034
Change-Id: Ia6d4ac740ec028a14c5269d09148db4d1a5d33ce
Reviewed-on: https://chromium-review.googlesource.com/1055941
Commit-Queue: Dmitry Skiba <dskiba@chromium.org>
Reviewed-by: default avataragrieve <agrieve@chromium.org>
Cr-Commit-Position: refs/heads/master@{#558829}
parent de1c2239
...@@ -106,7 +106,8 @@ public class EarlyTraceEvent { ...@@ -106,7 +106,8 @@ public class EarlyTraceEvent {
@VisibleForTesting static volatile int sState = STATE_DISABLED; @VisibleForTesting static volatile int sState = STATE_DISABLED;
// Not final as these object are not likely to be used at all. // Not final as these object are not likely to be used at all.
@VisibleForTesting static List<Event> sCompletedEvents; @VisibleForTesting static List<Event> sCompletedEvents;
@VisibleForTesting static Map<String, Event> sPendingEvents; @VisibleForTesting
static Map<String, Event> sPendingEventByKey;
@VisibleForTesting static List<AsyncEvent> sAsyncEvents; @VisibleForTesting static List<AsyncEvent> sAsyncEvents;
@VisibleForTesting static List<String> sPendingAsyncEvents; @VisibleForTesting static List<String> sPendingAsyncEvents;
...@@ -138,7 +139,7 @@ public class EarlyTraceEvent { ...@@ -138,7 +139,7 @@ public class EarlyTraceEvent {
synchronized (sLock) { synchronized (sLock) {
if (sState != STATE_DISABLED) return; if (sState != STATE_DISABLED) return;
sCompletedEvents = new ArrayList<Event>(); sCompletedEvents = new ArrayList<Event>();
sPendingEvents = new HashMap<String, Event>(); sPendingEventByKey = new HashMap<String, Event>();
sAsyncEvents = new ArrayList<AsyncEvent>(); sAsyncEvents = new ArrayList<AsyncEvent>();
sPendingAsyncEvents = new ArrayList<String>(); sPendingAsyncEvents = new ArrayList<String>();
sState = STATE_ENABLED; sState = STATE_ENABLED;
...@@ -183,7 +184,7 @@ public class EarlyTraceEvent { ...@@ -183,7 +184,7 @@ public class EarlyTraceEvent {
Event conflictingEvent; Event conflictingEvent;
synchronized (sLock) { synchronized (sLock) {
if (!enabled()) return; if (!enabled()) return;
conflictingEvent = sPendingEvents.put(name, event); conflictingEvent = sPendingEventByKey.put(makeEventKeyForCurrentThread(name), event);
} }
if (conflictingEvent != null) { if (conflictingEvent != null) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
...@@ -196,7 +197,7 @@ public class EarlyTraceEvent { ...@@ -196,7 +197,7 @@ public class EarlyTraceEvent {
if (!isActive()) return; if (!isActive()) return;
synchronized (sLock) { synchronized (sLock) {
if (!isActive()) return; if (!isActive()) return;
Event event = sPendingEvents.remove(name); Event event = sPendingEventByKey.remove(makeEventKeyForCurrentThread(name));
if (event == null) return; if (event == null) return;
event.end(); event.end();
sCompletedEvents.add(event); sCompletedEvents.add(event);
...@@ -231,7 +232,7 @@ public class EarlyTraceEvent { ...@@ -231,7 +232,7 @@ public class EarlyTraceEvent {
static void resetForTesting() { static void resetForTesting() {
sState = EarlyTraceEvent.STATE_DISABLED; sState = EarlyTraceEvent.STATE_DISABLED;
sCompletedEvents = null; sCompletedEvents = null;
sPendingEvents = null; sPendingEventByKey = null;
sAsyncEvents = null; sAsyncEvents = null;
sPendingAsyncEvents = null; sPendingAsyncEvents = null;
} }
...@@ -245,9 +246,9 @@ public class EarlyTraceEvent { ...@@ -245,9 +246,9 @@ public class EarlyTraceEvent {
dumpAsyncEvents(sAsyncEvents); dumpAsyncEvents(sAsyncEvents);
sAsyncEvents.clear(); sAsyncEvents.clear();
} }
if (sPendingEvents.isEmpty() && sPendingAsyncEvents.isEmpty()) { if (sPendingEventByKey.isEmpty() && sPendingAsyncEvents.isEmpty()) {
sState = STATE_FINISHED; sState = STATE_FINISHED;
sPendingEvents = null; sPendingEventByKey = null;
sCompletedEvents = null; sCompletedEvents = null;
sPendingAsyncEvents = null; sPendingAsyncEvents = null;
sAsyncEvents = null; sAsyncEvents = null;
...@@ -279,6 +280,16 @@ public class EarlyTraceEvent { ...@@ -279,6 +280,16 @@ public class EarlyTraceEvent {
return nativeNowNanos - javaNowNanos; return nativeNowNanos - javaNowNanos;
} }
/**
* Returns a key which consists of |name| and the ID of the current thread.
* The key is used with pending events making them thread-specific, thus avoiding
* an exception when similarly named events are started from multiple threads.
*/
@VisibleForTesting
static String makeEventKeyForCurrentThread(String name) {
return name + "@" + Process.myTid();
}
private static native void nativeRecordEarlyEvent(String name, long beginTimNanos, private static native void nativeRecordEarlyEvent(String name, long beginTimNanos,
long endTimeNanos, int threadId, long threadDurationMillis); long endTimeNanos, int threadId, long threadDurationMillis);
private static native void nativeRecordEarlyStartAsyncEvent( private static native void nativeRecordEarlyStartAsyncEvent(
......
...@@ -50,7 +50,7 @@ public class EarlyTraceEventTest { ...@@ -50,7 +50,7 @@ public class EarlyTraceEventTest {
long afterNanos = Event.elapsedRealtimeNanos(); long afterNanos = Event.elapsedRealtimeNanos();
Assert.assertEquals(1, EarlyTraceEvent.sCompletedEvents.size()); Assert.assertEquals(1, EarlyTraceEvent.sCompletedEvents.size());
Assert.assertTrue(EarlyTraceEvent.sPendingEvents.isEmpty()); Assert.assertTrue(EarlyTraceEvent.sPendingEventByKey.isEmpty());
Event event = EarlyTraceEvent.sCompletedEvents.get(0); Event event = EarlyTraceEvent.sCompletedEvents.get(0);
Assert.assertEquals(EVENT_NAME, event.mName); Assert.assertEquals(EVENT_NAME, event.mName);
Assert.assertEquals(myThreadId, event.mThreadId); Assert.assertEquals(myThreadId, event.mThreadId);
...@@ -71,7 +71,7 @@ public class EarlyTraceEventTest { ...@@ -71,7 +71,7 @@ public class EarlyTraceEventTest {
long afterNanos = Event.elapsedRealtimeNanos(); long afterNanos = Event.elapsedRealtimeNanos();
Assert.assertEquals(2, EarlyTraceEvent.sAsyncEvents.size()); Assert.assertEquals(2, EarlyTraceEvent.sAsyncEvents.size());
Assert.assertTrue(EarlyTraceEvent.sPendingEvents.isEmpty()); Assert.assertTrue(EarlyTraceEvent.sPendingEventByKey.isEmpty());
AsyncEvent eventStart = EarlyTraceEvent.sAsyncEvents.get(0); AsyncEvent eventStart = EarlyTraceEvent.sAsyncEvents.get(0);
AsyncEvent eventEnd = EarlyTraceEvent.sAsyncEvents.get(1); AsyncEvent eventEnd = EarlyTraceEvent.sAsyncEvents.get(1);
Assert.assertEquals(EVENT_NAME, eventStart.mName); Assert.assertEquals(EVENT_NAME, eventStart.mName);
...@@ -111,7 +111,7 @@ public class EarlyTraceEventTest { ...@@ -111,7 +111,7 @@ public class EarlyTraceEventTest {
long afterNanos = Event.elapsedRealtimeNanos(); long afterNanos = Event.elapsedRealtimeNanos();
Assert.assertEquals(1, EarlyTraceEvent.sCompletedEvents.size()); Assert.assertEquals(1, EarlyTraceEvent.sCompletedEvents.size());
Assert.assertTrue(EarlyTraceEvent.sPendingEvents.isEmpty()); Assert.assertTrue(EarlyTraceEvent.sPendingEventByKey.isEmpty());
Event event = EarlyTraceEvent.sCompletedEvents.get(0); Event event = EarlyTraceEvent.sCompletedEvents.get(0);
Assert.assertEquals(EVENT_NAME, event.mName); Assert.assertEquals(EVENT_NAME, event.mName);
Assert.assertEquals(myThreadId, event.mThreadId); Assert.assertEquals(myThreadId, event.mThreadId);
...@@ -129,15 +129,16 @@ public class EarlyTraceEventTest { ...@@ -129,15 +129,16 @@ public class EarlyTraceEventTest {
EarlyTraceEvent.begin(EVENT_NAME); EarlyTraceEvent.begin(EVENT_NAME);
Assert.assertTrue(EarlyTraceEvent.sCompletedEvents.isEmpty()); Assert.assertTrue(EarlyTraceEvent.sCompletedEvents.isEmpty());
Assert.assertEquals(1, EarlyTraceEvent.sPendingEvents.size()); Assert.assertEquals(1, EarlyTraceEvent.sPendingEventByKey.size());
EarlyTraceEvent.Event event = EarlyTraceEvent.sPendingEvents.get(EVENT_NAME); EarlyTraceEvent.Event event = EarlyTraceEvent.sPendingEventByKey.get(
EarlyTraceEvent.makeEventKeyForCurrentThread(EVENT_NAME));
Assert.assertEquals(EVENT_NAME, event.mName); Assert.assertEquals(EVENT_NAME, event.mName);
} }
@Test @Test
@SmallTest @SmallTest
@Feature({"Android-AppBase"}) @Feature({"Android-AppBase"})
public void testNoDuplicatePendingEvents() { public void testNoDuplicatePendingEventsFromSameThread() {
EarlyTraceEvent.enable(); EarlyTraceEvent.enable();
EarlyTraceEvent.begin(EVENT_NAME); EarlyTraceEvent.begin(EVENT_NAME);
try { try {
...@@ -149,6 +150,21 @@ public class EarlyTraceEventTest { ...@@ -149,6 +150,21 @@ public class EarlyTraceEventTest {
Assert.fail(); Assert.fail();
} }
@Test
@SmallTest
@Feature({"Android-AppBase"})
public void testDuplicatePendingEventsFromDifferentThreads() throws Exception {
EarlyTraceEvent.enable();
Thread otherThread = new Thread(() -> { EarlyTraceEvent.begin(EVENT_NAME); });
otherThread.start();
otherThread.join();
// At this point we have a pending event with EVENT_NAME name. But events are per
// thread, so we should be able to start EVENT_NAME event in a different thread.
EarlyTraceEvent.begin(EVENT_NAME);
}
@Test @Test
@SmallTest @SmallTest
@Feature({"Android-AppBase"}) @Feature({"Android-AppBase"})
...@@ -182,7 +198,7 @@ public class EarlyTraceEventTest { ...@@ -182,7 +198,7 @@ public class EarlyTraceEventTest {
EarlyTraceEvent.begin(EVENT_NAME2); EarlyTraceEvent.begin(EVENT_NAME2);
EarlyTraceEvent.end(EVENT_NAME2); EarlyTraceEvent.end(EVENT_NAME2);
Assert.assertEquals(1, EarlyTraceEvent.sPendingEvents.size()); Assert.assertEquals(1, EarlyTraceEvent.sPendingEventByKey.size());
Assert.assertTrue(EarlyTraceEvent.sCompletedEvents.isEmpty()); Assert.assertTrue(EarlyTraceEvent.sCompletedEvents.isEmpty());
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment