Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3818a53e authored by Dmitri Plotnikov's avatar Dmitri Plotnikov
Browse files

Run per procstate CPU time-in-state tracking on the main thread

Procstate tracking will now be enabled unconditionally
when eBPF is supported by the kernel. Otherwise, it will be disabled.

Bug: 197162116
Test: atest FrameworksCoreTests:BatteryStatsTests

Change-Id: I200ef51b92aa19d42a10966aa6fea6f99d741f3f
parent c13e7277
Loading
Loading
Loading
Loading
+6 −11
Original line number Diff line number Diff line
@@ -634,7 +634,7 @@ public abstract class BatteryStats implements Parcelable {
     */
    public static int mapToInternalProcessState(int procState) {
        if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
            return ActivityManager.PROCESS_STATE_NONEXISTENT;
            return Uid.PROCESS_STATE_NONEXISTENT;
        } else if (procState == ActivityManager.PROCESS_STATE_TOP) {
            return Uid.PROCESS_STATE_TOP;
        } else if (ActivityManager.isForegroundService(procState)) {
@@ -911,6 +911,11 @@ public abstract class BatteryStats implements Parcelable {
         * Total number of process states we track.
         */
        public static final int NUM_PROCESS_STATE = 7;
        /**
         * State of the UID when it has no running processes.  It is intentionally out of
         * bounds 0..NUM_PROCESS_STATE.
         */
        public static final int PROCESS_STATE_NONEXISTENT = NUM_PROCESS_STATE;

        // Used in dump
        static final String[] PROCESS_STATE_NAMES = {
@@ -930,16 +935,6 @@ public abstract class BatteryStats implements Parcelable {
                "C"   // CACHED
        };

        /**
         * When the process exits one of these states, we need to make sure cpu time in this state
         * is not attributed to any non-critical process states.
         */
        public static final int[] CRITICAL_PROC_STATES = {
                Uid.PROCESS_STATE_TOP,
                Uid.PROCESS_STATE_FOREGROUND_SERVICE,
                Uid.PROCESS_STATE_FOREGROUND
        };

        public abstract long getProcessStateTime(int state, long elapsedRealtimeUs, int which);
        public abstract Timer getProcessStateTimer(int state);

+90 −241

File changed.

Preview size limit exceeded, changes collapsed.

+4 −0
Original line number Diff line number Diff line
@@ -620,6 +620,10 @@ public abstract class KernelCpuUidTimeReader<T> {
            }
            return numClusterFreqs;
        }

        public boolean isFastCpuTimesReader() {
            return mBpfTimesAvailable;
        }
    }

    /**
+52 −81
Original line number Diff line number Diff line
@@ -37,7 +37,6 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.os.BatteryStats;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.Display;

import androidx.test.filters.LargeTest;
@@ -53,6 +52,7 @@ import org.mockito.MockitoAnnotations;

@LargeTest
@RunWith(AndroidJUnit4.class)
@SuppressWarnings("GuardedBy")
public class BatteryStatsImplTest {
    private static final long[] CPU_FREQS = {1, 2, 3, 4, 5};
    private static final int NUM_CPU_FREQS = CPU_FREQS.length;
@@ -62,29 +62,26 @@ public class BatteryStatsImplTest {
    @Mock
    private KernelSingleUidTimeReader mKernelSingleUidTimeReader;

    private final MockClock mMockClock = new MockClock();
    private MockBatteryStatsImpl mBatteryStatsImpl;

    private MockClock mMockClock = new MockClock();

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);

        when(mKernelUidCpuFreqTimeReader.isFastCpuTimesReader()).thenReturn(true);
        when(mKernelUidCpuFreqTimeReader.readFreqs(any())).thenReturn(CPU_FREQS);
        when(mKernelUidCpuFreqTimeReader.allUidTimesAvailable()).thenReturn(true);
        when(mKernelSingleUidTimeReader.singleUidCpuTimesAvailable()).thenReturn(true);
        mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock)
                .setKernelCpuUidFreqTimeReader(mKernelUidCpuFreqTimeReader)
                .setKernelSingleUidTimeReader(mKernelSingleUidTimeReader)
                .setTrackingCpuByProcStateEnabled(true);
                .setKernelSingleUidTimeReader(mKernelSingleUidTimeReader);
    }

    @Test
    public void testUpdateProcStateCpuTimes() {
        mBatteryStatsImpl.setOnBatteryInternal(true);
        synchronized (mBatteryStatsImpl) {
        mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
        }

        final int[] testUids = {10032, 10048, 10145, 10139};
        final int[] activityManagerProcStates = {
@@ -99,15 +96,24 @@ public class BatteryStatsImplTest {
                PROCESS_STATE_TOP,
                PROCESS_STATE_CACHED
        };
        addPendingUids(testUids, testProcStates);

        // Initialize time-in-freq counters
        mMockClock.realtime = 1000;
        for (int i = 0; i < testUids.length; ++i) {
            mBatteryStatsImpl.noteUidProcessStateLocked(testUids[i], activityManagerProcStates[i]);
            mockKernelSingleUidTimeReader(testUids[i], new long[5]);
            mBatteryStatsImpl.noteUidProcessStateLocked(testUids[i], activityManagerProcStates[i]);
        }

        final long[] timeInFreqs = new long[NUM_CPU_FREQS];

        // Verify there are no cpu times initially.
        for (int i = 0; i < testUids.length; ++i) {
            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStatsLocked(testUids[i]);
            for (int procState = 0; procState < NUM_PROCESS_STATE; ++procState) {
                assertFalse(u.getCpuFreqTimes(timeInFreqs, procState));
                assertFalse(u.getScreenOffCpuFreqTimes(timeInFreqs, procState));
            }
        }
        mBatteryStatsImpl.updateProcStateCpuTimes(true, false);

        // Obtain initial CPU time-in-freq counts
        final long[][] cpuTimes = {
@@ -117,24 +123,14 @@ public class BatteryStatsImplTest {
                {4859048, 348903, 4578967, 5973894, 298549}
        };

        final long[] timeInFreqs = new long[NUM_CPU_FREQS];
        mMockClock.realtime += 1000;

        for (int i = 0; i < testUids.length; ++i) {
            mockKernelSingleUidTimeReader(testUids[i], cpuTimes[i]);

            // Verify there are no cpu times initially.
            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStatsLocked(testUids[i]);
            for (int procState = 0; procState < NUM_PROCESS_STATE; ++procState) {
                assertFalse(u.getCpuFreqTimes(timeInFreqs, procState));
                assertFalse(u.getScreenOffCpuFreqTimes(timeInFreqs, procState));
            }
            mBatteryStatsImpl.updateProcStateCpuTimesLocked(testUids[i],
                    mMockClock.realtime);
        }
        addPendingUids(testUids, testProcStates);

        mMockClock.realtime += 1000;
        mBatteryStatsImpl.updateProcStateCpuTimes(true, false);

        verifyNoPendingUids();
        for (int i = 0; i < testUids.length; ++i) {
            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
            for (int procState = 0; procState < NUM_PROCESS_STATE; ++procState) {
@@ -155,19 +151,19 @@ public class BatteryStatsImplTest {
                {945894, 9089432, 19478, 3834, 7845},
                {843895, 43948, 949582, 99, 384}
        };

        mMockClock.realtime += 1000;

        for (int i = 0; i < testUids.length; ++i) {
            long[] newCpuTimes = new long[cpuTimes[i].length];
            for (int j = 0; j < cpuTimes[i].length; j++) {
                newCpuTimes[j] = cpuTimes[i][j] + delta1[i][j];
            }
            mockKernelSingleUidTimeReader(testUids[i], newCpuTimes);
            mBatteryStatsImpl.updateProcStateCpuTimesLocked(testUids[i],
                    mMockClock.realtime);
        }
        addPendingUids(testUids, testProcStates);

        mMockClock.realtime += 1000;
        mBatteryStatsImpl.updateProcStateCpuTimes(true, false);

        verifyNoPendingUids();
        for (int i = 0; i < testUids.length; ++i) {
            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
            for (int procState = 0; procState < NUM_PROCESS_STATE; ++procState) {
@@ -186,10 +182,8 @@ public class BatteryStatsImplTest {
        }

        // Validate the on-battery-screen-off counter
        synchronized (mBatteryStatsImpl) {
        mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0,
                mMockClock.realtime * 1000);
        }

        final long[][] delta2 = {
                {95932, 2943, 49834, 89034, 139},
@@ -197,19 +191,19 @@ public class BatteryStatsImplTest {
                {678, 7498, 9843, 889, 4894},
                {488, 998, 8498, 394, 574}
        };

        mMockClock.realtime += 1000;

        for (int i = 0; i < testUids.length; ++i) {
            long[] newCpuTimes = new long[cpuTimes[i].length];
            for (int j = 0; j < cpuTimes[i].length; j++) {
                newCpuTimes[j] = cpuTimes[i][j] + delta1[i][j] + delta2[i][j];
            }
            mockKernelSingleUidTimeReader(testUids[i], newCpuTimes);
            mBatteryStatsImpl.updateProcStateCpuTimesLocked(testUids[i],
                    mMockClock.realtime);
        }
        addPendingUids(testUids, testProcStates);

        mMockClock.realtime += 1000;
        mBatteryStatsImpl.updateProcStateCpuTimes(true, true);

        verifyNoPendingUids();
        for (int i = 0; i < testUids.length; ++i) {
            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
            for (int procState = 0; procState < NUM_PROCESS_STATE; ++procState) {
@@ -239,24 +233,25 @@ public class BatteryStatsImplTest {
                {3049509483598l, 4597834, 377654, 94589035, 7854},
                {9493, 784, 99895, 8974893, 9879843}
        };

        mMockClock.realtime += 1000;

        final int parentUid = testUids[1];
        final int childUid = 99099;
        addIsolatedUid(parentUid, childUid);
        final long[] isolatedUidCpuTimes = {495784, 398473, 4895, 4905, 30984093};
        mockKernelSingleUidTimeReader(childUid, isolatedUidCpuTimes, isolatedUidCpuTimes);

        for (int i = 0; i < testUids.length; ++i) {
            long[] newCpuTimes = new long[cpuTimes[i].length];
            for (int j = 0; j < cpuTimes[i].length; j++) {
                newCpuTimes[j] = cpuTimes[i][j] + delta1[i][j] + delta2[i][j] + delta3[i][j];
            }
            mockKernelSingleUidTimeReader(testUids[i], newCpuTimes);
            mBatteryStatsImpl.updateProcStateCpuTimesLocked(testUids[i],
                    mMockClock.realtime);
        }
        addPendingUids(testUids, testProcStates);
        final int parentUid = testUids[1];
        final int childUid = 99099;
        addIsolatedUid(parentUid, childUid);
        final long[] isolatedUidCpuTimes = {495784, 398473, 4895, 4905, 30984093};
        mockKernelSingleUidTimeReader(childUid, isolatedUidCpuTimes, isolatedUidCpuTimes);

        mMockClock.realtime += 1000;
        mBatteryStatsImpl.updateProcStateCpuTimes(true, true);

        verifyNoPendingUids();
        for (int i = 0; i < testUids.length; ++i) {
            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
            for (int procState = 0; procState < NUM_PROCESS_STATE; ++procState) {
@@ -284,11 +279,9 @@ public class BatteryStatsImplTest {
    }

    @Test
    public void testCopyFromAllUidsCpuTimes() {
    public void testUpdateCpuTimesForAllUids() {
        mBatteryStatsImpl.setOnBatteryInternal(false);
        synchronized (mBatteryStatsImpl) {
        mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
        }

        mMockClock.realtime = 1000;

@@ -299,14 +292,14 @@ public class BatteryStatsImplTest {
                PROCESS_STATE_TOP,
                PROCESS_STATE_CACHED
        };
        addPendingUids(testUids, testProcStates);

        for (int i = 0; i < testUids.length; ++i) {
            BatteryStatsImpl.Uid uid = mBatteryStatsImpl.getUidStatsLocked(testUids[i]);
            uid.setProcessStateForTest(testProcStates[i], mMockClock.elapsedRealtime());
            mockKernelSingleUidTimeReader(testUids[i], new long[NUM_CPU_FREQS]);
            mBatteryStatsImpl.updateProcStateCpuTimesLocked(testUids[i],
                    mMockClock.elapsedRealtime());
        }
        mBatteryStatsImpl.updateProcStateCpuTimes(true, false);

        final SparseArray<long[]> allUidCpuTimes = new SparseArray<>();
        long[][] allCpuTimes = {
@@ -330,9 +323,8 @@ public class BatteryStatsImplTest {
        }

        mMockClock.realtime += 1000;
        mBatteryStatsImpl.copyFromAllUidsCpuTimes(true, false);

        verifyNoPendingUids();
        mBatteryStatsImpl.updateCpuTimesForAllUids();

        final long[] timeInFreqs = new long[NUM_CPU_FREQS];

@@ -411,9 +403,7 @@ public class BatteryStatsImplTest {
        final int releaseTimeMs = 1005;
        final int currentTimeMs = 1011;

        synchronized (mBatteryStatsImpl) {
        mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
        }

        // Create a Uid Object
        final BatteryStats.Uid u = mBatteryStatsImpl.getUidStatsLocked(testUid);
@@ -436,9 +426,7 @@ public class BatteryStatsImplTest {
        final int acquireTimeMs = 1000;
        final int currentTimeMs = 1011;

        synchronized (mBatteryStatsImpl) {
        mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
        }

        // Create a Uid Object
        final BatteryStats.Uid u = mBatteryStatsImpl.getUidStatsLocked(testUid);
@@ -464,9 +452,7 @@ public class BatteryStatsImplTest {
        final int releaseTimeMs_2 = 1009;
        final int currentTimeMs = 1011;

        synchronized (mBatteryStatsImpl) {
        mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
        }

        // Create a Uid Object
        final BatteryStats.Uid u = mBatteryStatsImpl.getUidStatsLocked(testUid);
@@ -496,9 +482,7 @@ public class BatteryStatsImplTest {
        final int releaseTimeMs_2 = 1009;
        final int currentTimeMs = 1011;

        synchronized (mBatteryStatsImpl) {
        mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
        }

        // Create a Uid Object
        final BatteryStats.Uid u = mBatteryStatsImpl.getUidStatsLocked(testUid);
@@ -523,17 +507,4 @@ public class BatteryStatsImplTest {
        final BatteryStatsImpl.Uid u = mBatteryStatsImpl.getUidStatsLocked(parentUid);
        u.addIsolatedUid(childUid);
    }

    private void addPendingUids(int[] uids, int[] procStates) {
        final SparseIntArray pendingUids = mBatteryStatsImpl.getPendingUids();
        for (int i = 0; i < uids.length; ++i) {
            pendingUids.put(uids[i], procStates[i]);
        }
    }

    private void verifyNoPendingUids() {
        final SparseIntArray pendingUids = mBatteryStatsImpl.getPendingUids();
        assertEquals("There shouldn't be any pending uids left: " + pendingUids,
                0, pendingUids.size());
    }
}
+0 −128
Original line number Diff line number Diff line
@@ -26,9 +26,6 @@ import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP;
import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING;
import static android.os.BatteryStats.Uid.UID_PROCESS_TYPES;

import static com.android.internal.os.BatteryStatsImpl.Constants.KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS;
import static com.android.internal.os.BatteryStatsImpl.Constants.KEY_TRACK_CPU_TIMES_BY_PROC_STATE;

import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
@@ -51,7 +48,6 @@ import android.os.Process;
import android.os.SystemClock;
import android.provider.Settings;
import android.support.test.uiautomator.UiDevice;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.DebugUtils;
import android.util.KeyValueListParser;
@@ -125,11 +121,6 @@ public class BstatsCpuTimesValidationTest {
                PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
        sTestPkgUid = sContext.getPackageManager().getPackageUid(TEST_PKG, 0);
        executeCmd("cmd deviceidle whitelist +" + TEST_PKG);

        final ArrayMap<String, String> desiredConstants = new ArrayMap<>();
        desiredConstants.put(KEY_TRACK_CPU_TIMES_BY_PROC_STATE, Boolean.toString(true));
        desiredConstants.put(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS, Integer.toString(0));
        updateBatteryStatsConstants(desiredConstants);
        checkCpuTimesAvailability();
    }

@@ -517,125 +508,6 @@ public class BstatsCpuTimesValidationTest {
        batteryOffScreenOn();
    }

    @Test
    public void testCpuFreqTimes_trackingDisabled() throws Exception {
        if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
            Log.w(TAG, "Skipping " + testName.getMethodName()
                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
            return;
        }

        final String bstatsConstants = Settings.Global.getString(sContext.getContentResolver(),
                Settings.Global.BATTERY_STATS_CONSTANTS);
        try {
            batteryOnScreenOn();
            forceStop();
            resetBatteryStats();
            final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid);
            assertNull("Initial snapshot should be null, initial="
                    + Arrays.toString(initialSnapshot), initialSnapshot);
            assertNull("Initial top state snapshot should be null",
                    getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP));

            doSomeWork(PROCESS_STATE_TOP);
            forceStop();

            final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
            final String msgCpuTimes = getAllCpuTimesMsg();
            assertCpuTimesValid(cpuTimesMs);
            long actualCpuTimeMs = 0;
            for (int i = 0; i < cpuTimesMs.length / 2; ++i) {
                actualCpuTimeMs += cpuTimesMs[i];
            }
            assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes,
                    WORK_DURATION_MS, actualCpuTimeMs);

            updateTrackPerProcStateCpuTimesSetting(bstatsConstants, false);

            doSomeWork(PROCESS_STATE_TOP);
            forceStop();

            final long[] cpuTimesMs2 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
            assertCpuTimesValid(cpuTimesMs2);
            assertCpuTimesEqual(cpuTimesMs2, cpuTimesMs, 20,
                    "Unexpected cpu times with tracking off");

            updateTrackPerProcStateCpuTimesSetting(bstatsConstants, true);

            final long[] cpuTimesMs3 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
            assertCpuTimesValid(cpuTimesMs3);
            assertCpuTimesEqual(cpuTimesMs3, cpuTimesMs, 500,
                    "Unexpected cpu times after turning on tracking");

            doSomeWork(PROCESS_STATE_TOP);
            forceStop();

            final long[] cpuTimesMs4 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
            assertCpuTimesValid(cpuTimesMs4);
            actualCpuTimeMs = 0;
            for (int i = 0; i < cpuTimesMs4.length / 2; ++i) {
                actualCpuTimeMs += cpuTimesMs4[i];
            }
            assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes,
                    2 * WORK_DURATION_MS, actualCpuTimeMs);

            batteryOffScreenOn();
        } finally {
            Settings.Global.putString(sContext.getContentResolver(),
                    Settings.Global.BATTERY_STATS_CONSTANTS, bstatsConstants);
        }
    }

    private void assertCpuTimesEqual(long[] actual, long[] expected, long delta, String errMsg) {
        for (int i = actual.length - 1; i >= 0; --i) {
            if (actual[i] > expected[i] + delta || actual[i] < expected[i]) {
                fail(errMsg + ", actual=" + Arrays.toString(actual)
                        + ", expected=" + Arrays.toString(expected) + ", delta=" + delta);
            }
        }
    }

    private void updateTrackPerProcStateCpuTimesSetting(String originalConstants, boolean enabled)
            throws Exception {
        final String newConstants;
        final String setting = KEY_TRACK_CPU_TIMES_BY_PROC_STATE + "=" + enabled;
        if (originalConstants == null || "null".equals(originalConstants)) {
            newConstants = setting;
        } else if (originalConstants.contains(KEY_TRACK_CPU_TIMES_BY_PROC_STATE)) {
            newConstants = originalConstants.replaceAll(
                    KEY_TRACK_CPU_TIMES_BY_PROC_STATE + "=(true|false)", setting);
        } else {
            newConstants = originalConstants + "," + setting;
        }
        Settings.Global.putString(sContext.getContentResolver(),
                Settings.Global.BATTERY_STATS_CONSTANTS, newConstants);
        assertTrackPerProcStateCpuTimesSetting(enabled);
    }

    private void assertTrackPerProcStateCpuTimesSetting(boolean enabled) throws Exception {
        final String expectedValue = Boolean.toString(enabled);
        assertDelayedCondition("Unexpected value for " + KEY_TRACK_CPU_TIMES_BY_PROC_STATE, () -> {
            final String actualValue = getSettingValueFromDump(KEY_TRACK_CPU_TIMES_BY_PROC_STATE);
            return expectedValue.equals(actualValue)
                    ? null : "expected=" + expectedValue + ", actual=" + actualValue;
        }, SETTING_UPDATE_TIMEOUT_MS, SETTING_UPDATE_CHECK_INTERVAL_MS);
    }

    private String getSettingValueFromDump(String key) throws Exception {
        final String settingsDump = executeCmdSilent("dumpsys batterystats --settings");
        final TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter('\n');
        splitter.setString(settingsDump);
        String next;
        while (splitter.hasNext()) {
            next = splitter.next().trim();
            if (next.startsWith(key)) {
                return next.split("=")[1];
            }
        }
        return null;
    }

    private void assertCpuTimesValid(long[] cpuTimes) {
        assertNotNull(cpuTimes);
        for (int i = 0; i < cpuTimes.length; ++i) {
Loading