Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a674bc9e authored by Marcin Oczeretko's avatar Marcin Oczeretko
Browse files

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
parent ec758728
Loading
Loading
Loading
Loading
+26 −20
Original line number Diff line number Diff line
@@ -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,7 +79,8 @@ public class LooperStats implements Looper.Observer {
        }

        DispatchSession session = (DispatchSession) token;
        Entry entry = getOrCreateEntry(msg);
        Entry entry = findEntry(msg, /* allowCreateNew= */session != DispatchSession.NOT_SAMPLED);
        if (entry != null) {
            synchronized (entry) {
                entry.messageCount++;
                if (session != DispatchSession.NOT_SAMPLED) {
@@ -91,6 +93,7 @@ public class LooperStats implements Looper.Observer {
                    entry.maxCpuUsageMicro = Math.max(entry.maxCpuUsageMicro, cpuUsage);
                }
            }
        }

        recycleSession(session);
    }
@@ -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,22 +162,25 @@ 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);
                }
            }
        }

        if (entry.workSourceUid != msg.workSourceUid
                || entry.handler.getClass() != msg.getTarget().getClass()
@@ -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);
        }
    }
+20 −14
Original line number Diff line number Diff line
@@ -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");