diff options
| author | 2018-07-09 19:09:47 +0000 | |
|---|---|---|
| committer | 2018-07-09 19:09:47 +0000 | |
| commit | d2af61a1e8271cf7b413a515c333d6b2d3dcb6c4 (patch) | |
| tree | da691686abb2193d28eb68e5e64cd91d1a74344c | |
| parent | 495e2008c7530d895ba0e0a7c489e5ff10e69c9a (diff) | |
| parent | 1d7f615eec2076b76f5e7582204df00edd15d941 (diff) | |
Merge "Add experiment flag to control binder call stats."
7 files changed, 268 insertions, 40 deletions
diff --git a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java index e4a850335aad..e126fb807b99 100644 --- a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java +++ b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java @@ -45,7 +45,7 @@ public class BinderCallsStatsPerfTest { @Before public void setUp() { - mBinderCallsStats = new BinderCallsStats(true); + mBinderCallsStats = new BinderCallsStats(); } @After @@ -54,6 +54,7 @@ public class BinderCallsStatsPerfTest { @Test public void timeCallSession() { + mBinderCallsStats.setDetailedTracking(true); final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); Binder b = new Binder(); int i = 0; @@ -66,9 +67,9 @@ public class BinderCallsStatsPerfTest { @Test public void timeCallSessionTrackingDisabled() { + mBinderCallsStats.setDetailedTracking(false); final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); Binder b = new Binder(); - mBinderCallsStats = new BinderCallsStats(false); while (state.keepRunning()) { BinderCallsStats.CallSession s = mBinderCallsStats.callStarted(b, 0); mBinderCallsStats.callEnded(s, 0, 0); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index f6b60065c59f..82b66d7b7925 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -12979,6 +12979,21 @@ public final class Settings { */ public static final String GNSS_HAL_LOCATION_REQUEST_DURATION_MILLIS = "gnss_hal_location_request_duration_millis"; + + /** + * Binder call stats settings. + * + * The following strings are supported as keys: + * <pre> + * enabled (boolean) + * detailed_tracking (boolean) + * upload_data (boolean) + * sampling_interval (int) + * </pre> + * + * @hide + */ + public static final String BINDER_CALLS_STATS = "binder_calls_stats"; } /** diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index f3016fc08836..20eab92d018b 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -45,14 +45,21 @@ import java.util.function.ToDoubleFunction; * per thread, uid or call description. */ public class BinderCallsStats { + public static final boolean ENABLED_DEFAULT = true; + public static final boolean DETAILED_TRACKING_DEFAULT = true; + public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 10; + private static final String TAG = "BinderCallsStats"; private static final int CALL_SESSIONS_POOL_SIZE = 100; private static final int PERIODIC_SAMPLING_INTERVAL = 10; private static final int MAX_EXCEPTION_COUNT_SIZE = 50; private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow"; + private static final CallSession NOT_ENABLED = new CallSession(); private static final BinderCallsStats sInstance = new BinderCallsStats(); - private volatile boolean mDetailedTracking = false; + private volatile boolean mEnabled = ENABLED_DEFAULT; + private volatile boolean mDetailedTracking = DETAILED_TRACKING_DEFAULT; + private volatile int mPeriodicSamplingInterval = PERIODIC_SAMPLING_INTERVAL_DEFAULT; @GuardedBy("mLock") private final SparseArray<UidEntry> mUidEntries = new SparseArray<>(); @GuardedBy("mLock") @@ -63,12 +70,8 @@ public class BinderCallsStats { @GuardedBy("mLock") private UidEntry mSampledEntries = new UidEntry(-1); - private BinderCallsStats() { - } - - @VisibleForTesting - public BinderCallsStats(boolean detailedTracking) { - mDetailedTracking = detailedTracking; + @VisibleForTesting // Use getInstance() instead. + public BinderCallsStats() { } public CallSession callStarted(Binder binder, int code) { @@ -76,10 +79,15 @@ public class BinderCallsStats { } private CallSession callStarted(String className, int code) { + if (!mEnabled) { + return NOT_ENABLED; + } + CallSession s = mCallSessionsPool.poll(); if (s == null) { s = new CallSession(); } + s.callStat.className = className; s.callStat.msg = code; s.exceptionThrown = false; @@ -92,7 +100,7 @@ public class BinderCallsStats { s.timeStarted = getElapsedRealtimeMicro(); } else { s.sampledCallStat = mSampledEntries.getOrCreate(s.callStat); - if (s.sampledCallStat.callCount % PERIODIC_SAMPLING_INTERVAL == 0) { + if (s.sampledCallStat.callCount % mPeriodicSamplingInterval == 0) { s.cpuTimeStarted = getThreadTimeMicro(); s.timeStarted = getElapsedRealtimeMicro(); } @@ -103,7 +111,23 @@ public class BinderCallsStats { public void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize) { Preconditions.checkNotNull(s); + if (s == NOT_ENABLED) { + return; + } + + processCallEnded(s, parcelRequestSize, parcelReplySize); + + if (mCallSessionsPool.size() < CALL_SESSIONS_POOL_SIZE) { + mCallSessionsPool.add(s); + } + } + + private void processCallEnded(CallSession s, int parcelRequestSize, int parcelReplySize) { synchronized (mLock) { + if (!mEnabled) { + return; + } + long duration; long latencyDuration; if (mDetailedTracking) { @@ -117,7 +141,7 @@ public class BinderCallsStats { latencyDuration = getElapsedRealtimeMicro() - s.timeStarted; } else { // callCount is always incremented, but time only once per sampling interval - long samplesCount = cs.callCount / PERIODIC_SAMPLING_INTERVAL + 1; + long samplesCount = cs.callCount / mPeriodicSamplingInterval + 1; duration = cs.cpuTimeMicros / samplesCount; latencyDuration = cs.latencyMicros / samplesCount; } @@ -155,9 +179,6 @@ public class BinderCallsStats { uidEntry.cpuTimeMicros += duration; uidEntry.callCount++; } - if (mCallSessionsPool.size() < CALL_SESSIONS_POOL_SIZE) { - mCallSessionsPool.add(s); - } } /** @@ -169,6 +190,9 @@ public class BinderCallsStats { */ public void callThrewException(CallSession s, Exception exception) { Preconditions.checkNotNull(s); + if (!mEnabled) { + return; + } s.exceptionThrown = true; try { String className = exception.getClass().getName(); @@ -192,6 +216,11 @@ public class BinderCallsStats { } private void dumpLocked(PrintWriter pw, Map<Integer,String> appIdToPkgNameMap, boolean verbose) { + if (!mEnabled) { + pw.println("Binder calls stats disabled."); + return; + } + long totalCallsCount = 0; long totalCpuTime = 0; pw.print("Start time: "); @@ -245,7 +274,7 @@ public class BinderCallsStats { for (CallStat e : sampledStatsList) { sb.setLength(0); sb.append(" ").append(e) - .append(',').append(e.cpuTimeMicros * PERIODIC_SAMPLING_INTERVAL) + .append(',').append(e.cpuTimeMicros * mPeriodicSamplingInterval) .append(',').append(e.callCount) .append(',').append(e.exceptionCount); pw.println(sb); @@ -304,9 +333,29 @@ public class BinderCallsStats { } public void setDetailedTracking(boolean enabled) { - if (enabled != mDetailedTracking) { - reset(); - mDetailedTracking = enabled; + synchronized (mLock) { + if (enabled != mDetailedTracking) { + mDetailedTracking = enabled; + reset(); + } + } + } + + public void setEnabled(boolean enabled) { + synchronized (mLock) { + if (enabled != mEnabled) { + mEnabled = enabled; + reset(); + } + } + } + + public void setSamplingInterval(int samplingInterval) { + synchronized (mLock) { + if (samplingInterval != mPeriodicSamplingInterval) { + mPeriodicSamplingInterval = samplingInterval; + reset(); + } } } diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 3b19c6a699ae..f91d14988d29 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -124,6 +124,7 @@ public class SettingsBackupTest { Settings.Global.BATTERY_DISCHARGE_THRESHOLD, Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS, Settings.Global.BATTERY_STATS_CONSTANTS, + Settings.Global.BINDER_CALLS_STATS, Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, Settings.Global.BLE_SCAN_LOW_POWER_WINDOW_MS, Settings.Global.BLE_SCAN_LOW_POWER_INTERVAL_MS, 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 2f9f758fa793..914fb7409c74 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java @@ -46,7 +46,9 @@ public class BinderCallsStatsTest { @Test public void testDetailedOff() { - TestBinderCallsStats bcs = new TestBinderCallsStats(false); + TestBinderCallsStats bcs = new TestBinderCallsStats(); + bcs.setDetailedTracking(false); + Binder binder = new Binder(); BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1); bcs.time += 10; @@ -98,7 +100,9 @@ public class BinderCallsStatsTest { @Test public void testDetailedOn() { - TestBinderCallsStats bcs = new TestBinderCallsStats(true); + TestBinderCallsStats bcs = new TestBinderCallsStats(); + bcs.setDetailedTracking(true); + Binder binder = new Binder(); BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1); bcs.time += 10; @@ -145,8 +149,86 @@ public class BinderCallsStatsTest { } @Test + public void testDisabled() { + TestBinderCallsStats bcs = new TestBinderCallsStats(); + bcs.setEnabled(false); + + Binder binder = new Binder(); + BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1); + bcs.time += 10; + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); + + SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries(); + assertEquals(0, uidEntries.size()); + } + + @Test + public void testDisableInBetweenCall() { + TestBinderCallsStats bcs = new TestBinderCallsStats(); + bcs.setEnabled(true); + + Binder binder = new Binder(); + BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1); + bcs.time += 10; + bcs.setEnabled(false); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); + + SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries(); + assertEquals(0, uidEntries.size()); + } + + @Test + public void testEnableInBetweenCall() { + TestBinderCallsStats bcs = new TestBinderCallsStats(); + bcs.setEnabled(false); + + Binder binder = new Binder(); + BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1); + bcs.time += 10; + bcs.setEnabled(true); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); + + SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries(); + assertEquals(0, uidEntries.size()); + } + + @Test + public void testSampling() { + TestBinderCallsStats bcs = new TestBinderCallsStats(); + bcs.setDetailedTracking(false); + bcs.setSamplingInterval(2); + + Binder binder = new Binder(); + BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1); + bcs.time += 10; + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); + + callSession = bcs.callStarted(binder, 1); + bcs.time += 1000; // shoud be ignored. + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); + + callSession = bcs.callStarted(binder, 1); + bcs.time += 50; + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); + + SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries(); + assertEquals(1, uidEntries.size()); + BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID); + Assert.assertNotNull(uidEntry); + assertEquals(3, uidEntry.callCount); + assertEquals(70, uidEntry.cpuTimeMicros); + assertEquals("Detailed tracking off - no entries should be returned", + 0, uidEntry.getCallStatsList().size()); + + BinderCallsStats.UidEntry sampledEntries = bcs.getSampledEntries(); + List<BinderCallsStats.CallStat> sampledCallStatsList = sampledEntries.getCallStatsList(); + assertEquals(1, sampledCallStatsList.size()); + } + + @Test public void testParcelSize() { - TestBinderCallsStats bcs = new TestBinderCallsStats(true); + TestBinderCallsStats bcs = new TestBinderCallsStats(); + bcs.setDetailedTracking(true); Binder binder = new Binder(); BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1); bcs.time += 10; @@ -161,7 +243,8 @@ public class BinderCallsStatsTest { @Test public void testMaxCpu() { - TestBinderCallsStats bcs = new TestBinderCallsStats(true); + TestBinderCallsStats bcs = new TestBinderCallsStats(); + bcs.setDetailedTracking(true); Binder binder = new Binder(); BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1); bcs.time += 50; @@ -179,7 +262,8 @@ public class BinderCallsStatsTest { @Test public void testMaxLatency() { - TestBinderCallsStats bcs = new TestBinderCallsStats(true); + TestBinderCallsStats bcs = new TestBinderCallsStats(); + bcs.setDetailedTracking(true); Binder binder = new Binder(); BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1); bcs.elapsedTime += 5; @@ -205,7 +289,8 @@ public class BinderCallsStatsTest { @Test public void testExceptionCount() { - TestBinderCallsStats bcs = new TestBinderCallsStats(true); + TestBinderCallsStats bcs = new TestBinderCallsStats(); + bcs.setDetailedTracking(true); Binder binder = new Binder(); BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1); bcs.callThrewException(callSession, new IllegalStateException()); @@ -227,7 +312,8 @@ public class BinderCallsStatsTest { @Test public void testDumpDoesNotThrowException() { - TestBinderCallsStats bcs = new TestBinderCallsStats(true); + TestBinderCallsStats bcs = new TestBinderCallsStats(); + bcs.setDetailedTracking(true); Binder binder = new Binder(); BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1); bcs.callThrewException(callSession, new IllegalStateException()); @@ -242,8 +328,7 @@ public class BinderCallsStatsTest { long time = 1234; long elapsedTime = 0; - TestBinderCallsStats(boolean detailedTracking) { - super(detailedTracking); + TestBinderCallsStats() { } @Override diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java index 490fcc1eb253..3d779d80b9a1 100644 --- a/services/core/java/com/android/server/BinderCallsStatsService.java +++ b/services/core/java/com/android/server/BinderCallsStatsService.java @@ -17,15 +17,20 @@ package com.android.server; import android.app.AppGlobals; +import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.database.ContentObserver; +import android.net.Uri; import android.os.Binder; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; +import android.provider.Settings; +import android.util.KeyValueListParser; import android.util.Slog; +import com.android.internal.os.BackgroundThread; import com.android.internal.os.BinderCallsStats; import java.io.FileDescriptor; @@ -41,18 +46,90 @@ public class BinderCallsStatsService extends Binder { private static final String PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING = "persist.sys.binder_calls_detailed_tracking"; - public static void start() { - BinderCallsStatsService service = new BinderCallsStatsService(); - ServiceManager.addService("binder_calls_stats", service); - boolean detailedTrackingEnabled = SystemProperties.getBoolean( - PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, false); - - if (detailedTrackingEnabled) { - Slog.i(TAG, "Enabled CPU usage tracking for binder calls. Controlled by " - + PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING - + " or via dumpsys binder_calls_stats --enable-detailed-tracking"); - BinderCallsStats.getInstance().setDetailedTracking(true); + /** Listens for flag changes. */ + private static class SettingsObserver extends ContentObserver { + private static final String SETTINGS_ENABLED_KEY = "enabled"; + 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 final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS); + private final Context mContext; + private final KeyValueListParser mParser = new KeyValueListParser(','); + + public SettingsObserver(Context context) { + super(BackgroundThread.getHandler()); + mContext = context; + context.getContentResolver().registerContentObserver(mUri, false, this, + UserHandle.USER_SYSTEM); + // Always kick once to ensure that we match current state + onChange(); + } + + @Override + public void onChange(boolean selfChange, Uri uri, int userId) { + if (mUri.equals(uri)) { + onChange(); + } + } + + public void onChange() { + // Do not overwrite the default set manually. + if (!SystemProperties.get(PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING).isEmpty()) { + return; + } + + BinderCallsStats stats = BinderCallsStats.getInstance(); + try { + mParser.setString(Settings.Global.getString(mContext.getContentResolver(), + Settings.Global.BINDER_CALLS_STATS)); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Bad binder call stats settings", e); + } + stats.setEnabled( + mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT)); + stats.setDetailedTracking(mParser.getBoolean( + SETTINGS_DETAILED_TRACKING_KEY, BinderCallsStats.DETAILED_TRACKING_DEFAULT)); + stats.setSamplingInterval(mParser.getInt( + SETTINGS_SAMPLING_INTERVAL_KEY, + BinderCallsStats.PERIODIC_SAMPLING_INTERVAL_DEFAULT)); + } + } + + public static class LifeCycle extends SystemService { + private BinderCallsStatsService mService; + + public LifeCycle(Context context) { + super(context); + } + + @Override + public void onStart() { + mService = new BinderCallsStatsService(); + publishBinderService("binder_calls_stats", mService); + boolean detailedTrackingEnabled = SystemProperties.getBoolean( + PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, false); + + if (detailedTrackingEnabled) { + Slog.i(TAG, "Enabled CPU usage tracking for binder calls. Controlled by " + + PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING + + " or via dumpsys binder_calls_stats --enable-detailed-tracking"); + BinderCallsStats.getInstance().setDetailedTracking(true); + } } + + @Override + public void onBootPhase(int phase) { + if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) { + mService.systemReady(getContext()); + } + } + } + + private SettingsObserver mSettingsObserver; + + public void systemReady(Context context) { + mSettingsObserver = new SettingsObserver(context); } public static void reset() { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 1f1b3f88622a..252a1fdb7416 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -722,7 +722,7 @@ public final class SystemServer { // Tracks cpu time spent in binder calls traceBeginAndSlog("StartBinderCallsStatsService"); - BinderCallsStatsService.start(); + mSystemServiceManager.startService(BinderCallsStatsService.LifeCycle.class); traceEnd(); } |