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

Commit 1d7f615e authored by Olivier Gaillard's avatar Olivier Gaillard
Browse files

Add experiment flag to control binder call stats.

For instance, to enabled detailed tracking locally.
adb shell settings put global binder_calls_stats detailed_tracking=true

Also adds the ability to turn off data collection completely and
changing the sampling interval. Uploading data through westworld can
re-use the same flag once implemented.

Test: Unit tested

Change-Id: I808c9902b8124ab643d9b197703d537da040ae3e
parent beac1471
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -45,7 +45,7 @@ public class BinderCallsStatsPerfTest {

    @Before
    public void setUp() {
        mBinderCallsStats = new BinderCallsStats(true);
        mBinderCallsStats = new BinderCallsStats();
    }

    @After
@@ -54,6 +54,7 @@ public class BinderCallsStatsPerfTest {

    @Test
    public void timeCallSession() {
        mBinderCallsStats.setDetailedTracking(true);
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        Binder b = new Binder();
        int i = 0;
@@ -66,9 +67,9 @@ public class BinderCallsStatsPerfTest {

    @Test
    public void timeCallSessionTrackingDisabled() {
        mBinderCallsStats.setDetailedTracking(false);
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        Binder b = new Binder();
        mBinderCallsStats = new BinderCallsStats(false);
        while (state.keepRunning()) {
            BinderCallsStats.CallSession s = mBinderCallsStats.callStarted(b, 0);
            mBinderCallsStats.callEnded(s, 0, 0);
+15 −0
Original line number Diff line number Diff line
@@ -13008,6 +13008,21 @@ public final class Settings {
         */
        public static final String GNSS_HAL_LOCATION_REQUEST_DURATION_MILLIS =
                "gnss_hal_location_request_duration_millis";
        /**
         * Binder call stats settings.
         *
         * The following strings are supported as keys:
         * <pre>
         *     enabled              (boolean)
         *     detailed_tracking    (boolean)
         *     upload_data          (boolean)
         *     sampling_interval    (int)
         * </pre>
         *
         * @hide
         */
        public static final String BINDER_CALLS_STATS = "binder_calls_stats";
    }
    /**
+65 −16
Original line number Diff line number Diff line
@@ -45,14 +45,21 @@ import java.util.function.ToDoubleFunction;
 * per thread, uid or call description.
 */
public class BinderCallsStats {
    public static final boolean ENABLED_DEFAULT = true;
    public static final boolean DETAILED_TRACKING_DEFAULT = true;
    public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 10;

    private static final String TAG = "BinderCallsStats";
    private static final int CALL_SESSIONS_POOL_SIZE = 100;
    private static final int PERIODIC_SAMPLING_INTERVAL = 10;
    private static final int MAX_EXCEPTION_COUNT_SIZE = 50;
    private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow";
    private static final CallSession NOT_ENABLED = new CallSession();
    private static final BinderCallsStats sInstance = new BinderCallsStats();

    private volatile boolean mDetailedTracking = false;
    private volatile boolean mEnabled = ENABLED_DEFAULT;
    private volatile boolean mDetailedTracking = DETAILED_TRACKING_DEFAULT;
    private volatile int mPeriodicSamplingInterval = PERIODIC_SAMPLING_INTERVAL_DEFAULT;
    @GuardedBy("mLock")
    private final SparseArray<UidEntry> mUidEntries = new SparseArray<>();
    @GuardedBy("mLock")
@@ -63,12 +70,8 @@ public class BinderCallsStats {
    @GuardedBy("mLock")
    private UidEntry mSampledEntries = new UidEntry(-1);

    private BinderCallsStats() {
    }

    @VisibleForTesting
    public BinderCallsStats(boolean detailedTracking) {
        mDetailedTracking = detailedTracking;
    @VisibleForTesting  // Use getInstance() instead.
    public BinderCallsStats() {
    }

    public CallSession callStarted(Binder binder, int code) {
@@ -76,10 +79,15 @@ public class BinderCallsStats {
    }

    private CallSession callStarted(String className, int code) {
        if (!mEnabled) {
          return NOT_ENABLED;
        }

        CallSession s = mCallSessionsPool.poll();
        if (s == null) {
            s = new CallSession();
        }

        s.callStat.className = className;
        s.callStat.msg = code;
        s.exceptionThrown = false;
@@ -92,7 +100,7 @@ public class BinderCallsStats {
                s.timeStarted = getElapsedRealtimeMicro();
            } else {
                s.sampledCallStat = mSampledEntries.getOrCreate(s.callStat);
                if (s.sampledCallStat.callCount % PERIODIC_SAMPLING_INTERVAL == 0) {
                if (s.sampledCallStat.callCount % mPeriodicSamplingInterval == 0) {
                    s.cpuTimeStarted = getThreadTimeMicro();
                    s.timeStarted = getElapsedRealtimeMicro();
                }
@@ -103,7 +111,23 @@ public class BinderCallsStats {

    public void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
        Preconditions.checkNotNull(s);
        if (s == NOT_ENABLED) {
          return;
        }

        processCallEnded(s, parcelRequestSize, parcelReplySize);

        if (mCallSessionsPool.size() < CALL_SESSIONS_POOL_SIZE) {
            mCallSessionsPool.add(s);
        }
    }

    private void processCallEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
        synchronized (mLock) {
            if (!mEnabled) {
              return;
            }

            long duration;
            long latencyDuration;
            if (mDetailedTracking) {
@@ -117,7 +141,7 @@ public class BinderCallsStats {
                    latencyDuration = getElapsedRealtimeMicro() - s.timeStarted;
                } else {
                    // callCount is always incremented, but time only once per sampling interval
                    long samplesCount = cs.callCount / PERIODIC_SAMPLING_INTERVAL + 1;
                    long samplesCount = cs.callCount / mPeriodicSamplingInterval + 1;
                    duration = cs.cpuTimeMicros / samplesCount;
                    latencyDuration = cs.latencyMicros / samplesCount;
                }
@@ -155,9 +179,6 @@ public class BinderCallsStats {
            uidEntry.cpuTimeMicros += duration;
            uidEntry.callCount++;
        }
        if (mCallSessionsPool.size() < CALL_SESSIONS_POOL_SIZE) {
            mCallSessionsPool.add(s);
        }
    }

    /**
@@ -169,6 +190,9 @@ public class BinderCallsStats {
     */
    public void callThrewException(CallSession s, Exception exception) {
        Preconditions.checkNotNull(s);
        if (!mEnabled) {
          return;
        }
        s.exceptionThrown = true;
        try {
            String className = exception.getClass().getName();
@@ -192,6 +216,11 @@ public class BinderCallsStats {
    }

    private void dumpLocked(PrintWriter pw, Map<Integer,String> appIdToPkgNameMap, boolean verbose) {
        if (!mEnabled) {
          pw.println("Binder calls stats disabled.");
          return;
        }

        long totalCallsCount = 0;
        long totalCpuTime = 0;
        pw.print("Start time: ");
@@ -245,7 +274,7 @@ public class BinderCallsStats {
            for (CallStat e : sampledStatsList) {
                sb.setLength(0);
                sb.append("    ").append(e)
                        .append(',').append(e.cpuTimeMicros * PERIODIC_SAMPLING_INTERVAL)
                        .append(',').append(e.cpuTimeMicros * mPeriodicSamplingInterval)
                        .append(',').append(e.callCount)
                        .append(',').append(e.exceptionCount);
                pw.println(sb);
@@ -304,9 +333,29 @@ public class BinderCallsStats {
    }

    public void setDetailedTracking(boolean enabled) {
        synchronized (mLock) {
          if (enabled != mDetailedTracking) {
            reset();
              mDetailedTracking = enabled;
              reset();
          }
        }
    }

    public void setEnabled(boolean enabled) {
        synchronized (mLock) {
            if (enabled != mEnabled) {
                mEnabled = enabled;
                reset();
            }
        }
    }

    public void setSamplingInterval(int samplingInterval) {
        synchronized (mLock) {
            if (samplingInterval != mPeriodicSamplingInterval) {
                mPeriodicSamplingInterval = samplingInterval;
                reset();
            }
        }
    }

+1 −0
Original line number Diff line number Diff line
@@ -124,6 +124,7 @@ public class SettingsBackupTest {
                    Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
                    Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
                    Settings.Global.BATTERY_STATS_CONSTANTS,
                    Settings.Global.BINDER_CALLS_STATS,
                    Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE,
                    Settings.Global.BLE_SCAN_LOW_POWER_WINDOW_MS,
                    Settings.Global.BLE_SCAN_LOW_POWER_INTERVAL_MS,
+94 −9
Original line number Diff line number Diff line
@@ -46,7 +46,9 @@ public class BinderCallsStatsTest {

    @Test
    public void testDetailedOff() {
        TestBinderCallsStats bcs = new TestBinderCallsStats(false);
        TestBinderCallsStats bcs = new TestBinderCallsStats();
        bcs.setDetailedTracking(false);

        Binder binder = new Binder();
        BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
        bcs.time += 10;
@@ -98,7 +100,9 @@ public class BinderCallsStatsTest {

    @Test
    public void testDetailedOn() {
        TestBinderCallsStats bcs = new TestBinderCallsStats(true);
        TestBinderCallsStats bcs = new TestBinderCallsStats();
        bcs.setDetailedTracking(true);

        Binder binder = new Binder();
        BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
        bcs.time += 10;
@@ -144,9 +148,87 @@ public class BinderCallsStatsTest {
        assertEquals(2, callStatsList.size());
    }

    @Test
    public void testDisabled() {
        TestBinderCallsStats bcs = new TestBinderCallsStats();
        bcs.setEnabled(false);

        Binder binder = new Binder();
        BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
        bcs.time += 10;
        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);

        SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
        assertEquals(0, uidEntries.size());
    }

    @Test
    public void testDisableInBetweenCall() {
        TestBinderCallsStats bcs = new TestBinderCallsStats();
        bcs.setEnabled(true);

        Binder binder = new Binder();
        BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
        bcs.time += 10;
        bcs.setEnabled(false);
        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);

        SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
        assertEquals(0, uidEntries.size());
    }

    @Test
    public void testEnableInBetweenCall() {
        TestBinderCallsStats bcs = new TestBinderCallsStats();
        bcs.setEnabled(false);

        Binder binder = new Binder();
        BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
        bcs.time += 10;
        bcs.setEnabled(true);
        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);

        SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
        assertEquals(0, uidEntries.size());
    }

    @Test
    public void testSampling() {
        TestBinderCallsStats bcs = new TestBinderCallsStats();
        bcs.setDetailedTracking(false);
        bcs.setSamplingInterval(2);

        Binder binder = new Binder();
        BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
        bcs.time += 10;
        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);

        callSession = bcs.callStarted(binder, 1);
        bcs.time += 1000;  // shoud be ignored.
        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);

        callSession = bcs.callStarted(binder, 1);
        bcs.time += 50;
        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);

        SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
        assertEquals(1, uidEntries.size());
        BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
        Assert.assertNotNull(uidEntry);
        assertEquals(3, uidEntry.callCount);
        assertEquals(70, uidEntry.cpuTimeMicros);
        assertEquals("Detailed tracking off - no entries should be returned",
                0, uidEntry.getCallStatsList().size());

        BinderCallsStats.UidEntry sampledEntries = bcs.getSampledEntries();
        List<BinderCallsStats.CallStat> sampledCallStatsList = sampledEntries.getCallStatsList();
        assertEquals(1, sampledCallStatsList.size());
    }

    @Test
    public void testParcelSize() {
        TestBinderCallsStats bcs = new TestBinderCallsStats(true);
        TestBinderCallsStats bcs = new TestBinderCallsStats();
        bcs.setDetailedTracking(true);
        Binder binder = new Binder();
        BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
        bcs.time += 10;
@@ -161,7 +243,8 @@ public class BinderCallsStatsTest {

    @Test
    public void testMaxCpu() {
        TestBinderCallsStats bcs = new TestBinderCallsStats(true);
        TestBinderCallsStats bcs = new TestBinderCallsStats();
        bcs.setDetailedTracking(true);
        Binder binder = new Binder();
        BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
        bcs.time += 50;
@@ -179,7 +262,8 @@ public class BinderCallsStatsTest {

    @Test
    public void testMaxLatency() {
        TestBinderCallsStats bcs = new TestBinderCallsStats(true);
        TestBinderCallsStats bcs = new TestBinderCallsStats();
        bcs.setDetailedTracking(true);
        Binder binder = new Binder();
        BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
        bcs.elapsedTime += 5;
@@ -205,7 +289,8 @@ public class BinderCallsStatsTest {

    @Test
    public void testExceptionCount() {
        TestBinderCallsStats bcs = new TestBinderCallsStats(true);
        TestBinderCallsStats bcs = new TestBinderCallsStats();
        bcs.setDetailedTracking(true);
        Binder binder = new Binder();
        BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
        bcs.callThrewException(callSession, new IllegalStateException());
@@ -227,7 +312,8 @@ public class BinderCallsStatsTest {

    @Test
    public void testDumpDoesNotThrowException() {
        TestBinderCallsStats bcs = new TestBinderCallsStats(true);
        TestBinderCallsStats bcs = new TestBinderCallsStats();
        bcs.setDetailedTracking(true);
        Binder binder = new Binder();
        BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
        bcs.callThrewException(callSession, new IllegalStateException());
@@ -242,8 +328,7 @@ public class BinderCallsStatsTest {
        long time = 1234;
        long elapsedTime = 0;

        TestBinderCallsStats(boolean detailedTracking) {
            super(detailedTracking);
        TestBinderCallsStats() {
        }

        @Override
Loading