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:
Marcin Oczeretko
2018-09-13 18:20:21 +01:00
parent ec7587283f
commit a674bc9eaf
2 changed files with 46 additions and 34 deletions

View File

@@ -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);
}
}

View File

@@ -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");