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

Commit cbd3a2e6 authored by Dmitri Plotnikov's avatar Dmitri Plotnikov
Browse files

Use CpuScalingPolicy in BatteryStats

Bug: 283012390
Test: atest FrameworksServicesTests:BatteryStatsTests
Change-Id: Id916b9b43332f3dac91c1a24351269201c4c415f
parent f6b9b302
Loading
Loading
Loading
Loading
+45 −32
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ import android.view.Display;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BatteryStatsHistoryIterator;
import com.android.internal.os.CpuScalingPolicies;

import com.google.android.collect.Lists;

@@ -1008,9 +1009,11 @@ public abstract class BatteryStats {
         * @param cluster the index of the CPU cluster.
         * @param step the index of the CPU speed. This is not the actual speed of the CPU.
         * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
         * @see com.android.internal.os.PowerProfile#getNumCpuClusters()
         * @see com.android.internal.os.PowerProfile#getNumSpeedStepsInCpuCluster(int)
         * @see com.android.internal.os.CpuScalingPolicies#getPolicies
         * @see com.android.internal.os.CpuScalingPolicies#getFrequencies
         * @deprecated Unused except in tests
         */
        @Deprecated
        public abstract long getTimeAtCpuSpeed(int cluster, int step, int which);

        /**
@@ -1648,11 +1651,9 @@ public abstract class BatteryStats {
    public abstract long getNextMaxDailyDeadline();

    /**
     * Returns the total number of frequencies across all CPU clusters.
     * Returns the CPU scaling policies.
     */
    public abstract int getCpuFreqCount();

    public abstract long[] getCpuFreqs();
    public abstract CpuScalingPolicies getCpuScalingPolicies();

    public final static class HistoryTag {
        public String string;
@@ -3390,8 +3391,8 @@ public abstract class BatteryStats {
     *     cluster1-speeed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...
     * </pre>
     *
     * @see com.android.internal.os.PowerProfile#getNumCpuClusters()
     * @see com.android.internal.os.PowerProfile#getNumSpeedStepsInCpuCluster(int)
     * @see com.android.internal.os.CpuScalingPolicies#getPolicies
     * @see com.android.internal.os.CpuScalingPolicies#getFrequencies
     */
    @Nullable
    public abstract long[] getSystemServiceTimeAtCpuSpeeds();
@@ -4677,12 +4678,14 @@ public abstract class BatteryStats {
                            proportionalAttributionCalculator.getProportionalPowerMah(consumer)));
        }

        final long[] cpuFreqs = getCpuFreqs();
        if (cpuFreqs != null) {
        final CpuScalingPolicies scalingPolicies = getCpuScalingPolicies();
        if (scalingPolicies != null) {
            sb.setLength(0);
            for (int i = 0; i < cpuFreqs.length; ++i) {
                if (i != 0) sb.append(',');
                sb.append(cpuFreqs[i]);
            for (int policy : scalingPolicies.getPolicies()) {
                for (int frequency : scalingPolicies.getFrequencies(policy)) {
                    if (sb.length() != 0) sb.append(',');
                    sb.append(frequency);
                }
            }
            dumpLine(pw, 0 /* uid */, category, GLOBAL_CPU_FREQ_DATA, sb.toString());
        }
@@ -4994,11 +4997,12 @@ public abstract class BatteryStats {
            }

            // If the cpuFreqs is null, then don't bother checking for cpu freq times.
            if (cpuFreqs != null) {
            if (scalingPolicies != null) {
                final long[] cpuFreqTimeMs = u.getCpuFreqTimes(which);
                // If total cpuFreqTimes is null, then we don't need to check for
                // screenOffCpuFreqTimes.
                if (cpuFreqTimeMs != null && cpuFreqTimeMs.length == cpuFreqs.length) {
                if (cpuFreqTimeMs != null
                        && cpuFreqTimeMs.length == scalingPolicies.getScalingStepCount()) {
                    sb.setLength(0);
                    for (int i = 0; i < cpuFreqTimeMs.length; ++i) {
                        if (i != 0) sb.append(',');
@@ -5018,7 +5022,8 @@ public abstract class BatteryStats {
                            cpuFreqTimeMs.length, sb.toString());
                }

                final long[] timesInFreqMs = new long[getCpuFreqCount()];
                final long[] timesInFreqMs =
                        new long[getCpuScalingPolicies().getScalingStepCount()];
                for (int procState = 0; procState < Uid.NUM_PROCESS_STATE; ++procState) {
                    if (u.getCpuFreqTimes(timesInFreqMs, procState)) {
                        sb.setLength(0);
@@ -6000,14 +6005,18 @@ public abstract class BatteryStats {
            }
        }

        final long[] cpuFreqs = getCpuFreqs();
        if (cpuFreqs != null) {
        final CpuScalingPolicies scalingPolicies = getCpuScalingPolicies();
        if (scalingPolicies != null) {
            sb.setLength(0);
            sb.append("  CPU freqs:");
            for (int i = 0; i < cpuFreqs.length; ++i) {
                sb.append(' ').append(cpuFreqs[i]);
            sb.append("  CPU scaling: ");
            for (int policy : scalingPolicies.getPolicies()) {
                sb.append(" policy").append(policy).append(':');
                for (int frequency : scalingPolicies.getFrequencies(policy)) {
                    sb.append(' ').append(frequency);
                }
            pw.println(sb.toString());
            }

            pw.println(sb);
            pw.println();
        }

@@ -6628,7 +6637,7 @@ public abstract class BatteryStats {
                pw.println(sb.toString());
            }

            final long[] timesInFreqMs = new long[getCpuFreqCount()];
            final long[] timesInFreqMs = new long[getCpuScalingPolicies().getScalingStepCount()];
            for (int procState = 0; procState < Uid.NUM_PROCESS_STATE; ++procState) {
                if (u.getCpuFreqTimes(timesInFreqMs, procState)) {
                    sb.setLength(0);
@@ -8078,12 +8087,13 @@ public abstract class BatteryStats {
            proto.write(UidProto.Cpu.USER_DURATION_MS, roundUsToMs(u.getUserCpuTimeUs(which)));
            proto.write(UidProto.Cpu.SYSTEM_DURATION_MS, roundUsToMs(u.getSystemCpuTimeUs(which)));

            final long[] cpuFreqs = getCpuFreqs();
            if (cpuFreqs != null) {
            final CpuScalingPolicies scalingPolicies = getCpuScalingPolicies();
            if (scalingPolicies != null) {
                final long[] cpuFreqTimeMs = u.getCpuFreqTimes(which);
                // If total cpuFreqTimes is null, then we don't need to check for
                // screenOffCpuFreqTimes.
                if (cpuFreqTimeMs != null && cpuFreqTimeMs.length == cpuFreqs.length) {
                if (cpuFreqTimeMs != null
                        && cpuFreqTimeMs.length == scalingPolicies.getScalingStepCount()) {
                    long[] screenOffCpuFreqTimeMs = u.getScreenOffCpuFreqTimes(which);
                    if (screenOffCpuFreqTimeMs == null) {
                        screenOffCpuFreqTimeMs = new long[cpuFreqTimeMs.length];
@@ -8100,8 +8110,9 @@ public abstract class BatteryStats {
                }
            }

            final long[] timesInFreqMs = new long[getCpuFreqCount()];
            final long[] timesInFreqScreenOffMs = new long[getCpuFreqCount()];
            final int stepCount = getCpuScalingPolicies().getScalingStepCount();
            final long[] timesInFreqMs = new long[stepCount];
            final long[] timesInFreqScreenOffMs = new long[stepCount];
            for (int procState = 0; procState < Uid.NUM_PROCESS_STATE; ++procState) {
                if (u.getCpuFreqTimes(timesInFreqMs, procState)) {
                    if (!u.getScreenOffCpuFreqTimes(timesInFreqScreenOffMs, procState)) {
@@ -8571,10 +8582,12 @@ public abstract class BatteryStats {
        dumpDurationSteps(proto, SystemProto.DISCHARGE_STEP, getDischargeLevelStepTracker());

        // CPU frequencies (GLOBAL_CPU_FREQ_DATA)
        final long[] cpuFreqs = getCpuFreqs();
        if (cpuFreqs != null) {
            for (long i : cpuFreqs) {
                proto.write(SystemProto.CPU_FREQUENCY, i);
        final CpuScalingPolicies scalingPolicies = getCpuScalingPolicies();
        if (scalingPolicies != null) {
            for (int policy : scalingPolicies.getPolicies()) {
                for (int frequency : scalingPolicies.getFrequencies(policy)) {
                    proto.write(SystemProto.CPU_FREQUENCY, frequency);
                }
            }
        }

+14 −59
Original line number Diff line number Diff line
@@ -16,9 +16,7 @@
package com.android.internal.os;

import static com.android.internal.os.KernelCpuProcStringReader.asLongs;
import static com.android.internal.util.Preconditions.checkNotNull;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.StrictMode;
import android.util.IntArray;
@@ -29,11 +27,9 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.KernelCpuProcStringReader.ProcFileIterator;
import com.android.internal.os.KernelCpuUidBpfMapReader.BpfMapIterator;

import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.CharBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

@@ -352,7 +348,7 @@ public abstract class KernelCpuUidTimeReader<T> {
        private int mFreqCount = 0;
        private int mErrors = 0;
        private boolean mPerClusterTimesAvailable;
        private boolean mAllUidTimesAvailable = true;
        private boolean mAllUidTimesAvailable;

        public KernelCpuUidFreqTimeReader(boolean throttle) {
            this(throttle, Clock.SYSTEM_CLOCK);
@@ -375,11 +371,23 @@ public abstract class KernelCpuUidTimeReader<T> {
            mProcFilePath = Paths.get(procFile);
        }

        /**
         * Initializes the reader.  Should be called during the system-ready boot phase.
         */
        public void onSystemReady() {
            if (mBpfTimesAvailable && mCpuFreqs == null) {
                readFreqsThroughBpf();
                // By extension: if we can read CPU frequencies through eBPF, we can also
                // read per-UID CPU time-in-state
                mAllUidTimesAvailable = mCpuFreqs != null;
            }
        }

        /**
         * @return Whether per-cluster times are available.
         */
        public boolean perClusterTimesAvailable() {
            return mPerClusterTimesAvailable;
            return mBpfTimesAvailable;
        }

        /**
@@ -396,59 +404,6 @@ public abstract class KernelCpuUidTimeReader<T> {
            return mLastTimes;
        }

        /**
         * Reads a list of CPU frequencies from /proc/uid_time_in_state. Uses a given PowerProfile
         * to determine if per-cluster times are available.
         *
         * @param powerProfile The PowerProfile to compare against.
         * @return A long[] of CPU frequencies in Hz.
         */
        public long[] readFreqs(@NonNull PowerProfile powerProfile) {
            checkNotNull(powerProfile);
            if (mCpuFreqs != null) {
                // No need to read cpu freqs more than once.
                return mCpuFreqs;
            }
            if (!mAllUidTimesAvailable) {
                return null;
            }
            if (mBpfTimesAvailable) {
                readFreqsThroughBpf();
            }
            if (mCpuFreqs == null) {
                final int oldMask = StrictMode.allowThreadDiskReadsMask();
                try (BufferedReader reader = Files.newBufferedReader(mProcFilePath)) {
                    if (readFreqs(reader.readLine()) == null) {
                        return null;
                    }
                } catch (IOException e) {
                    if (++mErrors >= MAX_ERROR_COUNT) {
                        mAllUidTimesAvailable = false;
                    }
                    Slog.e(mTag, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
                    return null;
                } finally {
                    StrictMode.setThreadPolicyMask(oldMask);
                }
            }
            // Check if the freqs in the proc file correspond to per-cluster freqs.
            final IntArray numClusterFreqs = extractClusterInfoFromProcFileFreqs();
            final int numClusters = powerProfile.getNumCpuClusters();
            if (numClusterFreqs.size() == numClusters) {
                mPerClusterTimesAvailable = true;
                for (int i = 0; i < numClusters; ++i) {
                    if (numClusterFreqs.get(i) != powerProfile.getNumSpeedStepsInCpuCluster(i)) {
                        mPerClusterTimesAvailable = false;
                        break;
                    }
                }
            } else {
                mPerClusterTimesAvailable = false;
            }
            Slog.i(mTag, "mPerClusterTimesAvailable=" + mPerClusterTimesAvailable);
            return mCpuFreqs;
        }

        private long[] readFreqsThroughBpf() {
            if (!mBpfTimesAvailable || mBpfReader == null) {
                return null;
+0 −18
Original line number Diff line number Diff line
@@ -821,24 +821,6 @@ public class PowerProfile {
        return 0;
    }

    /**
     * Description of a CPU power bracket: which cluster/frequency combinations are included.
     * @deprecated Use getCpuPowerBracketDescription
     */
    @Deprecated
    public String getCpuPowerBracketDescription(int powerBracket) {
        return "unsupported";
    }

    /**
     * Returns the CPU power bracket corresponding to the specified cluster and frequency step
     * @deprecated Use getCpuPowerBracketForScalingStep
     */
    @Deprecated
    public int getPowerBracketForCpuCore(int cluster, int step) {
        return 0;
    }

    private int mNumDisplays;

    private void initDisplays() {
+0 −62
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.internal.os;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
@@ -99,63 +98,6 @@ public class KernelCpuUidFreqTimeReaderTest {
        FileUtils.deleteContents(getContext().getFilesDir());
    }

    @Test
    public void testReadFreqs_perClusterTimesNotAvailable() throws Exception {
        final long[][] freqs = {
                {1, 12, 123, 1234},
                {1, 12, 123, 23, 123, 1234, 12345, 123456},
                {1, 12, 123, 23, 123, 1234, 12345, 123456, 12, 123, 12345},
                {1, 12, 123, 23, 2345, 234567}
        };
        final int[] numClusters = {2, 2, 3, 1};
        final int[][] numFreqs = {{3, 6}, {4, 5}, {3, 5, 4}, {3}};
        for (int i = 0; i < freqs.length; ++i) {
            mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
                    new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader, false);
            setCpuClusterFreqs(numClusters[i], numFreqs[i]);
            setFreqs(freqs[i]);
            long[] actualFreqs = mReader.readFreqs(mPowerProfile);
            assertArrayEquals(freqs[i], actualFreqs);
            final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
                    Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
            assertFalse(errMsg, mReader.perClusterTimesAvailable());

            // Verify that a second call won't re-read the freqs
            clearFreqsAndData();
            actualFreqs = mReader.readFreqs(mPowerProfile);
            assertArrayEquals(freqs[i], actualFreqs);
            assertFalse(errMsg, mReader.perClusterTimesAvailable());
        }
    }

    @Test
    public void testReadFreqs_perClusterTimesAvailable() throws Exception {
        final long[][] freqs = {
                {1, 12, 123, 1234},
                {1, 12, 123, 23, 123, 1234, 12345, 123456},
                {1, 12, 123, 23, 123, 1234, 12345, 123456, 12, 123, 12345, 1234567}
        };
        final int[] numClusters = {1, 2, 3};
        final int[][] numFreqs = {{4}, {3, 5}, {3, 5, 4}};
        for (int i = 0; i < freqs.length; ++i) {
            mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
                    new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader, false);
            setCpuClusterFreqs(numClusters[i], numFreqs[i]);
            setFreqs(freqs[i]);
            long[] actualFreqs = mReader.readFreqs(mPowerProfile);
            assertArrayEquals(freqs[i], actualFreqs);
            final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
                    Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
            assertTrue(errMsg, mReader.perClusterTimesAvailable());

            // Verify that a second call won't re-read the freqs
            clearFreqsAndData();
            actualFreqs = mReader.readFreqs(mPowerProfile);
            assertArrayEquals(freqs[i], actualFreqs);
            assertTrue(errMsg, mReader.perClusterTimesAvailable());
        }
    }

    @Test
    public void testReadDelta() throws Exception {
        final long[] freqs = {110, 123, 145, 167, 289, 997};
@@ -170,8 +112,6 @@ public class KernelCpuUidFreqTimeReaderTest {

        // Verify that readDelta also reads the frequencies if not already available.
        clearFreqsAndData();
        long[] actualFreqs = mReader.readFreqs(mPowerProfile);
        assertArrayEquals(freqs, actualFreqs);

        // Verify that a second call will only return deltas.
        mCallback.clear();
@@ -222,8 +162,6 @@ public class KernelCpuUidFreqTimeReaderTest {

        // Verify that readDelta also reads the frequencies if not already available.
        clearFreqsAndData();
        long[] actualFreqs = mReader.readFreqs(mPowerProfile);
        assertArrayEquals(freqs, actualFreqs);

        // Verify that a second call should still return absolute values
        mCallback.clear();
+0 −5
Original line number Diff line number Diff line
@@ -537,15 +537,10 @@ public class PowerProfileTest {
        return null;
    }

    private void assertEquals(int expected, int actual) {
        Assert.assertEquals(expected, actual);
    }

    private void assertEquals(double expected, double actual) {
        Assert.assertEquals(expected, actual, 0.1);
    }


    @Test
    public void powerBrackets_specifiedInPowerProfile() {
        mProfile.forceInitForTesting(mContext, R.xml.power_profile_test_power_brackets);
Loading