diff options
| author | 2018-11-30 16:22:23 +0000 | |
|---|---|---|
| committer | 2018-12-03 11:12:39 +0000 | |
| commit | 7949061843fe9fe998fe15993d1b70da844bf17b (patch) | |
| tree | 7f6250190b6db22d5816fc2978fa1eaf90fdbc7f | |
| parent | 2648555677544c8f76a8bbaec5e0e93943a24919 (diff) | |
Adds an overflow mechanism for binder calls.
This logic will ensure that we have a limit for the number of items we
track to make sure we do not use too much memory.
We still have an overflow per uid in order to properly attribute the cpu
usage to the uids.
Test: atest BinderCallsStatsTest
Change-Id: Ife9f7249bae35d5c61a6d35ac9d25437d213e959
3 files changed, 106 insertions, 3 deletions
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index 34e8ed406200..875d7c9ee7a6 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -52,17 +52,23 @@ public class BinderCallsStats implements BinderInternal.Observer { public static final boolean ENABLED_DEFAULT = false; public static final boolean DETAILED_TRACKING_DEFAULT = true; public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 100; + public static final int MAX_BINDER_CALL_STATS_COUNT_DEFAULT = 5000; + + private static class OverflowBinder extends Binder {} private static final String TAG = "BinderCallsStats"; private static final int CALL_SESSIONS_POOL_SIZE = 100; private static final int MAX_EXCEPTION_COUNT_SIZE = 50; private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow"; + private static final Class<? extends Binder> OVERFLOW_BINDER = OverflowBinder.class; + private static final int OVERFLOW_TRANSACTION_CODE = -1; // 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; + private int mMaxBinderCallStatsCount = MAX_BINDER_CALL_STATS_COUNT_DEFAULT; @GuardedBy("mLock") private final SparseArray<UidEntry> mUidEntries = new SparseArray<>(); @GuardedBy("mLock") @@ -71,6 +77,7 @@ public class BinderCallsStats implements BinderInternal.Observer { private final Object mLock = new Object(); private final Random mRandom; private long mStartTime = System.currentTimeMillis(); + private long mCallStatsCount = 0; private CachedDeviceState.Readonly mDeviceState; @@ -158,7 +165,13 @@ public class BinderCallsStats implements BinderInternal.Observer { final CallStat callStat = uidEntry.getOrCreate( callingUid, s.binderClass, s.transactionCode, - mDeviceState.isScreenInteractive()); + mDeviceState.isScreenInteractive(), + mCallStatsCount >= mMaxBinderCallStatsCount); + final boolean isNewCallStat = callStat.callCount == 0; + if (isNewCallStat) { + mCallStatsCount++; + } + callStat.callCount++; callStat.recordedCallCount++; callStat.cpuTimeMicros += duration; @@ -444,6 +457,24 @@ public class BinderCallsStats implements BinderInternal.Observer { } } + /** + * Sets the maximum number of items to track. + */ + public void setMaxBinderCallStats(int maxKeys) { + if (maxKeys <= 0) { + Slog.w(TAG, "Ignored invalid max value (value must be positive): " + + maxKeys); + return; + } + + synchronized (mLock) { + if (maxKeys != mMaxBinderCallStatsCount) { + mMaxBinderCallStatsCount = maxKeys; + reset(); + } + } + } + public void setSamplingInterval(int samplingInterval) { if (samplingInterval <= 0) { Slog.w(TAG, "Ignored invalid sampling interval (value must be positive): " @@ -461,6 +492,7 @@ public class BinderCallsStats implements BinderInternal.Observer { public void reset() { synchronized (mLock) { + mCallStatsCount = 0; mUidEntries.clear(); mExceptionCounts.clear(); mStartTime = System.currentTimeMillis(); @@ -595,10 +627,21 @@ public class BinderCallsStats implements BinderInternal.Observer { } CallStat getOrCreate(int callingUid, Class<? extends Binder> binderClass, - int transactionCode, boolean screenInteractive) { + int transactionCode, boolean screenInteractive, boolean maxCallStatsReached) { CallStat mapCallStat = get(callingUid, binderClass, transactionCode, screenInteractive); - // Only create CallStat if it's a new entry, otherwise update existing instance + // Only create CallStat if it's a new entry, otherwise update existing instance. if (mapCallStat == null) { + if (maxCallStatsReached) { + mapCallStat = get(callingUid, OVERFLOW_BINDER, OVERFLOW_TRANSACTION_CODE, + screenInteractive); + if (mapCallStat != null) { + return mapCallStat; + } + + binderClass = OVERFLOW_BINDER; + transactionCode = OVERFLOW_TRANSACTION_CODE; + } + mapCallStat = new CallStat(callingUid, binderClass, transactionCode, screenInteractive); CallStatKey key = new CallStatKey(); diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java index 30309cf64e96..8691e73f82fb 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java @@ -557,6 +557,62 @@ public class BinderCallsStatsTest { assertEquals(0, bcs.getExceptionCounts().size()); } + @Test + public void testOverflow_sameEntry() { + TestBinderCallsStats bcs = new TestBinderCallsStats(); + bcs.setDetailedTracking(true); + bcs.setSamplingInterval(1); + bcs.setMaxBinderCallStats(2); + + Binder binder = new Binder(); + CallSession callSession = bcs.callStarted(binder, 1); + bcs.time += 10; + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); + + callSession = bcs.callStarted(binder, 1); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); + + callSession = bcs.callStarted(binder, 1); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); + + BinderCallsStats.UidEntry uidEntry = bcs.getUidEntries().get(WORKSOURCE_UID); + List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList()); + assertEquals(1, callStatsList.size()); + BinderCallsStats.CallStat callStats = callStatsList.get(0); + assertEquals(3, callStats.callCount); + } + + @Test + public void testOverflow_overflowEntry() { + TestBinderCallsStats bcs = new TestBinderCallsStats(); + bcs.setDetailedTracking(true); + bcs.setSamplingInterval(1); + bcs.setMaxBinderCallStats(1); + + Binder binder = new Binder(); + CallSession callSession = bcs.callStarted(binder, 1); + bcs.time += 10; + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); + + callSession = bcs.callStarted(binder, 2); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); + + List<BinderCallsStats.ExportedCallStat> callStatsList = bcs.getExportedCallStats(); + assertEquals(2, callStatsList.size()); + BinderCallsStats.ExportedCallStat callStats = callStatsList.get(0); + assertEquals(1, callStats.callCount); + assertEquals("1", callStats.methodName); + assertEquals("android.os.Binder", callStats.className); + assertEquals(CALLING_UID, callStats.callingUid); + + callStats = callStatsList.get(1); + assertEquals(1, callStats.callCount); + assertEquals("-1", callStats.methodName); + assertEquals("com.android.internal.os.BinderCallsStats$OverflowBinder", + callStats.className); + assertEquals(CALLING_UID, callStats.callingUid); + } + class TestBinderCallsStats extends BinderCallsStats { public int callingUid = CALLING_UID; public int workSourceUid = WORKSOURCE_UID; diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java index dd960751ab21..98203213e996 100644 --- a/services/core/java/com/android/server/BinderCallsStatsService.java +++ b/services/core/java/com/android/server/BinderCallsStatsService.java @@ -56,6 +56,7 @@ public class BinderCallsStatsService extends Binder { private static final String SETTINGS_DETAILED_TRACKING_KEY = "detailed_tracking"; private static final String SETTINGS_UPLOAD_DATA_KEY = "upload_data"; private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval"; + private static final String SETTINGS_MAX_CALL_STATS_KEY = "max_call_stats_count"; private boolean mEnabled; private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS); @@ -97,6 +98,9 @@ public class BinderCallsStatsService extends Binder { mBinderCallsStats.setSamplingInterval(mParser.getInt( SETTINGS_SAMPLING_INTERVAL_KEY, BinderCallsStats.PERIODIC_SAMPLING_INTERVAL_DEFAULT)); + mBinderCallsStats.setSamplingInterval(mParser.getInt( + SETTINGS_MAX_CALL_STATS_KEY, + BinderCallsStats.MAX_BINDER_CALL_STATS_COUNT_DEFAULT)); final boolean enabled = |