diff options
3 files changed, 107 insertions, 5 deletions
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index 57e1bf79622c..6f911cbd6121 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -66,6 +66,7 @@ public class BinderCallsStats implements BinderInternal.Observer { public static final boolean DEFAULT_IGNORE_BATTERY_STATUS = false; public static final boolean DEFAULT_COLLECT_LATENCY_DATA = true; public static final int MAX_BINDER_CALL_STATS_COUNT_DEFAULT = 1500; + public static final int SHARDING_MODULO_DEFAULT = 1; private static final String DEBUG_ENTRY_PREFIX = "__DEBUG_"; private static class OverflowBinder extends Binder {} @@ -107,6 +108,12 @@ public class BinderCallsStats implements BinderInternal.Observer { private boolean mIgnoreBatteryStatus = DEFAULT_IGNORE_BATTERY_STATUS; private boolean mCollectLatencyData = DEFAULT_COLLECT_LATENCY_DATA; + // Controls how many APIs will be collected per device. 1 means all APIs, 10 means every 10th + // API will be collected. + private int mShardingModulo = SHARDING_MODULO_DEFAULT; + // Controls which shards will be collected on this device. + private int mShardingOffset; + private CachedDeviceState.Readonly mDeviceState; private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch; @@ -178,6 +185,7 @@ public class BinderCallsStats implements BinderInternal.Observer { this.mRandom = injector.getRandomGenerator(); this.mCallStatsObserverHandler = injector.getHandler(); this.mLatencyObserver = injector.getLatencyObserver(processSource); + this.mShardingOffset = mRandom.nextInt(mShardingModulo); } public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) { @@ -343,6 +351,19 @@ public class BinderCallsStats implements BinderInternal.Observer { } } + private boolean shouldExport(ExportedCallStat e, boolean applySharding) { + if (!applySharding) { + return true; + } + + int hash = e.binderClass.hashCode(); + hash = 31 * hash + e.transactionCode; + hash = 31 * hash + e.callingUid; + hash = 31 * hash + (e.screenInteractive ? 1231 : 1237); + + return (hash + mShardingOffset) % mShardingModulo == 0; + } + private UidEntry getUidEntry(int uid) { UidEntry uidEntry = mUidEntries.get(uid); if (uidEntry == null) { @@ -424,6 +445,15 @@ public class BinderCallsStats implements BinderInternal.Observer { * This method is expensive to call. */ public ArrayList<ExportedCallStat> getExportedCallStats() { + return getExportedCallStats(false); + } + + /** + * This method is expensive to call. + * Exports call stats and applies sharding if requested. + */ + @VisibleForTesting + public ArrayList<ExportedCallStat> getExportedCallStats(boolean applySharding) { // We do not collect all the data if detailed tracking is off. if (!mDetailedTracking) { return new ArrayList<>(); @@ -435,7 +465,10 @@ public class BinderCallsStats implements BinderInternal.Observer { for (int entryIdx = 0; entryIdx < uidEntriesSize; entryIdx++) { final UidEntry entry = mUidEntries.valueAt(entryIdx); for (CallStat stat : entry.getCallStatsList()) { - resultCallStats.add(getExportedCallStat(entry.workSourceUid, stat)); + ExportedCallStat e = getExportedCallStat(entry.workSourceUid, stat); + if (shouldExport(e, applySharding)) { + resultCallStats.add(e); + } } } } @@ -450,6 +483,7 @@ public class BinderCallsStats implements BinderInternal.Observer { resultCallStats.add( createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis())); resultCallStats.add(createDebugEntry("sampling_interval", mPeriodicSamplingInterval)); + resultCallStats.add(createDebugEntry("sharding_modulo", mShardingModulo)); } return resultCallStats; @@ -459,11 +493,24 @@ public class BinderCallsStats implements BinderInternal.Observer { * This method is expensive to call. */ public ArrayList<ExportedCallStat> getExportedCallStats(int workSourceUid) { + return getExportedCallStats(workSourceUid, false); + } + + /** + * This method is expensive to call. + * Exports call stats and applies sharding if requested. + */ + @VisibleForTesting + public ArrayList<ExportedCallStat> getExportedCallStats( + int workSourceUid, boolean applySharding) { ArrayList<ExportedCallStat> resultCallStats = new ArrayList<>(); synchronized (mLock) { final UidEntry entry = getUidEntry(workSourceUid); for (CallStat stat : entry.getCallStatsList()) { - resultCallStats.add(getExportedCallStat(workSourceUid, stat)); + ExportedCallStat e = getExportedCallStat(workSourceUid, stat); + if (shouldExport(e, applySharding)) { + resultCallStats.add(e); + } } } @@ -555,6 +602,7 @@ public class BinderCallsStats implements BinderInternal.Observer { pw.print("On battery time (ms): "); pw.println(mBatteryStopwatch != null ? mBatteryStopwatch.getMillis() : 0); pw.println("Sampling interval period: " + mPeriodicSamplingInterval); + pw.println("Sharding modulo: " + mShardingModulo); final String datasetSizeDesc = verbose ? "" : "(top 90% by cpu time) "; final StringBuilder sb = new StringBuilder(); @@ -566,9 +614,9 @@ public class BinderCallsStats implements BinderInternal.Observer { + "call_count):"); final List<ExportedCallStat> exportedCallStats; if (workSourceUid != Process.INVALID_UID) { - exportedCallStats = getExportedCallStats(workSourceUid); + exportedCallStats = getExportedCallStats(workSourceUid, true); } else { - exportedCallStats = getExportedCallStats(); + exportedCallStats = getExportedCallStats(true); } exportedCallStats.sort(BinderCallsStats::compareByCpuDesc); for (ExportedCallStat e : exportedCallStats) { @@ -784,6 +832,23 @@ public class BinderCallsStats implements BinderInternal.Observer { } } + /** Updates the sharding modulo. */ + public void setShardingModulo(int shardingModulo) { + if (shardingModulo <= 0) { + Slog.w(TAG, "Ignored invalid sharding modulo (value must be positive): " + + shardingModulo); + return; + } + + synchronized (mLock) { + if (shardingModulo != mShardingModulo) { + mShardingModulo = shardingModulo; + mShardingOffset = mRandom.nextInt(shardingModulo); + reset(); + } + } + } + /** Whether to collect latency histograms. */ public void setCollectLatencyData(boolean collectLatencyData) { mCollectLatencyData = collectLatencyData; @@ -1104,6 +1169,7 @@ public class BinderCallsStats implements BinderInternal.Observer { public static final String SETTINGS_TRACK_DIRECT_CALLING_UID_KEY = "track_calling_uid"; public static final String SETTINGS_MAX_CALL_STATS_KEY = "max_call_stats_count"; public static final String SETTINGS_IGNORE_BATTERY_STATUS_KEY = "ignore_battery_status"; + public static final String SETTINGS_SHARDING_MODULO_KEY = "sharding_modulo"; // Settings for BinderLatencyObserver. public static final String SETTINGS_COLLECT_LATENCY_DATA_KEY = "collect_latency_data"; public static final String SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY = 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 e2e672ea6f68..55943a0dc319 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java @@ -241,6 +241,34 @@ public class BinderCallsStatsTest { assertEquals(10, callStats.maxCpuTimeMicros); } + @Test + public void testSharding() { + TestBinderCallsStats bcs = new TestBinderCallsStats(); + bcs.setShardingModulo(2); + + Binder binder = new Binder(); + CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID); + bcs.time += 10; + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); + + callSession = bcs.callStarted(binder, 2 /* another method */, WORKSOURCE_UID); + bcs.time += 10; + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); + + + SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries(); + assertEquals(1, uidEntries.size()); + BinderCallsStats.UidEntry uidEntry = uidEntries.get(WORKSOURCE_UID); + assertEquals(2, uidEntry.callCount); + assertEquals(2, uidEntry.recordedCallCount); + + List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList()); + assertEquals(2, callStatsList.size()); + + assertEquals(1, bcs.getExportedCallStats(true).size()); + assertEquals(2, bcs.getExportedCallStats(false).size()); + } + private static class BinderWithGetTransactionName extends Binder { public static String getDefaultTransactionName(int code) { return "resolved"; @@ -669,7 +697,7 @@ public class BinderCallsStatsTest { bcs.setAddDebugEntries(true); bcs.setSamplingInterval(10); ArrayList<BinderCallsStats.ExportedCallStat> callStats = bcs.getExportedCallStats(); - assertEquals(4, callStats.size()); + assertEquals(5, callStats.size()); BinderCallsStats.ExportedCallStat debugEntry1 = callStats.get(0); assertEquals("", debugEntry1.className); assertEquals("__DEBUG_start_time_millis", debugEntry1.methodName); @@ -685,6 +713,9 @@ public class BinderCallsStatsTest { BinderCallsStats.ExportedCallStat debugEntry4 = callStats.get(3); assertEquals("__DEBUG_sampling_interval", debugEntry4.methodName); assertEquals(10, debugEntry4.latencyMicros); + BinderCallsStats.ExportedCallStat debugEntry5 = callStats.get(4); + assertEquals("__DEBUG_sharding_modulo", debugEntry5.methodName); + assertEquals(1, debugEntry5.latencyMicros); } @Test diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java index 0bd331b737c6..b87184aa5582 100644 --- a/services/core/java/com/android/server/BinderCallsStatsService.java +++ b/services/core/java/com/android/server/BinderCallsStatsService.java @@ -25,6 +25,7 @@ import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_IGNORE_BATTERY_STATUS_KEY; import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_MAX_CALL_STATS_KEY; import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_SAMPLING_INTERVAL_KEY; +import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_SHARDING_MODULO_KEY; import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_TRACK_DIRECT_CALLING_UID_KEY; import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_TRACK_SCREEN_INTERACTIVE_KEY; @@ -188,6 +189,10 @@ public class BinderCallsStatsService extends Binder { mBinderCallsStats.setIgnoreBatteryStatus( mParser.getBoolean(SETTINGS_IGNORE_BATTERY_STATUS_KEY, BinderCallsStats.DEFAULT_IGNORE_BATTERY_STATUS)); + mBinderCallsStats.setShardingModulo(mParser.getInt( + SETTINGS_SHARDING_MODULO_KEY, + BinderCallsStats.SHARDING_MODULO_DEFAULT)); + mBinderCallsStats.setCollectLatencyData( mParser.getBoolean(SETTINGS_COLLECT_LATENCY_DATA_KEY, BinderCallsStats.DEFAULT_COLLECT_LATENCY_DATA)); |