LooperStats - don't create entries for unsampled sessions
This should reduce the size of the data, because we will not track the very rare calls unless we are collecting a detailed sample. Test: Manual and UTs Change-Id: If217a9a848935878a6efcdfac9df723c6ce54361
This commit is contained in:
@@ -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<Entry> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<LooperStats.ExportedEntry> 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");
|
||||
|
||||
Reference in New Issue
Block a user