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

Commit 2a523ecc authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes from topic "cherrypicker-L08300000959200714:N79700001348406342" into udc-dev

* changes:
  Start/Stop CPU monitoring based on the client callback addition/removal.
  Verify CPU availability against client thresholds and notify the clients.
  Monitor CPU availability using CPU frequency stats.
  Update CpuInfoReader to compute normalized available CPU frequency.
parents 2e196a41 b9e7f3dd
Loading
Loading
Loading
Loading
+50 −13
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ import static com.android.server.cpu.CpuAvailabilityMonitoringConfig.CPUSET_BACK

import com.android.internal.util.Preconditions;

import java.util.Objects;

/** CPU availability information. */
public final class CpuAvailabilityInfo {
    /** Constant to indicate missing CPU availability percent. */
@@ -35,29 +37,64 @@ public final class CpuAvailabilityInfo {
    @CpuAvailabilityMonitoringConfig.Cpuset
    public final int cpuset;

    /** Uptime (in milliseconds) when the data in this object was captured. */
    public final long dataTimestampUptimeMillis;

    /** The latest average CPU availability percent. */
    public final int latestAvgAvailabilityPercent;

    /** The past N-second average CPU availability percent. */
    public final int pastNSecAvgAvailabilityPercent;
    /**
     * The past N-millisecond average CPU availability percent.
     *
     * <p>When there is not enough data to calculate the past N-millisecond average, this field will
     * contain the value {@link MISSING_CPU_AVAILABILITY_PERCENT}.
     */
    public final int pastNMillisAvgAvailabilityPercent;

    /** The duration over which the {@link pastNSecAvgAvailabilityPercent} was calculated. */
    public final int avgAvailabilityDurationSec;
    /** The duration over which the {@link pastNMillisAvgAvailabilityPercent} was calculated. */
    public final long pastNMillisDuration;

    @Override
    public String toString() {
        return "CpuAvailabilityInfo{" + "cpuset=" + cpuset + ", latestAvgAvailabilityPercent="
                + latestAvgAvailabilityPercent + ", pastNSecAvgAvailabilityPercent="
                + pastNSecAvgAvailabilityPercent + ", avgAvailabilityDurationSec="
                + avgAvailabilityDurationSec + '}';
        return "CpuAvailabilityInfo{" + "cpuset = " + cpuset + ", dataTimestampUptimeMillis = "
                + dataTimestampUptimeMillis + ", latestAvgAvailabilityPercent = "
                + latestAvgAvailabilityPercent + ", pastNMillisAvgAvailabilityPercent = "
                + pastNMillisAvgAvailabilityPercent + ", pastNMillisDuration = "
                + pastNMillisDuration + '}';
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof CpuAvailabilityInfo)) {
            return false;
        }
        CpuAvailabilityInfo info = (CpuAvailabilityInfo) obj;
        return cpuset == info.cpuset && dataTimestampUptimeMillis == info.dataTimestampUptimeMillis
                && latestAvgAvailabilityPercent == info.latestAvgAvailabilityPercent
                && pastNMillisAvgAvailabilityPercent == info.pastNMillisAvgAvailabilityPercent
                && pastNMillisDuration == info.pastNMillisDuration;
    }

    @Override
    public int hashCode() {
        return Objects.hash(cpuset, dataTimestampUptimeMillis, latestAvgAvailabilityPercent,
                pastNMillisAvgAvailabilityPercent, pastNMillisDuration);
    }

    CpuAvailabilityInfo(int cpuset, int latestAvgAvailabilityPercent,
            int pastNSecAvgAvailabilityPercent, int avgAvailabilityDurationSec) {
    CpuAvailabilityInfo(int cpuset, long dataTimestampUptimeMillis,
            int latestAvgAvailabilityPercent, int pastNMillisAvgAvailabilityPercent,
            long pastNMillisDuration) {
        this.cpuset = Preconditions.checkArgumentInRange(cpuset, CPUSET_ALL, CPUSET_BACKGROUND,
                "cpuset");
        this.latestAvgAvailabilityPercent = latestAvgAvailabilityPercent;
        this.pastNSecAvgAvailabilityPercent = pastNSecAvgAvailabilityPercent;
        this.avgAvailabilityDurationSec = avgAvailabilityDurationSec;
        this.dataTimestampUptimeMillis =
                Preconditions.checkArgumentNonnegative(dataTimestampUptimeMillis);
        this.latestAvgAvailabilityPercent = Preconditions.checkArgumentNonnegative(
                latestAvgAvailabilityPercent);
        this.pastNMillisAvgAvailabilityPercent = pastNMillisAvgAvailabilityPercent;
        this.pastNMillisDuration = Preconditions.checkArgumentNonnegative(
                pastNMillisDuration);
    }
}
+13 −2
Original line number Diff line number Diff line
@@ -90,8 +90,19 @@ public final class CpuAvailabilityMonitoringConfig {

    @Override
    public String toString() {
        return "CpuAvailabilityMonitoringConfig{cpuset=" + cpuset + ", mThresholds=" + mThresholds
                + ')';
        return "CpuAvailabilityMonitoringConfig{cpuset=" + toCpusetString(cpuset) + ", mThresholds="
                + mThresholds + ')';
    }

    /** Returns the string equivalent of the provided cpuset. */
    public static String toCpusetString(int cpuset) {
        switch (cpuset) {
            case CPUSET_ALL:
                return "CPUSET_ALL";
            case CPUSET_BACKGROUND:
                return "CPUSET_BACKGROUND";
        }
        return "Invalid cpuset: " + cpuset;
    }

    private CpuAvailabilityMonitoringConfig(Builder builder) {
+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 + '}';
        }
    }

+546 −48

File changed.

Preview size limit exceeded, changes collapsed.

+44 −35

File changed.

Preview size limit exceeded, changes collapsed.

Loading