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

Commit 34ad8af0 authored by Olivier Gaillard's avatar Olivier Gaillard
Browse files

Improve binder calls stats sampling.

- Decouple detailed tracking and sampling. Detailed tracking now control
which data wwe should track (e.g. exception, max reply size, ...),
sampling controls the % of requests to track
- Do not create a CallStat object just for the sake of tracking the call
count. Most of the keys have a really low call count (60% of keys below
10 call counts, 90% below 100 calls). When data sampling is enabled, we
can save lots of memory.

Test: unit test
Change-Id: If81b38023a76a8b1e07e655f9ecd8a098d401df4
parent c0eadf99
Loading
Loading
Loading
Loading
+29 −8
Original line number Diff line number Diff line
@@ -67,7 +67,10 @@ public class BinderCallsStats implements BinderInternal.Observer {
    private static final int MAX_EXCEPTION_COUNT_SIZE = 50;
    private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow";

    // Whether to collect all the data: cpu + exceptions + reply/request sizes.
    private boolean mDetailedTracking = DETAILED_TRACKING_DEFAULT;
    // Sampling period to control how often to track CPU usage. 1 means all calls, 100 means ~1 out
    // of 100 requests.
    private int mPeriodicSamplingInterval = PERIODIC_SAMPLING_INTERVAL_DEFAULT;
    @GuardedBy("mLock")
    private final SparseArray<UidEntry> mUidEntries = new SparseArray<>();
@@ -177,7 +180,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
        s.exceptionThrown = false;
        s.cpuTimeStarted = -1;
        s.timeStarted = -1;
        if (mDetailedTracking || shouldRecordDetailedData()) {
        if (shouldRecordDetailedData()) {
            s.cpuTimeStarted = getThreadTimeMicro();
            s.timeStarted = getElapsedRealtimeMicro();
        }
@@ -224,14 +227,14 @@ public class BinderCallsStats implements BinderInternal.Observer {

            final UidEntry uidEntry = getUidEntry(callingUid);
            uidEntry.callCount++;
            final CallStat callStat = uidEntry.getOrCreate(
                    s.binderClass, s.transactionCode, mScreenInteractive);
            callStat.callCount++;

            if (recordCall) {
                uidEntry.cpuTimeMicros += duration;
                uidEntry.recordedCallCount++;

                final CallStat callStat = uidEntry.getOrCreate(
                        s.binderClass, s.transactionCode, mScreenInteractive);
                callStat.callCount++;
                callStat.recordedCallCount++;
                callStat.cpuTimeMicros += duration;
                callStat.maxCpuTimeMicros = Math.max(callStat.maxCpuTimeMicros, duration);
@@ -245,6 +248,14 @@ public class BinderCallsStats implements BinderInternal.Observer {
                    callStat.maxReplySizeBytes =
                            Math.max(callStat.maxReplySizeBytes, parcelReplySize);
                }
            } else {
                // Only record the total call count if we already track data for this key.
                // It helps to keep the memory usage down when sampling is enabled.
                final CallStat callStat = uidEntry.get(
                        s.binderClass, s.transactionCode, mScreenInteractive);
                if (callStat != null) {
                    callStat.callCount++;
                }
            }
        }
    }
@@ -459,7 +470,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
            pw.println(String.format("  %6d %s", entry.second, entry.first));
        }

        if (!mDetailedTracking && mPeriodicSamplingInterval != 1) {
        if (mPeriodicSamplingInterval != 1) {
            pw.println("");
            pw.println("/!\\ Displayed data is sampled. See sampling interval at the top.");
        }
@@ -488,6 +499,9 @@ public class BinderCallsStats implements BinderInternal.Observer {
        return mRandom.nextInt() % mPeriodicSamplingInterval == 0;
    }

    /**
     * Sets to true to collect all the data.
     */
    public void setDetailedTracking(boolean enabled) {
        synchronized (mLock) {
            if (enabled != mDetailedTracking) {
@@ -546,7 +560,8 @@ public class BinderCallsStats implements BinderInternal.Observer {
        // Number of calls for which we collected data for. We do not record data for all the calls
        // when sampling is on.
        public long recordedCallCount;
        // Real number of total calls.
        // Roughly the real number of total calls. We only track only track the API call count once
        // at least one non-sampled count happened.
        public long callCount;
        // Total CPU of all for all the recorded calls.
        // Approximate total CPU usage can be computed by
@@ -620,13 +635,19 @@ public class BinderCallsStats implements BinderInternal.Observer {
        private Map<CallStatKey, CallStat> mCallStats = new ArrayMap<>();
        private CallStatKey mTempKey = new CallStatKey();

        CallStat getOrCreate(Class<? extends Binder> binderClass, int transactionCode,
        @Nullable
        CallStat get(Class<? extends Binder> binderClass, int transactionCode,
                boolean screenInteractive) {
            // Use a global temporary key to avoid creating new objects for every lookup.
            mTempKey.binderClass = binderClass;
            mTempKey.transactionCode = transactionCode;
            mTempKey.screenInteractive = screenInteractive;
            CallStat mapCallStat = mCallStats.get(mTempKey);
            return mCallStats.get(mTempKey);
        }

        CallStat getOrCreate(Class<? extends Binder> binderClass, int transactionCode,
                boolean screenInteractive) {
            CallStat mapCallStat = get(binderClass, transactionCode, screenInteractive);
            // Only create CallStat if it's a new entry, otherwise update existing instance
            if (mapCallStat == null) {
                mapCallStat = new CallStat(binderClass, transactionCode, screenInteractive);
+3 −9
Original line number Diff line number Diff line
@@ -90,7 +90,7 @@ public class BinderCallsStatsTest {
        assertEquals(1, uidEntry.recordedCallCount);
        // Still sampled even for another API.
        callStatsList = new ArrayList(uidEntry.getCallStatsList());
        assertEquals(2, callStatsList.size());
        assertEquals(1, callStatsList.size());
    }

    @Test
@@ -221,20 +221,13 @@ public class BinderCallsStatsTest {
        assertEquals(10, uidEntry.cpuTimeMicros);

        List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
        assertEquals(2, callStatsList.size());
        assertEquals(1, callStatsList.size());

        BinderCallsStats.CallStat callStats = callStatsList.get(0);
        assertEquals(1, callStats.callCount);
        assertEquals(1, callStats.recordedCallCount);
        assertEquals(10, callStats.cpuTimeMicros);
        assertEquals(10, callStats.maxCpuTimeMicros);

        // Only call count should is tracked, rest is sampled.
        callStats = callStatsList.get(1);
        assertEquals(1, callStats.callCount);
        assertEquals(0, callStats.recordedCallCount);
        assertEquals(0, callStats.cpuTimeMicros);
        assertEquals(0, callStats.maxCpuTimeMicros);
    }

    private static class BinderWithGetTransactionName extends Binder {
@@ -586,6 +579,7 @@ public class BinderCallsStatsTest {
                    };
                }
            });
            setSamplingInterval(1);
        }

        @Override