Merge "Add sharding to BinderCallsStats collection and make it configurable. The hash code of the callsession will be used to select which apis will be collected for based on the configured modulo. This is needed to reduce the amount of data collected from public devices." into sc-dev am: d49a40998a

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14440248

Change-Id: I680a1342507c882d988f486600da0afdfa1efe67
This commit is contained in:
Siim Sammul
2021-05-24 13:29:32 +00:00
committed by Automerger Merge Worker
3 changed files with 107 additions and 5 deletions

View File

@@ -66,6 +66,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
public static final boolean DEFAULT_IGNORE_BATTERY_STATUS = false; public static final boolean DEFAULT_IGNORE_BATTERY_STATUS = false;
public static final boolean DEFAULT_COLLECT_LATENCY_DATA = true; public static final boolean DEFAULT_COLLECT_LATENCY_DATA = true;
public static final int MAX_BINDER_CALL_STATS_COUNT_DEFAULT = 1500; 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 final String DEBUG_ENTRY_PREFIX = "__DEBUG_";
private static class OverflowBinder extends Binder {} 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 mIgnoreBatteryStatus = DEFAULT_IGNORE_BATTERY_STATUS;
private boolean mCollectLatencyData = DEFAULT_COLLECT_LATENCY_DATA; 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.Readonly mDeviceState;
private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch; private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch;
@@ -178,6 +185,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
this.mRandom = injector.getRandomGenerator(); this.mRandom = injector.getRandomGenerator();
this.mCallStatsObserverHandler = injector.getHandler(); this.mCallStatsObserverHandler = injector.getHandler();
this.mLatencyObserver = injector.getLatencyObserver(processSource); this.mLatencyObserver = injector.getLatencyObserver(processSource);
this.mShardingOffset = mRandom.nextInt(mShardingModulo);
} }
public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) { 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) { private UidEntry getUidEntry(int uid) {
UidEntry uidEntry = mUidEntries.get(uid); UidEntry uidEntry = mUidEntries.get(uid);
if (uidEntry == null) { if (uidEntry == null) {
@@ -424,6 +445,15 @@ public class BinderCallsStats implements BinderInternal.Observer {
* This method is expensive to call. * This method is expensive to call.
*/ */
public ArrayList<ExportedCallStat> getExportedCallStats() { 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. // We do not collect all the data if detailed tracking is off.
if (!mDetailedTracking) { if (!mDetailedTracking) {
return new ArrayList<>(); return new ArrayList<>();
@@ -435,7 +465,10 @@ public class BinderCallsStats implements BinderInternal.Observer {
for (int entryIdx = 0; entryIdx < uidEntriesSize; entryIdx++) { for (int entryIdx = 0; entryIdx < uidEntriesSize; entryIdx++) {
final UidEntry entry = mUidEntries.valueAt(entryIdx); final UidEntry entry = mUidEntries.valueAt(entryIdx);
for (CallStat stat : entry.getCallStatsList()) { 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( resultCallStats.add(
createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis())); createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis()));
resultCallStats.add(createDebugEntry("sampling_interval", mPeriodicSamplingInterval)); resultCallStats.add(createDebugEntry("sampling_interval", mPeriodicSamplingInterval));
resultCallStats.add(createDebugEntry("sharding_modulo", mShardingModulo));
} }
return resultCallStats; return resultCallStats;
@@ -459,11 +493,24 @@ public class BinderCallsStats implements BinderInternal.Observer {
* This method is expensive to call. * This method is expensive to call.
*/ */
public ArrayList<ExportedCallStat> getExportedCallStats(int workSourceUid) { 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<>(); ArrayList<ExportedCallStat> resultCallStats = new ArrayList<>();
synchronized (mLock) { synchronized (mLock) {
final UidEntry entry = getUidEntry(workSourceUid); final UidEntry entry = getUidEntry(workSourceUid);
for (CallStat stat : entry.getCallStatsList()) { 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.print("On battery time (ms): ");
pw.println(mBatteryStopwatch != null ? mBatteryStopwatch.getMillis() : 0); pw.println(mBatteryStopwatch != null ? mBatteryStopwatch.getMillis() : 0);
pw.println("Sampling interval period: " + mPeriodicSamplingInterval); pw.println("Sampling interval period: " + mPeriodicSamplingInterval);
pw.println("Sharding modulo: " + mShardingModulo);
final String datasetSizeDesc = verbose ? "" : "(top 90% by cpu time) "; final String datasetSizeDesc = verbose ? "" : "(top 90% by cpu time) ";
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
@@ -566,9 +614,9 @@ public class BinderCallsStats implements BinderInternal.Observer {
+ "call_count):"); + "call_count):");
final List<ExportedCallStat> exportedCallStats; final List<ExportedCallStat> exportedCallStats;
if (workSourceUid != Process.INVALID_UID) { if (workSourceUid != Process.INVALID_UID) {
exportedCallStats = getExportedCallStats(workSourceUid); exportedCallStats = getExportedCallStats(workSourceUid, true);
} else { } else {
exportedCallStats = getExportedCallStats(); exportedCallStats = getExportedCallStats(true);
} }
exportedCallStats.sort(BinderCallsStats::compareByCpuDesc); exportedCallStats.sort(BinderCallsStats::compareByCpuDesc);
for (ExportedCallStat e : exportedCallStats) { 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. */ /** Whether to collect latency histograms. */
public void setCollectLatencyData(boolean collectLatencyData) { public void setCollectLatencyData(boolean collectLatencyData) {
mCollectLatencyData = 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_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_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_IGNORE_BATTERY_STATUS_KEY = "ignore_battery_status";
public static final String SETTINGS_SHARDING_MODULO_KEY = "sharding_modulo";
// Settings for BinderLatencyObserver. // Settings for BinderLatencyObserver.
public static final String SETTINGS_COLLECT_LATENCY_DATA_KEY = "collect_latency_data"; public static final String SETTINGS_COLLECT_LATENCY_DATA_KEY = "collect_latency_data";
public static final String SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY = public static final String SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY =

View File

@@ -241,6 +241,34 @@ public class BinderCallsStatsTest {
assertEquals(10, callStats.maxCpuTimeMicros); 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 { private static class BinderWithGetTransactionName extends Binder {
public static String getDefaultTransactionName(int code) { public static String getDefaultTransactionName(int code) {
return "resolved"; return "resolved";
@@ -669,7 +697,7 @@ public class BinderCallsStatsTest {
bcs.setAddDebugEntries(true); bcs.setAddDebugEntries(true);
bcs.setSamplingInterval(10); bcs.setSamplingInterval(10);
ArrayList<BinderCallsStats.ExportedCallStat> callStats = bcs.getExportedCallStats(); ArrayList<BinderCallsStats.ExportedCallStat> callStats = bcs.getExportedCallStats();
assertEquals(4, callStats.size()); assertEquals(5, callStats.size());
BinderCallsStats.ExportedCallStat debugEntry1 = callStats.get(0); BinderCallsStats.ExportedCallStat debugEntry1 = callStats.get(0);
assertEquals("", debugEntry1.className); assertEquals("", debugEntry1.className);
assertEquals("__DEBUG_start_time_millis", debugEntry1.methodName); assertEquals("__DEBUG_start_time_millis", debugEntry1.methodName);
@@ -685,6 +713,9 @@ public class BinderCallsStatsTest {
BinderCallsStats.ExportedCallStat debugEntry4 = callStats.get(3); BinderCallsStats.ExportedCallStat debugEntry4 = callStats.get(3);
assertEquals("__DEBUG_sampling_interval", debugEntry4.methodName); assertEquals("__DEBUG_sampling_interval", debugEntry4.methodName);
assertEquals(10, debugEntry4.latencyMicros); assertEquals(10, debugEntry4.latencyMicros);
BinderCallsStats.ExportedCallStat debugEntry5 = callStats.get(4);
assertEquals("__DEBUG_sharding_modulo", debugEntry5.methodName);
assertEquals(1, debugEntry5.latencyMicros);
} }
@Test @Test

View File

@@ -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_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_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_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_DIRECT_CALLING_UID_KEY;
import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_TRACK_SCREEN_INTERACTIVE_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( mBinderCallsStats.setIgnoreBatteryStatus(
mParser.getBoolean(SETTINGS_IGNORE_BATTERY_STATUS_KEY, mParser.getBoolean(SETTINGS_IGNORE_BATTERY_STATUS_KEY,
BinderCallsStats.DEFAULT_IGNORE_BATTERY_STATUS)); BinderCallsStats.DEFAULT_IGNORE_BATTERY_STATUS));
mBinderCallsStats.setShardingModulo(mParser.getInt(
SETTINGS_SHARDING_MODULO_KEY,
BinderCallsStats.SHARDING_MODULO_DEFAULT));
mBinderCallsStats.setCollectLatencyData( mBinderCallsStats.setCollectLatencyData(
mParser.getBoolean(SETTINGS_COLLECT_LATENCY_DATA_KEY, mParser.getBoolean(SETTINGS_COLLECT_LATENCY_DATA_KEY,
BinderCallsStats.DEFAULT_COLLECT_LATENCY_DATA)); BinderCallsStats.DEFAULT_COLLECT_LATENCY_DATA));