diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java index b8e180685dda7..e4724ff45b9e9 100644 --- a/core/java/com/android/internal/os/LooperStats.java +++ b/core/java/com/android/internal/os/LooperStats.java @@ -17,6 +17,7 @@ package com.android.internal.os; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -36,7 +37,7 @@ import java.util.concurrent.ThreadLocalRandom; * @hide Only for use within the system server. */ public class LooperStats implements Looper.Observer { - private static final int TOKEN_POOL_SIZE = 50; + private static final int SESSION_POOL_SIZE = 50; @GuardedBy("mLock") private final SparseArray mEntries = new SparseArray<>(512); @@ -78,17 +79,19 @@ public class LooperStats implements Looper.Observer { } DispatchSession session = (DispatchSession) token; - Entry entry = getOrCreateEntry(msg); - synchronized (entry) { - entry.messageCount++; - if (session != DispatchSession.NOT_SAMPLED) { - entry.recordedMessageCount++; - long latency = getElapsedRealtimeMicro() - session.startTimeMicro; - long cpuUsage = getThreadTimeMicro() - session.cpuStartMicro; - entry.totalLatencyMicro += latency; - entry.maxLatencyMicro = Math.max(entry.maxLatencyMicro, latency); - entry.cpuUsageMicro += cpuUsage; - entry.maxCpuUsageMicro = Math.max(entry.maxCpuUsageMicro, cpuUsage); + Entry entry = findEntry(msg, /* allowCreateNew= */session != DispatchSession.NOT_SAMPLED); + if (entry != null) { + synchronized (entry) { + entry.messageCount++; + if (session != DispatchSession.NOT_SAMPLED) { + entry.recordedMessageCount++; + long latency = getElapsedRealtimeMicro() - session.startTimeMicro; + long cpuUsage = getThreadTimeMicro() - session.cpuStartMicro; + entry.totalLatencyMicro += latency; + entry.maxLatencyMicro = Math.max(entry.maxLatencyMicro, latency); + entry.cpuUsageMicro += cpuUsage; + entry.maxCpuUsageMicro = Math.max(entry.maxCpuUsageMicro, cpuUsage); + } } } @@ -102,7 +105,7 @@ public class LooperStats implements Looper.Observer { } DispatchSession session = (DispatchSession) token; - Entry entry = getOrCreateEntry(msg); + Entry entry = findEntry(msg, /* allowCreateNew= */true); synchronized (entry) { entry.exceptionCount++; } @@ -159,20 +162,23 @@ public class LooperStats implements Looper.Observer { mSamplingInterval = samplingInterval; } - @NonNull - private Entry getOrCreateEntry(Message msg) { + @Nullable + private Entry findEntry(Message msg, boolean allowCreateNew) { final boolean isInteractive = mDeviceState.isScreenInteractive(); final int id = Entry.idFor(msg, isInteractive); Entry entry; synchronized (mLock) { entry = mEntries.get(id); if (entry == null) { - if (mEntries.size() >= mEntriesSizeCap) { - // If over the size cap, track totals under a single entry. + if (!allowCreateNew) { + return null; + } else if (mEntries.size() >= mEntriesSizeCap) { + // If over the size cap track totals under OVERFLOW entry. return mOverflowEntry; + } else { + entry = new Entry(msg, isInteractive); + mEntries.put(id, entry); } - entry = new Entry(msg, isInteractive); - mEntries.put(id, entry); } } @@ -187,7 +193,7 @@ public class LooperStats implements Looper.Observer { } private void recycleSession(DispatchSession session) { - if (session != DispatchSession.NOT_SAMPLED && mSessionPool.size() < TOKEN_POOL_SIZE) { + if (session != DispatchSession.NOT_SAMPLED && mSessionPool.size() < SESSION_POOL_SIZE) { mSessionPool.add(session); } } diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java index 1008a90406b75..0c8dd9d6ed595 100644 --- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java @@ -152,29 +152,35 @@ public final class LooperStatsTest { looperStats.messageDispatched(token3, mHandlerSecond.obtainMessage().setCallback(() -> { })); - // Contributes to entry1. + // Will not be sampled so does not contribute to any entries. Object token4 = looperStats.messageDispatchStarting(); + looperStats.tickRealtime(10); + looperStats.tickThreadTime(10); + looperStats.messageDispatched(token4, mHandlerSecond.obtainMessage(0)); + + // Contributes to entry1. + Object token5 = looperStats.messageDispatchStarting(); looperStats.tickRealtime(100); looperStats.tickThreadTime(100); - looperStats.messageDispatched(token4, mHandlerAnonymous.obtainMessage(1)); + looperStats.messageDispatched(token5, mHandlerAnonymous.obtainMessage(1)); List entries = looperStats.getEntries(); assertThat(entries).hasSize(3); entries.sort(Comparator.comparing(e -> e.handlerClassName)); - // Captures data for token4 call. + // Captures data for token5 call. LooperStats.ExportedEntry entry1 = entries.get(0); assertThat(entry1.workSourceUid).isEqualTo(-1); assertThat(entry1.threadName).isEqualTo("TestThread1"); assertThat(entry1.handlerClassName).isEqualTo("com.android.internal.os.LooperStatsTest$1"); assertThat(entry1.messageName).isEqualTo("0x1" /* 1 in hex */); assertThat(entry1.messageCount).isEqualTo(1); - assertThat(entry1.recordedMessageCount).isEqualTo(0); + assertThat(entry1.recordedMessageCount).isEqualTo(1); assertThat(entry1.exceptionCount).isEqualTo(0); - assertThat(entry1.totalLatencyMicros).isEqualTo(0); - assertThat(entry1.maxLatencyMicros).isEqualTo(0); - assertThat(entry1.cpuUsageMicros).isEqualTo(0); - assertThat(entry1.maxCpuUsageMicros).isEqualTo(0); + assertThat(entry1.totalLatencyMicros).isEqualTo(100); + assertThat(entry1.maxLatencyMicros).isEqualTo(100); + assertThat(entry1.cpuUsageMicros).isEqualTo(100); + assertThat(entry1.maxCpuUsageMicros).isEqualTo(100); // Captures data for token1 and token2 calls. LooperStats.ExportedEntry entry2 = entries.get(1); @@ -274,7 +280,7 @@ public final class LooperStatsTest { @Test public void testMessagesOverSizeCap() { - TestableLooperStats looperStats = new TestableLooperStats(2, 1 /* sizeCap */); + TestableLooperStats looperStats = new TestableLooperStats(1, 1 /* sizeCap */); Object token1 = looperStats.messageDispatchStarting(); looperStats.tickRealtime(100); @@ -305,12 +311,12 @@ public final class LooperStatsTest { assertThat(entry1.handlerClassName).isEqualTo(""); assertThat(entry1.messageName).isEqualTo("OVERFLOW"); assertThat(entry1.messageCount).isEqualTo(3); - assertThat(entry1.recordedMessageCount).isEqualTo(1); + assertThat(entry1.recordedMessageCount).isEqualTo(3); assertThat(entry1.exceptionCount).isEqualTo(0); - assertThat(entry1.totalLatencyMicros).isEqualTo(10); - assertThat(entry1.maxLatencyMicros).isEqualTo(10); - assertThat(entry1.cpuUsageMicros).isEqualTo(10); - assertThat(entry1.maxCpuUsageMicros).isEqualTo(10); + assertThat(entry1.totalLatencyMicros).isEqualTo(70); + assertThat(entry1.maxLatencyMicros).isEqualTo(50); + assertThat(entry1.cpuUsageMicros).isEqualTo(40); + assertThat(entry1.maxCpuUsageMicros).isEqualTo(20); LooperStats.ExportedEntry entry2 = entries.get(1); assertThat(entry2.threadName).isEqualTo("TestThread1");