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

Commit aabb81ec authored by Lakshman Annadorai's avatar Lakshman Annadorai Committed by Cherrypicker Worker
Browse files

Update CpuInfoReader to compute normalized available CPU frequency.

- Guard against reading CpuInfos too frequently.
- Implement dump logic in CpuInfoReader.
- Guard against zero time value in the time_in_state file.

Test: atest CpuInfoReaderTest
Bug: 242722241
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:67bc88121775785907a3aebd87db7c4bdef5fff9)
Merged-In: I9fc109c43fe0bd5f1d16135ad3167325ae502e15
Change-Id: I9fc109c43fe0bd5f1d16135ad3167325ae502e15
parent f5549dfd
Loading
Loading
Loading
Loading
+146 −13
Original line number Diff line number Diff line
@@ -21,8 +21,10 @@ import static com.android.server.cpu.CpuMonitorService.TAG;

import android.annotation.IntDef;
import android.annotation.Nullable;
import android.os.SystemClock;
import android.system.Os;
import android.system.OsConstants;
import android.util.IndentingPrintWriter;
import android.util.IntArray;
import android.util.LongSparseLongArray;
import android.util.SparseArray;
@@ -50,6 +52,9 @@ public final class CpuInfoReader {
    private static final String POLICY_DIR_PREFIX = "policy";
    private static final String RELATED_CPUS_FILE = "related_cpus";
    private static final String AFFECTED_CPUS_FILE = "affected_cpus";
    // TODO(b/263154344): Avoid reading from cpuinfo_cur_freq because non-root users don't have
    //  read permission for this file. The file permissions are set by the Kernel. Instead, read
    //  the current frequency only from scaling_cur_freq.
    private static final String CUR_CPUFREQ_FILE = "cpuinfo_cur_freq";
    private static final String MAX_CPUFREQ_FILE = "cpuinfo_max_freq";
    private static final String CUR_SCALING_FREQ_FILE = "scaling_cur_freq";
@@ -70,16 +75,18 @@ public final class CpuInfoReader {
    private static final Pattern TIME_IN_STATE_PATTERN =
            Pattern.compile("(?<freqKHz>[0-9]+)\\s(?<time>[0-9]+)");
    private static final long MILLIS_PER_CLOCK_TICK = 1000L / Os.sysconf(OsConstants._SC_CLK_TCK);
    private static final long MIN_READ_INTERVAL_MILLISECONDS = 500;

    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = {"FLAG_CPUSET_CATEGORY_"}, flag = true, value = {
            FLAG_CPUSET_CATEGORY_TOP_APP,
            FLAG_CPUSET_CATEGORY_BACKGROUND
    })
    private @interface CpusetCategory{}
    /** package **/ @interface CpusetCategory{}

    // TODO(b/242722241): Protect updatable variables with a local lock.
    private final File mCpusetDir;
    private final long mMinReadIntervalMillis;
    private final SparseIntArray mCpusetCategoriesByCpus = new SparseIntArray();
    private final SparseArray<File> mCpuFreqPolicyDirsById = new SparseArray<>();
    private final SparseArray<StaticPolicyInfo> mStaticPolicyInfoById = new SparseArray<>();
@@ -90,16 +97,20 @@ public final class CpuInfoReader {
    private SparseArray<CpuUsageStats> mCumulativeCpuUsageStats = new SparseArray<>();
    private boolean mIsEnabled;
    private boolean mHasTimeInStateFile;
    private long mLastReadUptimeMillis;
    private SparseArray<CpuInfo> mLastReadCpuInfos;

    public CpuInfoReader() {
        this(new File(CPUSET_DIR_PATH), new File(CPUFREQ_DIR_PATH), new File(PROC_STAT_FILE_PATH));
        this(new File(CPUSET_DIR_PATH), new File(CPUFREQ_DIR_PATH), new File(PROC_STAT_FILE_PATH),
                MIN_READ_INTERVAL_MILLISECONDS);
    }

    @VisibleForTesting
    CpuInfoReader(File cpusetDir, File cpuFreqDir, File procStatFile) {
    CpuInfoReader(File cpusetDir, File cpuFreqDir, File procStatFile, long minReadIntervalMillis) {
        mCpusetDir = cpusetDir;
        mCpuFreqDir = cpuFreqDir;
        mProcStatFile = procStatFile;
        mMinReadIntervalMillis = minReadIntervalMillis;
    }

    /**
@@ -167,6 +178,16 @@ public final class CpuInfoReader {
        if (!mIsEnabled) {
            return null;
        }
        long uptimeMillis = SystemClock.uptimeMillis();
        if (mLastReadUptimeMillis > 0
                && uptimeMillis - mLastReadUptimeMillis < mMinReadIntervalMillis) {
            Slogf.w(TAG, "Skipping reading from device and returning the last read CpuInfos. "
                    + "Last read was %d ms ago, min read interval is %d ms",
                    uptimeMillis - mLastReadUptimeMillis, mMinReadIntervalMillis);
            return mLastReadCpuInfos;
        }
        mLastReadUptimeMillis = uptimeMillis;
        mLastReadCpuInfos = null;
        SparseArray<CpuUsageStats> cpuUsageStatsByCpus = readLatestCpuUsageStats();
        if (cpuUsageStatsByCpus == null || cpuUsageStatsByCpus.size() == 0) {
            Slogf.e(TAG, "Failed to read latest CPU usage stats");
@@ -202,6 +223,12 @@ public final class CpuInfoReader {
                        + " policy ID %d", policyId);
                continue;
            }
            if (curFreqKHz > maxFreqKHz) {
                Slogf.w(TAG, "Current CPU frequency (%d) is greater than maximum CPU frequency"
                        + " (%d) for policy ID (%d). Skipping CPU frequency policy", curFreqKHz,
                        maxFreqKHz, policyId);
                continue;
            }
            for (int coreIdx = 0; coreIdx < staticPolicyInfo.relatedCpuCores.size(); coreIdx++) {
                int relatedCpuCore = staticPolicyInfo.relatedCpuCores.get(coreIdx);
                CpuInfo prevCpuInfo = cpuInfoByCpus.get(relatedCpuCore);
@@ -241,9 +268,73 @@ public final class CpuInfoReader {
                }
            }
        }
        mLastReadCpuInfos = cpuInfoByCpus;
        return cpuInfoByCpus;
    }

    /** Dumps the current state. */
    public void dump(IndentingPrintWriter writer) {
        writer.printf("*%s*\n", getClass().getSimpleName());
        writer.increaseIndent();    // Add intend for the outermost block.

        writer.printf("mCpusetDir = %s\n", mCpusetDir.getAbsolutePath());
        writer.printf("mCpuFreqDir = %s\n", mCpuFreqDir.getAbsolutePath());
        writer.printf("mProcStatFile = %s\n", mProcStatFile.getAbsolutePath());
        writer.printf("mIsEnabled = %s\n", mIsEnabled);
        writer.printf("mHasTimeInStateFile = %s\n", mHasTimeInStateFile);
        writer.printf("mLastReadUptimeMillis = %d\n", mLastReadUptimeMillis);
        writer.printf("mMinReadIntervalMillis = %d\n", mMinReadIntervalMillis);

        writer.printf("Cpuset categories by CPU core:\n");
        writer.increaseIndent();
        for (int i = 0; i < mCpusetCategoriesByCpus.size(); i++) {
            writer.printf("CPU core id = %d, %s\n", mCpusetCategoriesByCpus.keyAt(i),
                    toCpusetCategoriesStr(mCpusetCategoriesByCpus.valueAt(i)));
        }
        writer.decreaseIndent();

        writer.println("Cpu frequency policy directories by policy id:");
        writer.increaseIndent();
        for (int i = 0; i < mCpuFreqPolicyDirsById.size(); i++) {
            writer.printf("Policy id = %d, Dir = %s\n", mCpuFreqPolicyDirsById.keyAt(i),
                    mCpuFreqPolicyDirsById.valueAt(i));
        }
        writer.decreaseIndent();

        writer.println("Static cpu frequency policy infos by policy id:");
        writer.increaseIndent();
        for (int i = 0; i < mStaticPolicyInfoById.size(); i++) {
            writer.printf("Policy id = %d, %s\n", mStaticPolicyInfoById.keyAt(i),
                    mStaticPolicyInfoById.valueAt(i));
        }
        writer.decreaseIndent();

        writer.println("Cpu time in frequency state by policy id:");
        writer.increaseIndent();
        for (int i = 0; i < mTimeInStateByPolicyId.size(); i++) {
            writer.printf("Policy id = %d, Time(millis) in state by CPU frequency(KHz) = %s\n",
                    mTimeInStateByPolicyId.keyAt(i), mTimeInStateByPolicyId.valueAt(i));
        }
        writer.decreaseIndent();

        writer.println("Last read CPU infos:");
        writer.increaseIndent();
        for (int i = 0; i < mLastReadCpuInfos.size(); i++) {
            writer.printf("%s\n", mLastReadCpuInfos.valueAt(i));
        }
        writer.decreaseIndent();

        writer.println("Latest cumulative CPU usage stats by CPU core:");
        writer.increaseIndent();
        for (int i = 0; i < mCumulativeCpuUsageStats.size(); i++) {
            writer.printf("CPU core id = %d, %s\n", mCumulativeCpuUsageStats.keyAt(i),
                    mCumulativeCpuUsageStats.valueAt(i));
        }
        writer.decreaseIndent();

        writer.decreaseIndent();    // Remove intend for the outermost block.
    }

    /**
     * Sets the CPU frequency for testing.
     *
@@ -496,6 +587,9 @@ public final class CpuInfoReader {
        for (int i = 0; i < timeInState.size(); i++) {
            totalTimeInState += timeInState.valueAt(i);
        }
        if (totalTimeInState == 0) {
            return CpuInfo.MISSING_FREQUENCY;
        }
        double avgFreqKHz = 0;
        for (int i = 0; i < timeInState.size(); i++) {
            avgFreqKHz += (timeInState.keyAt(i) * timeInState.valueAt(i)) / totalTimeInState;
@@ -624,16 +718,29 @@ public final class CpuInfoReader {
        @CpusetCategory
        public final int cpusetCategories;
        public final boolean isOnline;
        public final long maxCpuFreqKHz;
        // Values in the below fields may be missing when a CPU core is offline.
        public final long curCpuFreqKHz;
        public final long maxCpuFreqKHz;
        public final long avgTimeInStateCpuFreqKHz;
        @Nullable
        public final CpuUsageStats latestCpuUsageStats;

        private long mNormalizedAvailableCpuFreqKHz;

        CpuInfo(int cpuCore, @CpusetCategory int cpusetCategories, boolean isOnline,
                long curCpuFreqKHz, long maxCpuFreqKHz, long avgTimeInStateCpuFreqKHz,
                CpuUsageStats latestCpuUsageStats) {
            this(cpuCore, cpusetCategories, isOnline, curCpuFreqKHz, maxCpuFreqKHz,
                    avgTimeInStateCpuFreqKHz, /* normalizedAvailableCpuFreqKHz= */ 0,
                    latestCpuUsageStats);
            this.mNormalizedAvailableCpuFreqKHz = computeNormalizedAvailableCpuFreqKHz();
        }

        // Should be used only for testing.
        @VisibleForTesting
        CpuInfo(int cpuCore, @CpusetCategory int cpusetCategories, boolean isOnline,
                long curCpuFreqKHz, long maxCpuFreqKHz, long avgTimeInStateCpuFreqKHz,
                long normalizedAvailableCpuFreqKHz, CpuUsageStats latestCpuUsageStats) {
            this.cpuCore = cpuCore;
            this.cpusetCategories = cpusetCategories;
            this.isOnline = isOnline;
@@ -641,6 +748,11 @@ public final class CpuInfoReader {
            this.maxCpuFreqKHz = maxCpuFreqKHz;
            this.avgTimeInStateCpuFreqKHz = avgTimeInStateCpuFreqKHz;
            this.latestCpuUsageStats = latestCpuUsageStats;
            this.mNormalizedAvailableCpuFreqKHz = normalizedAvailableCpuFreqKHz;
        }

        public long getNormalizedAvailableCpuFreqKHz() {
            return mNormalizedAvailableCpuFreqKHz;
        }

        @Override
@@ -657,6 +769,8 @@ public final class CpuInfoReader {
                    .append(avgTimeInStateCpuFreqKHz == MISSING_FREQUENCY ? "missing"
                            : avgTimeInStateCpuFreqKHz)
                    .append(", latestCpuUsageStats = ").append(latestCpuUsageStats)
                    .append(", mNormalizedAvailableCpuFreqKHz = ")
                    .append(mNormalizedAvailableCpuFreqKHz)
                    .append(" }").toString();
        }

@@ -673,13 +787,32 @@ public final class CpuInfoReader {
                    && isOnline == other.isOnline  && curCpuFreqKHz == other.curCpuFreqKHz
                    && maxCpuFreqKHz == other.maxCpuFreqKHz
                    && avgTimeInStateCpuFreqKHz == other.avgTimeInStateCpuFreqKHz
                    && latestCpuUsageStats.equals(other.latestCpuUsageStats);
                    && latestCpuUsageStats.equals(other.latestCpuUsageStats)
                    && mNormalizedAvailableCpuFreqKHz == other.mNormalizedAvailableCpuFreqKHz;
        }

        @Override
        public int hashCode() {
            return Objects.hash(cpuCore, cpusetCategories, isOnline, curCpuFreqKHz, maxCpuFreqKHz,
                    avgTimeInStateCpuFreqKHz, latestCpuUsageStats);
                    avgTimeInStateCpuFreqKHz, latestCpuUsageStats, mNormalizedAvailableCpuFreqKHz);
        }

        private long computeNormalizedAvailableCpuFreqKHz() {
            if (!isOnline) {
                return MISSING_FREQUENCY;
            }
            long totalTimeMillis = latestCpuUsageStats.getTotalTimeMillis();
            if (totalTimeMillis == 0) {
                Slogf.wtf(TAG, "Total CPU time millis is 0. This shouldn't happen unless stats are"
                        + " polled too frequently");
                return MISSING_FREQUENCY;
            }
            double nonIdlePercent = 100.0 * (totalTimeMillis
                    - (double) latestCpuUsageStats.idleTimeMillis) / totalTimeMillis;
            long curFreqKHz = avgTimeInStateCpuFreqKHz == MISSING_FREQUENCY
                    ? curCpuFreqKHz : avgTimeInStateCpuFreqKHz;
            double availablePercent = 100.0 - (nonIdlePercent * curFreqKHz / maxCpuFreqKHz);
            return (long) ((availablePercent * maxCpuFreqKHz) / 100.0);
        }
    }

@@ -712,7 +845,7 @@ public final class CpuInfoReader {
            this.guestNiceTimeMillis = guestNiceTimeMillis;
        }

        public long getTotalTime() {
        public long getTotalTimeMillis() {
            return userTimeMillis + niceTimeMillis + systemTimeMillis + idleTimeMillis
                    + iowaitTimeMillis + irqTimeMillis + softirqTimeMillis + stealTimeMillis
                    + guestTimeMillis + guestNiceTimeMillis;
@@ -796,8 +929,8 @@ public final class CpuInfoReader {

        @Override
        public String toString() {
            return "FrequencyPair{cpuFreqKHz=" + cpuFreqKHz + ", scalingFreqKHz=" + scalingFreqKHz
                    + '}';
            return "FrequencyPair{cpuFreqKHz = " + cpuFreqKHz + ", scalingFreqKHz = "
                    + scalingFreqKHz + '}';
        }
    }

+44 −35

File changed.

Preview size limit exceeded, changes collapsed.