summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/com/android/internal/os/BinderCallsStats.java74
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java33
-rw-r--r--services/core/java/com/android/server/BinderCallsStatsService.java5
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));