diff options
3 files changed, 699 insertions, 427 deletions
diff --git a/services/core/java/com/android/server/cpu/CpuInfoReader.java b/services/core/java/com/android/server/cpu/CpuInfoReader.java index b13d7533f29b..ca97a9847b39 100644 --- a/services/core/java/com/android/server/cpu/CpuInfoReader.java +++ b/services/core/java/com/android/server/cpu/CpuInfoReader.java @@ -16,11 +16,15 @@ package com.android.server.cpu; +import static com.android.server.cpu.CpuMonitorService.DEBUG; +import static com.android.server.cpu.CpuMonitorService.TAG; + import android.annotation.IntDef; import android.annotation.Nullable; import android.system.Os; import android.system.OsConstants; -import android.util.ArrayMap; +import android.util.IntArray; +import android.util.LongSparseLongArray; import android.util.SparseArray; import android.util.SparseIntArray; @@ -31,8 +35,7 @@ import java.io.File; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.file.Files; -import java.util.ArrayList; -import java.util.Collections; +import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.regex.Matcher; @@ -40,7 +43,6 @@ import java.util.regex.Pattern; /** Reader to read CPU information from proc and sys fs files exposed by the Kernel. */ public final class CpuInfoReader { - static final String TAG = CpuInfoReader.class.getSimpleName(); static final int FLAG_CPUSET_CATEGORY_TOP_APP = 1 << 0; static final int FLAG_CPUSET_CATEGORY_BACKGROUND = 1 << 1; @@ -67,7 +69,7 @@ public final class CpuInfoReader { + "(?<guestNiceClockTicks>[0-9]+)"); private static final Pattern TIME_IN_STATE_PATTERN = Pattern.compile("(?<freqKHz>[0-9]+)\\s(?<time>[0-9]+)"); - private static final long MILLIS_PER_JIFFY = 1000L / Os.sysconf(OsConstants._SC_CLK_TCK); + private static final long MILLIS_PER_CLOCK_TICK = 1000L / Os.sysconf(OsConstants._SC_CLK_TCK); @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"FLAG_CPUSET_CATEGORY_"}, flag = true, value = { @@ -76,14 +78,15 @@ public final class CpuInfoReader { }) private @interface CpusetCategory{} + // TODO(b/242722241): Protect updatable variables with a local lock. private final File mCpusetDir; private final SparseIntArray mCpusetCategoriesByCpus = new SparseIntArray(); - private final SparseArray<Long> mMaxCpuFrequenciesByCpus = new SparseArray<>(); - private final ArrayMap<String, ArrayMap<Long, Long>> mTimeInStateByPolicy = new ArrayMap<>(); + private final SparseArray<File> mCpuFreqPolicyDirsById = new SparseArray<>(); + private final SparseArray<StaticPolicyInfo> mStaticPolicyInfoById = new SparseArray<>(); + private final SparseArray<LongSparseLongArray> mTimeInStateByPolicyId = new SparseArray<>(); private File mCpuFreqDir; private File mProcStatFile; - private File[] mCpuFreqPolicyDirs; private SparseArray<CpuUsageStats> mCumulativeCpuUsageStats = new SparseArray<>(); private boolean mIsEnabled; private boolean mHasTimeInStateFile; @@ -99,88 +102,206 @@ public final class CpuInfoReader { mProcStatFile = procStatFile; } - /** Inits CpuInfoReader and returns a boolean to indicate whether the reader is enabled. */ + /** + * Initializes CpuInfoReader and returns a boolean to indicate whether the reader is enabled. + */ public boolean init() { - mCpuFreqPolicyDirs = mCpuFreqDir.listFiles( + if (mCpuFreqPolicyDirsById.size() > 0) { + Slogf.w(TAG, "Ignoring duplicate CpuInfoReader init request"); + return mIsEnabled; + } + File[] policyDirs = mCpuFreqDir.listFiles( file -> file.isDirectory() && file.getName().startsWith(POLICY_DIR_PREFIX)); - if (mCpuFreqPolicyDirs == null || mCpuFreqPolicyDirs.length == 0) { + if (policyDirs == null || policyDirs.length == 0) { Slogf.w(TAG, "Missing CPU frequency policy directories at %s", mCpuFreqDir.getAbsolutePath()); return false; } + populateCpuFreqPolicyDirsById(policyDirs); + if (mCpuFreqPolicyDirsById.size() == 0) { + Slogf.e(TAG, "Failed to parse CPU frequency policy directory paths: %s", + Arrays.toString(policyDirs)); + return false; + } + readStaticPolicyInfo(); + if (mStaticPolicyInfoById.size() == 0) { + Slogf.e(TAG, "Failed to read static CPU frequency policy info from policy dirs: %s", + Arrays.toString(policyDirs)); + return false; + } if (!mProcStatFile.exists()) { Slogf.e(TAG, "Missing proc stat file at %s", mProcStatFile.getAbsolutePath()); return false; } readCpusetCategories(); if (mCpusetCategoriesByCpus.size() == 0) { - Slogf.e(TAG, "Failed to read cpuset information read from %s", - mCpusetDir.getAbsolutePath()); + Slogf.e(TAG, "Failed to read cpuset information from %s", mCpusetDir.getAbsolutePath()); return false; } - readMaxCpuFrequencies(); - if (mMaxCpuFrequenciesByCpus.size() == 0) { - Slogf.e(TAG, "Failed to read max CPU frequencies from policy directories at %s", - mCpuFreqDir.getAbsolutePath()); - return false; + // Certain CPU performance scaling drivers, such as intel_pstate, perform their own CPU + // frequency transitions and do not supply this information to the Kernel's cpufreq node. + // Thus, the `time_in_state` file won't be available on devices running such scaling + // drivers. Check the presence of this file only once during init and do not throw error + // when this file is missing. The implementation must accommodate such use cases. + for (int i = 0; i < mCpuFreqPolicyDirsById.size() && !mHasTimeInStateFile; ++i) { + // If all the CPU cores on a policy are offline, this file might be missing for the + // policy. Make sure this file is not available on all policies before marking it as + // missing. + mHasTimeInStateFile |= new File(mCpuFreqPolicyDirsById.valueAt(i), TIME_IN_STATE_FILE) + .exists(); + } + if (!mHasTimeInStateFile) { + Slogf.e(TAG, "Time in state file not available for any cpufreq policy"); } - - // The Kernel must be configured to generate the `time_in_state` file. If the Kernel is not - // configured to generate this file, this file won't be available for the entire system - // uptime. Thus, check for the presence of this file only during init. - mHasTimeInStateFile = new File(mCpuFreqPolicyDirs[0], TIME_IN_STATE_FILE).exists(); mIsEnabled = true; return true; } - /** Reads CPU information from proc and sys fs files exposed by the Kernel. */ - public List<CpuInfo> readCpuInfos() { + /** + * Reads CPU information from proc and sys fs files exposed by the Kernel. + * + * @return SparseArray keyed by CPU core ID; {@code null} on error or when disabled. + */ + @Nullable + public SparseArray<CpuInfo> readCpuInfos() { if (!mIsEnabled) { - return Collections.emptyList(); + return null; } - SparseArray<CpuUsageStats> latestCpuUsageStats = readLatestCpuUsageStats(); - if (latestCpuUsageStats == null) { + SparseArray<CpuUsageStats> cpuUsageStatsByCpus = readLatestCpuUsageStats(); + if (cpuUsageStatsByCpus == null || cpuUsageStatsByCpus.size() == 0) { Slogf.e(TAG, "Failed to read latest CPU usage stats"); - return Collections.emptyList(); - } - SparseArray<Long> cpuFrequenciesByCpus = readCurrentCpuFrequencies(); - List<CpuInfo> cpuInfos = new ArrayList<>(); - for (int i = 0; i < cpuFrequenciesByCpus.size(); i++) { - int cpu = cpuFrequenciesByCpus.keyAt(i); - long curFrequency = cpuFrequenciesByCpus.valueAt(i); - if (!mMaxCpuFrequenciesByCpus.contains(cpu) || !latestCpuUsageStats.contains(cpu)) { - Slogf.w(TAG, "Missing max CPU frequency or CPU usage stats for CPU core %d", cpu); + return null; + } + SparseArray<DynamicPolicyInfo> dynamicPolicyInfoById = readDynamicPolicyInfo(); + if (dynamicPolicyInfoById.size() == 0) { + Slogf.e(TAG, "Failed to read dynamic policy infos"); + return null; + } + SparseArray<CpuInfo> cpuInfoByCpus = new SparseArray<>(); + for (int i = 0; i < mStaticPolicyInfoById.size(); i++) { + int policyId = mStaticPolicyInfoById.keyAt(i); + StaticPolicyInfo staticPolicyInfo = mStaticPolicyInfoById.valueAt(i); + DynamicPolicyInfo dynamicPolicyInfo = dynamicPolicyInfoById.get(policyId); + if (dynamicPolicyInfo == null) { + Slogf.w(TAG, "Missing dynamic policy info for policy ID %d", policyId); continue; } - int cpuCategories = mCpusetCategoriesByCpus.get(cpu, -1); - if (cpuCategories < 0) { - Slogf.w(TAG, "Missing cpuset information for CPU core %d", cpu); + long curFreqKHz = CpuInfo.MISSING_FREQUENCY; + long maxFreqKHz = CpuInfo.MISSING_FREQUENCY; + if (dynamicPolicyInfo.curCpuFreqPair.cpuFreqKHz != CpuInfo.MISSING_FREQUENCY + && staticPolicyInfo.maxCpuFreqPair.cpuFreqKHz != CpuInfo.MISSING_FREQUENCY) { + curFreqKHz = dynamicPolicyInfo.curCpuFreqPair.cpuFreqKHz; + maxFreqKHz = staticPolicyInfo.maxCpuFreqPair.cpuFreqKHz; + } else if (dynamicPolicyInfo.curCpuFreqPair.scalingFreqKHz != CpuInfo.MISSING_FREQUENCY + && staticPolicyInfo.maxCpuFreqPair.scalingFreqKHz + != CpuInfo.MISSING_FREQUENCY) { + curFreqKHz = dynamicPolicyInfo.curCpuFreqPair.scalingFreqKHz; + maxFreqKHz = staticPolicyInfo.maxCpuFreqPair.scalingFreqKHz; + } else { + Slogf.w(TAG, "Current and maximum CPU frequency information mismatch/missing for" + + " policy ID %d", policyId); continue; } - cpuInfos.add(new CpuInfo(cpu, cpuCategories, curFrequency, - mMaxCpuFrequenciesByCpus.get(cpu), latestCpuUsageStats.get(cpu))); + for (int coreIdx = 0; coreIdx < staticPolicyInfo.relatedCpuCores.size(); coreIdx++) { + int relatedCpuCore = staticPolicyInfo.relatedCpuCores.get(coreIdx); + CpuInfo prevCpuInfo = cpuInfoByCpus.get(relatedCpuCore); + if (prevCpuInfo != null) { + Slogf.wtf(TAG, "CPU info already available for the CPU core %d", + relatedCpuCore); + if (prevCpuInfo.isOnline) { + continue; + } + } + int cpusetCategories = mCpusetCategoriesByCpus.get(relatedCpuCore, -1); + if (cpusetCategories < 0) { + Slogf.w(TAG, "Missing cpuset information for the CPU core %d", + relatedCpuCore); + continue; + } + CpuUsageStats usageStats = cpuUsageStatsByCpus.get(relatedCpuCore); + if (dynamicPolicyInfo.affectedCpuCores.indexOf(relatedCpuCore) < 0) { + cpuInfoByCpus.append(relatedCpuCore, new CpuInfo(relatedCpuCore, + cpusetCategories, /* isOnline= */false, CpuInfo.MISSING_FREQUENCY, + maxFreqKHz, CpuInfo.MISSING_FREQUENCY, usageStats)); + continue; + } + // If a CPU core is online, it must have the usage stats. When the usage stats is + // missing, drop the core's CPU info. + if (usageStats == null) { + Slogf.w(TAG, "Missing CPU usage information for online CPU core %d", + relatedCpuCore); + continue; + } + CpuInfo cpuInfo = new CpuInfo(relatedCpuCore, cpusetCategories, /* isOnline= */true, + curFreqKHz, maxFreqKHz, dynamicPolicyInfo.avgTimeInStateCpuFreqKHz, + usageStats); + cpuInfoByCpus.append(relatedCpuCore, cpuInfo); + if (DEBUG) { + Slogf.d(TAG, "Added %s for CPU core %d", cpuInfo, relatedCpuCore); + } + } } - return cpuInfos; + return cpuInfoByCpus; } + /** + * Sets the CPU frequency for testing. + * + * <p>Return {@code true} on success. Otherwise, returns {@code false}. + */ @VisibleForTesting - void setCpuFreqDir(File cpuFreqDir) { + boolean setCpuFreqDir(File cpuFreqDir) { File[] cpuFreqPolicyDirs = cpuFreqDir.listFiles( file -> file.isDirectory() && file.getName().startsWith(POLICY_DIR_PREFIX)); - if (mCpuFreqPolicyDirs == null || mCpuFreqPolicyDirs.length == 0) { + if (cpuFreqPolicyDirs == null || cpuFreqPolicyDirs.length == 0) { Slogf.w(TAG, "Failed to set CPU frequency directory. Missing policy directories at %s", - mCpuFreqDir.getAbsolutePath()); - return; + cpuFreqDir.getAbsolutePath()); + return false; + } + populateCpuFreqPolicyDirsById(cpuFreqPolicyDirs); + int numCpuFreqPolicyDirs = mCpuFreqPolicyDirsById.size(); + int numStaticPolicyInfos = mStaticPolicyInfoById.size(); + if (numCpuFreqPolicyDirs == 0 || numCpuFreqPolicyDirs != numStaticPolicyInfos) { + Slogf.e(TAG, "Failed to set CPU frequency directory to %s. Total CPU frequency " + + "policies (%d) under new path is either 0 or not equal to initial " + + "total CPU frequency policies. Clearing CPU frequency policy " + + "directories", cpuFreqDir.getAbsolutePath(), numCpuFreqPolicyDirs, + numStaticPolicyInfos); + mCpuFreqPolicyDirsById.clear(); + return false; } mCpuFreqDir = cpuFreqDir; - mCpuFreqPolicyDirs = cpuFreqPolicyDirs; - Slogf.i(TAG, "Set CPU frequency directory to %s", cpuFreqDir.getAbsolutePath()); + return true; } + /** + * Sets the proc stat file for testing. + * + * <p>Return true on success. Otherwise, returns false. + */ @VisibleForTesting - void setProcStatFile(File procStatFile) { + boolean setProcStatFile(File procStatFile) { + if (!procStatFile.exists()) { + Slogf.e(TAG, "Missing proc stat file at %s", procStatFile.getAbsolutePath()); + return false; + } mProcStatFile = procStatFile; - Slogf.i(TAG, "Set proc stat file to %s", procStatFile.getAbsolutePath()); + return true; + } + + private void populateCpuFreqPolicyDirsById(File[] policyDirs) { + mCpuFreqPolicyDirsById.clear(); + for (int i = 0; i < policyDirs.length; i++) { + File policyDir = policyDirs[i]; + String policyIdStr = policyDir.getName().substring(POLICY_DIR_PREFIX.length()); + if (policyIdStr.isEmpty()) { + continue; + } + mCpuFreqPolicyDirsById.append(Integer.parseInt(policyIdStr), policyDir); + if (DEBUG) { + Slogf.d(TAG, "Cached policy directory %s for policy id %s", policyDir, policyIdStr); + } + } } private void readCpusetCategories() { @@ -200,11 +321,13 @@ public final class CpuInfoReader { cpusetCategory = FLAG_CPUSET_CATEGORY_BACKGROUND; break; default: + // Ignore other cpuset categories because the implementation doesn't support + // monitoring CPU availability for other cpusets. continue; } File cpuCoresFile = new File(dir.getPath(), CPUS_FILE); - List<Integer> cpuCores = readCpuCores(cpuCoresFile); - if (cpuCores.isEmpty()) { + IntArray cpuCores = readCpuCores(cpuCoresFile); + if (cpuCores == null || cpuCores.size() == 0) { Slogf.e(TAG, "Failed to read CPU cores from %s", cpuCoresFile.getAbsolutePath()); continue; } @@ -212,80 +335,104 @@ public final class CpuInfoReader { int categories = mCpusetCategoriesByCpus.get(cpuCores.get(j)); categories |= cpusetCategory; mCpusetCategoriesByCpus.append(cpuCores.get(j), categories); + if (DEBUG) { + Slogf.d(TAG, "Mapping CPU core id %d with cpuset categories [%s]", + cpuCores.get(j), toCpusetCategoriesStr(categories)); + } } } } - private void readMaxCpuFrequencies() { - for (int i = 0; i < mCpuFreqPolicyDirs.length; i++) { - File policyDir = mCpuFreqPolicyDirs[i]; - long maxCpuFreqKHz = readMaxCpuFrequency(policyDir); - if (maxCpuFreqKHz == 0) { - Slogf.w(TAG, "Invalid max CPU frequency read from %s", policyDir.getAbsolutePath()); + private void readStaticPolicyInfo() { + for (int i = 0; i < mCpuFreqPolicyDirsById.size(); i++) { + int policyId = mCpuFreqPolicyDirsById.keyAt(i); + File policyDir = mCpuFreqPolicyDirsById.valueAt(i); + FrequencyPair maxCpuFreqPair = readMaxCpuFrequency(policyDir); + if (maxCpuFreqPair.isEmpty()) { + Slogf.w(TAG, "Missing max CPU frequency information at %s", + policyDir.getAbsolutePath()); continue; } File cpuCoresFile = new File(policyDir, RELATED_CPUS_FILE); - List<Integer> cpuCores = readCpuCores(cpuCoresFile); - if (cpuCores.isEmpty()) { - Slogf.e(TAG, "Failed to read CPU cores from %s", cpuCoresFile.getAbsolutePath()); + IntArray relatedCpuCores = readCpuCores(cpuCoresFile); + if (relatedCpuCores == null || relatedCpuCores.size() == 0) { + Slogf.e(TAG, "Failed to read related CPU cores from %s", + cpuCoresFile.getAbsolutePath()); continue; } - for (int j = 0; j < cpuCores.size(); j++) { - mMaxCpuFrequenciesByCpus.append(cpuCores.get(j), maxCpuFreqKHz); + StaticPolicyInfo staticPolicyInfo = new StaticPolicyInfo(maxCpuFreqPair, + relatedCpuCores); + mStaticPolicyInfoById.append(policyId, staticPolicyInfo); + if (DEBUG) { + Slogf.d(TAG, "Added static policy info %s for policy id %d", staticPolicyInfo, + policyId); } } } - private long readMaxCpuFrequency(File policyDir) { - long curCpuFreqKHz = readCpuFreqKHz(new File(policyDir, MAX_CPUFREQ_FILE)); - return curCpuFreqKHz > 0 ? curCpuFreqKHz - : readCpuFreqKHz(new File(policyDir, MAX_SCALING_FREQ_FILE)); + private FrequencyPair readMaxCpuFrequency(File policyDir) { + return new FrequencyPair(readCpuFreqKHz(new File(policyDir, MAX_CPUFREQ_FILE)), + readCpuFreqKHz(new File(policyDir, MAX_SCALING_FREQ_FILE))); } - private SparseArray<Long> readCurrentCpuFrequencies() { - SparseArray<Long> curCpuFrequenciesByCpus = new SparseArray<>(); - for (int i = 0; i < mCpuFreqPolicyDirs.length; i++) { - File policyDir = mCpuFreqPolicyDirs[i]; - long curCpuFreqKHz = readCurrentCpuFrequency(policyDir); - if (curCpuFreqKHz == 0) { + private SparseArray<DynamicPolicyInfo> readDynamicPolicyInfo() { + SparseArray<DynamicPolicyInfo> dynamicPolicyInfoById = new SparseArray<>(); + for (int i = 0; i < mCpuFreqPolicyDirsById.size(); i++) { + int policyId = mCpuFreqPolicyDirsById.keyAt(i); + File policyDir = mCpuFreqPolicyDirsById.valueAt(i); + FrequencyPair curCpuFreqPair = readCurrentCpuFrequency(policyDir); + if (curCpuFreqPair.isEmpty()) { Slogf.w(TAG, "Missing current frequency information at %s", policyDir.getAbsolutePath()); continue; } + long avgTimeInStateCpuFreqKHz = readAvgTimeInStateCpuFrequency(policyId, policyDir); File cpuCoresFile = new File(policyDir, AFFECTED_CPUS_FILE); - List<Integer> cpuCores = readCpuCores(cpuCoresFile); - if (cpuCores.isEmpty()) { + IntArray affectedCpuCores = readCpuCores(cpuCoresFile); + if (affectedCpuCores == null || affectedCpuCores.size() == 0) { Slogf.e(TAG, "Failed to read CPU cores from %s", cpuCoresFile.getAbsolutePath()); continue; } - for (int j = 0; j < cpuCores.size(); j++) { - curCpuFrequenciesByCpus.append(cpuCores.get(j), curCpuFreqKHz); + DynamicPolicyInfo dynamicPolicyInfo = new DynamicPolicyInfo(curCpuFreqPair, + avgTimeInStateCpuFreqKHz, affectedCpuCores); + dynamicPolicyInfoById.append(policyId, dynamicPolicyInfo); + if (DEBUG) { + Slogf.d(TAG, "Read dynamic policy info %s for policy id %d", dynamicPolicyInfo, + policyId); } } - return curCpuFrequenciesByCpus; + return dynamicPolicyInfoById; + } + + private FrequencyPair readCurrentCpuFrequency(File policyDir) { + return new FrequencyPair(readCpuFreqKHz(new File(policyDir, CUR_CPUFREQ_FILE)), + readCpuFreqKHz(new File(policyDir, CUR_SCALING_FREQ_FILE))); } - private long readCurrentCpuFrequency(File policyDir) { - ArrayMap<Long, Long> latestTimeInState = readTimeInState(policyDir); - if (latestTimeInState == null) { - long curCpuFreqKHz = readCpuFreqKHz(new File(policyDir, CUR_CPUFREQ_FILE)); - return curCpuFreqKHz > 0 ? curCpuFreqKHz : - readCpuFreqKHz(new File(policyDir, CUR_SCALING_FREQ_FILE)); - } - String policyDirName = policyDir.getName(); - if (mTimeInStateByPolicy.containsKey(policyDirName)) { - ArrayMap<Long, Long> prevTimeInState = mTimeInStateByPolicy.get(policyDirName); - ArrayMap<Long, Long> deltaTimeInState = - calculateDeltaTimeInState(prevTimeInState, latestTimeInState); - mTimeInStateByPolicy.put(policyDirName, latestTimeInState); - return calculateAvgCpuFreq(deltaTimeInState); - } - mTimeInStateByPolicy.put(policyDirName, latestTimeInState); - return calculateAvgCpuFreq(latestTimeInState); + private long readAvgTimeInStateCpuFrequency(int policyId, File policyDir) { + LongSparseLongArray latestTimeInState = readTimeInState(policyDir); + if (latestTimeInState == null || latestTimeInState.size() == 0) { + return CpuInfo.MISSING_FREQUENCY; + } + LongSparseLongArray prevTimeInState = mTimeInStateByPolicyId.get(policyId); + if (prevTimeInState == null) { + mTimeInStateByPolicyId.put(policyId, latestTimeInState); + if (DEBUG) { + Slogf.d(TAG, "Added aggregated time in state info for policy id %d", policyId); + } + return calculateAvgCpuFreq(latestTimeInState); + } + LongSparseLongArray deltaTimeInState = calculateDeltaTimeInState(prevTimeInState, + latestTimeInState); + mTimeInStateByPolicyId.put(policyId, latestTimeInState); + if (DEBUG) { + Slogf.d(TAG, "Added latest delta time in state info for policy id %d", policyId); + } + return calculateAvgCpuFreq(deltaTimeInState); } @Nullable - private ArrayMap<Long, Long> readTimeInState(File policyDir) { + private LongSparseLongArray readTimeInState(File policyDir) { if (!mHasTimeInStateFile) { return null; } @@ -296,14 +443,14 @@ public final class CpuInfoReader { Slogf.w(TAG, "Empty time in state file at %s", timeInStateFile.getAbsolutePath()); return null; } - ArrayMap<Long, Long> cpuTimeByFrequencies = new ArrayMap<>(); + LongSparseLongArray cpuTimeByFrequencies = new LongSparseLongArray(); for (int i = 0; i < lines.size(); i++) { Matcher m = TIME_IN_STATE_PATTERN.matcher(lines.get(i).trim()); if (!m.find()) { continue; } cpuTimeByFrequencies.put(Long.parseLong(m.group("freqKHz")), - jiffyStrToMillis(m.group("time"))); + clockTickStrToMillis(m.group("time"))); } return cpuTimeByFrequencies; } catch (Exception e) { @@ -316,40 +463,35 @@ public final class CpuInfoReader { private static long readCpuFreqKHz(File file) { if (!file.exists()) { Slogf.e(TAG, "CPU frequency file %s doesn't exist", file.getAbsolutePath()); - return 0; + return CpuInfo.MISSING_FREQUENCY; } try { List<String> lines = Files.readAllLines(file.toPath()); if (!lines.isEmpty()) { long frequency = Long.parseLong(lines.get(0).trim()); - return frequency > 0 ? frequency : 0; + return frequency > 0 ? frequency : CpuInfo.MISSING_FREQUENCY; } } catch (Exception e) { Slogf.e(TAG, e, "Failed to read integer content from file: %s", file.getAbsolutePath()); } - return 0; + return CpuInfo.MISSING_FREQUENCY; } - private static ArrayMap<Long, Long> calculateDeltaTimeInState( - ArrayMap<Long, Long> prevTimeInState, ArrayMap<Long, Long> latestTimeInState) { - ArrayMap<Long, Long> deltaTimeInState = new ArrayMap(); - for (int i = 0; i < latestTimeInState.size(); i++) { + private static LongSparseLongArray calculateDeltaTimeInState( + LongSparseLongArray prevTimeInState, LongSparseLongArray latestTimeInState) { + int numTimeInStateEntries = latestTimeInState.size(); + LongSparseLongArray deltaTimeInState = new LongSparseLongArray(numTimeInStateEntries); + for (int i = 0; i < numTimeInStateEntries; i++) { long freq = latestTimeInState.keyAt(i); long durationMillis = latestTimeInState.valueAt(i); - long deltaDurationMillis; - if (prevTimeInState.containsKey(freq)) { - long prevDurationMillis = prevTimeInState.get(freq); - deltaDurationMillis = durationMillis > prevDurationMillis - ? (durationMillis - prevDurationMillis) : durationMillis; - } else { - deltaDurationMillis = durationMillis; - } - deltaTimeInState.put(freq, deltaDurationMillis); + long prevDurationMillis = prevTimeInState.get(freq); + deltaTimeInState.put(freq, durationMillis > prevDurationMillis + ? (durationMillis - prevDurationMillis) : durationMillis); } return deltaTimeInState; } - private static long calculateAvgCpuFreq(ArrayMap<Long, Long> timeInState) { + private static long calculateAvgCpuFreq(LongSparseLongArray timeInState) { double totalTimeInState = 0; for (int i = 0; i < timeInState.size(); i++) { totalTimeInState += timeInState.valueAt(i); @@ -364,24 +506,27 @@ public final class CpuInfoReader { /** * Reads the list of CPU cores from the given file. * - * Reads CPU cores represented in one of the below formats. + * <p>Reads CPU cores represented in one of the below formats. * <ul> * <li> Single core id. Eg: 1 * <li> Core id range. Eg: 1-4 * <li> Comma separated values. Eg: 1, 3-5, 7 * </ul> */ - private static List<Integer> readCpuCores(File file) { + @Nullable + private static IntArray readCpuCores(File file) { if (!file.exists()) { Slogf.e(TAG, "Failed to read CPU cores as the file '%s' doesn't exist", file.getAbsolutePath()); - return Collections.emptyList(); + return null; } try { List<String> lines = Files.readAllLines(file.toPath()); - List<Integer> cpuCores = new ArrayList<>(); + IntArray cpuCores = new IntArray(0); for (int i = 0; i < lines.size(); i++) { - String[] pairs = lines.get(i).trim().split(","); + String line = lines.get(i); + String[] pairs = line.contains(",") ? line.trim().split(",") + : line.trim().split(" "); for (int j = 0; j < pairs.length; j++) { String[] minMaxPairs = pairs[j].split("-"); if (minMaxPairs.length >= 2) { @@ -404,7 +549,7 @@ public final class CpuInfoReader { } catch (Exception e) { Slogf.e(TAG, e, "Failed to read CPU cores from %s", file.getAbsolutePath()); } - return Collections.emptyList(); + return null; } @Nullable @@ -435,16 +580,16 @@ public final class CpuInfoReader { continue; } cpuUsageStats.append(Integer.parseInt(m.group("core")), - new CpuUsageStats(jiffyStrToMillis(m.group("userClockTicks")), - jiffyStrToMillis(m.group("niceClockTicks")), - jiffyStrToMillis(m.group("sysClockTicks")), - jiffyStrToMillis(m.group("idleClockTicks")), - jiffyStrToMillis(m.group("iowaitClockTicks")), - jiffyStrToMillis(m.group("irqClockTicks")), - jiffyStrToMillis(m.group("softirqClockTicks")), - jiffyStrToMillis(m.group("stealClockTicks")), - jiffyStrToMillis(m.group("guestClockTicks")), - jiffyStrToMillis(m.group("guestNiceClockTicks")))); + new CpuUsageStats(clockTickStrToMillis(m.group("userClockTicks")), + clockTickStrToMillis(m.group("niceClockTicks")), + clockTickStrToMillis(m.group("sysClockTicks")), + clockTickStrToMillis(m.group("idleClockTicks")), + clockTickStrToMillis(m.group("iowaitClockTicks")), + clockTickStrToMillis(m.group("irqClockTicks")), + clockTickStrToMillis(m.group("softirqClockTicks")), + clockTickStrToMillis(m.group("stealClockTicks")), + clockTickStrToMillis(m.group("guestClockTicks")), + clockTickStrToMillis(m.group("guestNiceClockTicks")))); } } catch (Exception e) { Slogf.e(TAG, e, "Failed to read cpu usage stats from %s", @@ -453,33 +598,64 @@ public final class CpuInfoReader { return cpuUsageStats; } - private static long jiffyStrToMillis(String jiffyStr) { - return Long.parseLong(jiffyStr) * MILLIS_PER_JIFFY; + private static long clockTickStrToMillis(String jiffyStr) { + return Long.parseLong(jiffyStr) * MILLIS_PER_CLOCK_TICK; + } + + private static String toCpusetCategoriesStr(int cpusetCategories) { + StringBuilder builder = new StringBuilder(); + if ((cpusetCategories & FLAG_CPUSET_CATEGORY_TOP_APP) != 0) { + builder.append("FLAG_CPUSET_CATEGORY_TOP_APP"); + } + if ((cpusetCategories & FLAG_CPUSET_CATEGORY_BACKGROUND) != 0) { + if (builder.length() > 0) { + builder.append('|'); + } + builder.append("FLAG_CPUSET_CATEGORY_BACKGROUND"); + } + return builder.toString(); } /** Contains information for each CPU core on the system. */ public static final class CpuInfo { + public static final long MISSING_FREQUENCY = 0; + public final int cpuCore; - public final @CpusetCategory int cpusetCategories; + @CpusetCategory + public final int cpusetCategories; + public final boolean isOnline; + // 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; - CpuInfo(int cpuCore, @CpusetCategory int cpusetCategories, long curCpuFreqKHz, - long maxCpuFreqKHz, CpuUsageStats latestCpuUsageStats) { + CpuInfo(int cpuCore, @CpusetCategory int cpusetCategories, boolean isOnline, + long curCpuFreqKHz, long maxCpuFreqKHz, long avgTimeInStateCpuFreqKHz, + CpuUsageStats latestCpuUsageStats) { this.cpuCore = cpuCore; this.cpusetCategories = cpusetCategories; + this.isOnline = isOnline; this.curCpuFreqKHz = curCpuFreqKHz; this.maxCpuFreqKHz = maxCpuFreqKHz; + this.avgTimeInStateCpuFreqKHz = avgTimeInStateCpuFreqKHz; this.latestCpuUsageStats = latestCpuUsageStats; } @Override public String toString() { return new StringBuilder("CpuInfo{ cpuCore = ").append(cpuCore) - .append(", cpusetCategories = ").append(cpusetCategories) - .append(", curCpuFreqKHz = ").append(curCpuFreqKHz) - .append(", maxCpuFreqKHz = ").append(maxCpuFreqKHz) + .append(", cpusetCategories = [") + .append(toCpusetCategoriesStr(cpusetCategories)) + .append("], isOnline = ").append(isOnline ? "Yes" : "No") + .append(", curCpuFreqKHz = ") + .append(curCpuFreqKHz == MISSING_FREQUENCY ? "missing" : curCpuFreqKHz) + .append(", maxCpuFreqKHz = ") + .append(maxCpuFreqKHz == MISSING_FREQUENCY ? "missing" : maxCpuFreqKHz) + .append(", avgTimeInStateCpuFreqKHz = ") + .append(avgTimeInStateCpuFreqKHz == MISSING_FREQUENCY ? "missing" + : avgTimeInStateCpuFreqKHz) .append(", latestCpuUsageStats = ").append(latestCpuUsageStats) .append(" }").toString(); } @@ -494,15 +670,16 @@ public final class CpuInfoReader { } CpuInfo other = (CpuInfo) obj; return cpuCore == other.cpuCore && cpusetCategories == other.cpusetCategories - && curCpuFreqKHz == other.curCpuFreqKHz + && isOnline == other.isOnline && curCpuFreqKHz == other.curCpuFreqKHz && maxCpuFreqKHz == other.maxCpuFreqKHz + && avgTimeInStateCpuFreqKHz == other.avgTimeInStateCpuFreqKHz && latestCpuUsageStats.equals(other.latestCpuUsageStats); } @Override public int hashCode() { - return Objects.hash(cpuCore, cpusetCategories, curCpuFreqKHz, maxCpuFreqKHz, - latestCpuUsageStats); + return Objects.hash(cpuCore, cpusetCategories, isOnline, curCpuFreqKHz, maxCpuFreqKHz, + avgTimeInStateCpuFreqKHz, latestCpuUsageStats); } } @@ -602,4 +779,61 @@ public final class CpuInfoReader { return lhs > rhs ? lhs - rhs : 0; } } + + private static final class FrequencyPair { + public final long cpuFreqKHz; + public final long scalingFreqKHz; + + FrequencyPair(long cpuFreqKHz, long scalingFreqKHz) { + this.cpuFreqKHz = cpuFreqKHz; + this.scalingFreqKHz = scalingFreqKHz; + } + + boolean isEmpty() { + return cpuFreqKHz == CpuInfo.MISSING_FREQUENCY + && scalingFreqKHz == CpuInfo.MISSING_FREQUENCY; + } + + @Override + public String toString() { + return "FrequencyPair{cpuFreqKHz=" + cpuFreqKHz + ", scalingFreqKHz=" + scalingFreqKHz + + '}'; + } + } + + private static final class StaticPolicyInfo { + public final FrequencyPair maxCpuFreqPair; + public final IntArray relatedCpuCores; + + StaticPolicyInfo(FrequencyPair maxCpuFreqPair, IntArray relatedCpuCores) { + this.maxCpuFreqPair = maxCpuFreqPair; + this.relatedCpuCores = relatedCpuCores; + } + + @Override + public String toString() { + return "StaticPolicyInfo{maxCpuFreqPair=" + maxCpuFreqPair + ", relatedCpuCores=" + + relatedCpuCores + '}'; + } + } + + private static final class DynamicPolicyInfo { + public final FrequencyPair curCpuFreqPair; + public final long avgTimeInStateCpuFreqKHz; + public final IntArray affectedCpuCores; + + DynamicPolicyInfo(FrequencyPair curCpuFreqPair, long avgTimeInStateCpuFreqKHz, + IntArray affectedCpuCores) { + this.curCpuFreqPair = curCpuFreqPair; + this.avgTimeInStateCpuFreqKHz = avgTimeInStateCpuFreqKHz; + this.affectedCpuCores = affectedCpuCores; + } + + @Override + public String toString() { + return "DynamicPolicyInfo{curCpuFreqPair=" + curCpuFreqPair + + ", avgTimeInStateCpuFreqKHz=" + avgTimeInStateCpuFreqKHz + + ", affectedCpuCores=" + affectedCpuCores + '}'; + } + } } diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy2/affected_cpus b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy2/affected_cpus index 06717bdd7f35..0cfbf08886fc 100644 --- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy2/affected_cpus +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy2/affected_cpus @@ -1 +1 @@ -2,3 +2 diff --git a/services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java b/services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java index 81af775a458c..9f3bc3358a4c 100644 --- a/services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java @@ -16,6 +16,9 @@ package com.android.server.cpu; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.server.cpu.CpuInfoReader.CpuInfo.MISSING_FREQUENCY; import static com.android.server.cpu.CpuInfoReader.FLAG_CPUSET_CATEGORY_BACKGROUND; import static com.android.server.cpu.CpuInfoReader.FLAG_CPUSET_CATEGORY_TOP_APP; @@ -23,9 +26,8 @@ import static com.google.common.truth.Truth.assertWithMessage; import android.content.Context; import android.content.res.AssetManager; -import android.util.Slog; - -import androidx.test.platform.app.InstrumentationRegistry; +import android.util.Log; +import android.util.SparseArray; import com.android.server.ExtendedMockitoTestCase; @@ -34,20 +36,14 @@ import libcore.io.Streams; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; -import java.util.List; import java.util.Objects; -/** - * <p>This class contains unit tests for the {@link CpuInfoReader}. - */ -@RunWith(MockitoJUnitRunner.class) +/** This class contains unit tests for the {@link CpuInfoReader}. */ public final class CpuInfoReaderTest extends ExtendedMockitoTestCase { private static final String TAG = CpuInfoReaderTest.class.getSimpleName(); private static final String ROOT_DIR_NAME = "CpuInfoReaderTest"; @@ -68,334 +64,354 @@ public final class CpuInfoReaderTest extends ExtendedMockitoTestCase { private static final String EMPTY_DIR = "empty_dir"; private static final String EMPTY_FILE = "empty_file"; - private final Context mContext = - InstrumentationRegistry.getInstrumentation().getTargetContext(); + private final Context mContext = getInstrumentation().getTargetContext(); private final File mCacheRoot = new File(mContext.getCacheDir(), ROOT_DIR_NAME); private final AssetManager mAssetManager = mContext.getAssets(); - private CpuInfoReader mCpuInfoReader; - @Before public void setUp() throws Exception { copyAssets(ROOT_DIR_NAME, mContext.getCacheDir()); - assertWithMessage("Cache root dir %s", mCacheRoot.getAbsolutePath()) + assertWithMessage("Cache root dir %s exists", mCacheRoot.getAbsolutePath()) .that(mCacheRoot.exists()).isTrue(); } @After public void tearDown() throws Exception { if (!deleteDirectory(mCacheRoot)) { - Slog.e(TAG, "Failed to delete cache root directory " + mCacheRoot.getAbsolutePath()); + Log.e(TAG, "Failed to delete cache root directory " + mCacheRoot.getAbsolutePath()); } } @Test public void testReadCpuInfoWithTimeInState() throws Exception { - mCpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR), + CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(VALID_CPUSET_DIR), getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR), getCacheFile(VALID_PROC_STAT)); - mCpuInfoReader.init(); - List<CpuInfoReader.CpuInfo> actualCpuInfos = mCpuInfoReader.readCpuInfos(); - List<CpuInfoReader.CpuInfo> expectedCpuInfos = List.of( - new CpuInfoReader.CpuInfo(/* cpuCore= */ 0, FLAG_CPUSET_CATEGORY_TOP_APP, - /* curCpuFreqKHz= */ 488_095, /* maxCpuFreqKHz= */ 2_500_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610, - /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050, - /* idleTimeMillis= */ 409_036_950, - /* iowaitTimeMillis= */ 1_322_810, /* irqTimeMillis= */ 8_146_740, - /* softirqTimeMillis= */ 428_970, /* stealTimeMillis= */ 81_950, - /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)), - new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, FLAG_CPUSET_CATEGORY_TOP_APP, - /* curCpuFreqKHz= */ 502_380, /* maxCpuFreqKHz= */ 2_800_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280, - /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020, - /* idleTimeMillis= */ 402_707_120, - /* iowaitTimeMillis= */ 1_186_960, /* irqTimeMillis= */ 14_786_940, - /* softirqTimeMillis= */ 1_498_130, /* stealTimeMillis= */ 78_780, - /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)), - new CpuInfoReader.CpuInfo(/* cpuCore= */ 2, - FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, - /* curCpuFreqKHz= */ 464_285, /* maxCpuFreqKHz= */ 2_000_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280, - /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020, - /* idleTimeMillis= */ 402_717_120, - /* iowaitTimeMillis= */ 1_166_960, /* irqTimeMillis= */ 14_796_940, - /* softirqTimeMillis= */ 1_478_130, /* stealTimeMillis= */ 88_780, - /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)), - new CpuInfoReader.CpuInfo(/* cpuCore= */ 3, - FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, - /* curCpuFreqKHz= */ 464_285, /* maxCpuFreqKHz= */ 2_000_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_349_610, - /* niceTimeMillis= */ 7_850_930, /* systemTimeMillis= */ 52_127_050, - /* idleTimeMillis= */ 409_136_950, - /* iowaitTimeMillis= */ 1_332_810, /* irqTimeMillis= */ 8_136_740, - /* softirqTimeMillis= */ 438_970, /* stealTimeMillis= */ 71_950, - /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0))); - - assertWithMessage("Cpu infos").that(actualCpuInfos) - .containsExactlyElementsIn(expectedCpuInfos); - - mCpuInfoReader.setCpuFreqDir(getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_2_DIR)); - mCpuInfoReader.setProcStatFile(getCacheFile(VALID_PROC_STAT_2)); - - actualCpuInfos = mCpuInfoReader.readCpuInfos(); - expectedCpuInfos = List.of( - new CpuInfoReader.CpuInfo(/* cpuCore= */ 0, FLAG_CPUSET_CATEGORY_TOP_APP, - /* curCpuFreqKHz= */ 419_354, /* maxCpuFreqKHz= */ 2_500_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000, - /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000, - /* idleTimeMillis= */ 110_000_000, - /* iowaitTimeMillis= */ 1_100_000, /* irqTimeMillis= */ 1_400_000, - /* softirqTimeMillis= */ 80_000, /* stealTimeMillis= */ 21_000, - /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)), - new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, FLAG_CPUSET_CATEGORY_TOP_APP, - /* curCpuFreqKHz= */ 429_032, /* maxCpuFreqKHz= */ 2_800_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 900_000, - /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000, - /* idleTimeMillis= */ 1_000_000, /* iowaitTimeMillis= */ 90_000, - /* irqTimeMillis= */ 200_000, /* softirqTimeMillis= */ 100_000, - /* stealTimeMillis= */ 100_000, /* guestTimeMillis= */ 0, - /* guestNiceTimeMillis= */ 0)), - new CpuInfoReader.CpuInfo(/* cpuCore= */ 2, - FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, - /* curCpuFreqKHz= */ 403_225, /* maxCpuFreqKHz= */ 2_000_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000, - /* niceTimeMillis= */ 2_000_000, /* systemTimeMillis= */ 0, - /* idleTimeMillis= */ 10_000_000, /* iowaitTimeMillis= */ 1_000_000, - /* irqTimeMillis= */ 20_000_000, /* softirqTimeMillis= */ 1_000_000, - /* stealTimeMillis= */ 100_000, /* guestTimeMillis= */ 0, - /* guestNiceTimeMillis= */ 0)), - new CpuInfoReader.CpuInfo(/* cpuCore= */ 3, - FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, - /* curCpuFreqKHz= */ 403_225, /* maxCpuFreqKHz= */ 2_000_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 2_000_000, - /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 1_000_000, - /* idleTimeMillis= */ 100_000, /* iowaitTimeMillis= */ 100_000, - /* irqTimeMillis= */ 100_000, /* softirqTimeMillis= */ 1_000_000, - /* stealTimeMillis= */ 1_000, /* guestTimeMillis= */ 0, - /* guestNiceTimeMillis= */ 0))); - - assertWithMessage("Second snapshot of cpu infos").that(actualCpuInfos) - .containsExactlyElementsIn(expectedCpuInfos); + + SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos(); + SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>(); + expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0, + FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_230_000, + /* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ 488_095, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610, + /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050, + /* idleTimeMillis= */ 409_036_950, /* iowaitTimeMillis= */ 1_322_810, + /* irqTimeMillis= */ 8_146_740, /* softirqTimeMillis= */ 428_970, + /* stealTimeMillis= */ 81_950, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, + FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000, + /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 502_380, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280, + /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020, + /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960, + /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130, + /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2, + FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, + /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000, + /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ 464_285, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280, + /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020, + /* idleTimeMillis= */ 402_717_120, /* iowaitTimeMillis= */ 1_166_960, + /* irqTimeMillis= */ 14_796_940, /* softirqTimeMillis= */ 1_478_130, + /* stealTimeMillis= */ 88_780, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + expectedCpuInfos.append(3, new CpuInfoReader.CpuInfo(/* cpuCore= */ 3, + FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, + /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000, + /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ 464_285, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_349_610, + /* niceTimeMillis= */ 7_850_930, /* systemTimeMillis= */ 52_127_050, + /* idleTimeMillis= */ 409_136_950, /* iowaitTimeMillis= */ 1_332_810, + /* irqTimeMillis= */ 8_136_740, /* softirqTimeMillis= */ 438_970, + /* stealTimeMillis= */ 71_950, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + + compareCpuInfos("CPU infos first snapshot", expectedCpuInfos, actualCpuInfos); + + cpuInfoReader.setCpuFreqDir(getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_2_DIR)); + cpuInfoReader.setProcStatFile(getCacheFile(VALID_PROC_STAT_2)); + + actualCpuInfos = cpuInfoReader.readCpuInfos(); + + expectedCpuInfos.clear(); + expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0, + FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000, + /* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ 419_354, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000, + /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000, + /* idleTimeMillis= */ 110_000_000, /* iowaitTimeMillis= */ 1_100_000, + /* irqTimeMillis= */ 1_400_000, /* softirqTimeMillis= */ 80_000, + /* stealTimeMillis= */ 21_000, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, + FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 2_800_000, + /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 429_032, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 900_000, + /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000, + /* idleTimeMillis= */ 1_000_000, /* iowaitTimeMillis= */ 90_000, + /* irqTimeMillis= */ 200_000, /* softirqTimeMillis= */ 100_000, + /* stealTimeMillis= */ 100_000, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2, + FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, + /* isOnline= */ true, /* curCpuFreqKHz= */ 2_000_000, + /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ 403_225, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000, + /* niceTimeMillis= */ 2_000_000, /* systemTimeMillis= */ 0, + /* idleTimeMillis= */ 10_000_000, /* iowaitTimeMillis= */ 1_000_000, + /* irqTimeMillis= */ 20_000_000, /* softirqTimeMillis= */ 1_000_000, + /* stealTimeMillis= */ 100_000, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + expectedCpuInfos.append(3, new CpuInfoReader.CpuInfo(/* cpuCore= */ 3, + FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, + /* isOnline= */ false, /* curCpuFreqKHz= */ MISSING_FREQUENCY, + /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 2_000_000, + /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 1_000_000, + /* idleTimeMillis= */ 100_000, /* iowaitTimeMillis= */ 100_000, + /* irqTimeMillis= */ 100_000, /* softirqTimeMillis= */ 1_000_000, + /* stealTimeMillis= */ 1_000, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + + compareCpuInfos("CPU infos second snapshot", expectedCpuInfos, actualCpuInfos); } @Test public void testReadCpuInfoWithoutTimeInState() throws Exception { - mCpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR), + CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(VALID_CPUSET_DIR), getCacheFile(VALID_CPUFREQ_WITHOUT_TIME_IN_STATE_DIR), getCacheFile(VALID_PROC_STAT)); - mCpuInfoReader.init(); - List<CpuInfoReader.CpuInfo> actualCpuInfos = mCpuInfoReader.readCpuInfos(); - List<CpuInfoReader.CpuInfo> expectedCpuInfos = List.of( - new CpuInfoReader.CpuInfo(/* cpuCore= */ 0, FLAG_CPUSET_CATEGORY_TOP_APP, - /* curCpuFreqKHz= */ 1_230_000, /* maxCpuFreqKHz= */ 2_500_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610, - /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050, - /* idleTimeMillis= */ 409_036_950, - /* iowaitTimeMillis= */ 1_322_810, /* irqTimeMillis= */ 8_146_740, - /* softirqTimeMillis= */ 428_970, /* stealTimeMillis= */ 81_950, - /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)), - new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, FLAG_CPUSET_CATEGORY_TOP_APP, - /* curCpuFreqKHz= */ 1_450_000, /* maxCpuFreqKHz= */ 2_800_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280, - /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020, - /* idleTimeMillis= */ 402_707_120, - /* iowaitTimeMillis= */ 1_186_960, /* irqTimeMillis= */ 14_786_940, - /* softirqTimeMillis= */ 1_498_130, /* stealTimeMillis= */ 78_780, - /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)), - new CpuInfoReader.CpuInfo(/* cpuCore= */ 2, - FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, - /* curCpuFreqKHz= */ 1_000_000, /* maxCpuFreqKHz= */ 2_000_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280, - /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020, - /* idleTimeMillis= */ 402_717_120, - /* iowaitTimeMillis= */ 1_166_960, /* irqTimeMillis= */ 14_796_940, - /* softirqTimeMillis= */ 1_478_130, /* stealTimeMillis= */ 88_780, - /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)), - new CpuInfoReader.CpuInfo(/* cpuCore= */ 3, - FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, - /* curCpuFreqKHz= */ 1_000_000, /* maxCpuFreqKHz= */ 2_000_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_349_610, - /* niceTimeMillis= */ 7_850_930, /* systemTimeMillis= */ 52_127_050, - /* idleTimeMillis= */ 409_136_950, - /* iowaitTimeMillis= */ 1_332_810, /* irqTimeMillis= */ 8_136_740, - /* softirqTimeMillis= */ 438_970, /* stealTimeMillis= */ 71_950, - /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0))); - - assertWithMessage("Cpu infos").that(actualCpuInfos) - .containsExactlyElementsIn(expectedCpuInfos); - - mCpuInfoReader.setCpuFreqDir(getCacheFile(VALID_CPUFREQ_WITHOUT_TIME_IN_STATE_2_DIR)); - mCpuInfoReader.setProcStatFile(getCacheFile(VALID_PROC_STAT_2)); - - actualCpuInfos = mCpuInfoReader.readCpuInfos(); - expectedCpuInfos = List.of( - new CpuInfoReader.CpuInfo(/* cpuCore= */ 0, FLAG_CPUSET_CATEGORY_TOP_APP, - /* curCpuFreqKHz= */ 1_000_000, /* maxCpuFreqKHz= */ 2_500_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000, - /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000, - /* idleTimeMillis= */ 110_000_000, - /* iowaitTimeMillis= */ 1_100_000, /* irqTimeMillis= */ 1_400_000, - /* softirqTimeMillis= */ 80_000, /* stealTimeMillis= */ 21_000, - /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)), - new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, FLAG_CPUSET_CATEGORY_TOP_APP, - /* curCpuFreqKHz= */ 2_800_000, /* maxCpuFreqKHz= */ 2_800_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 900_000, - /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000, - /* idleTimeMillis= */ 1_000_000, /* iowaitTimeMillis= */ 90_000, - /* irqTimeMillis= */ 200_000, /* softirqTimeMillis= */ 100_000, - /* stealTimeMillis= */ 100_000, /* guestTimeMillis= */ 0, - /* guestNiceTimeMillis= */ 0)), - new CpuInfoReader.CpuInfo(/* cpuCore= */ 2, - FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, - /* curCpuFreqKHz= */ 2_000_000, /* maxCpuFreqKHz= */ 2_000_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000, - /* niceTimeMillis= */ 2_000_000, /* systemTimeMillis= */ 0, - /* idleTimeMillis= */ 10_000_000, /* iowaitTimeMillis= */ 1_000_000, - /* irqTimeMillis= */ 20_000_000, /* softirqTimeMillis= */ 1_000_000, - /* stealTimeMillis= */ 100_000, /* guestTimeMillis= */ 0, - /* guestNiceTimeMillis= */ 0)), - new CpuInfoReader.CpuInfo(/* cpuCore= */ 3, - FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, - /* curCpuFreqKHz= */ 2_000_000, /* maxCpuFreqKHz= */ 2_000_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 2_000_000, - /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 1_000_000, - /* idleTimeMillis= */ 100_000, /* iowaitTimeMillis= */ 100_000, - /* irqTimeMillis= */ 100_000, /* softirqTimeMillis= */ 1_000_000, - /* stealTimeMillis= */ 1_000, /* guestTimeMillis= */ 0, - /* guestNiceTimeMillis= */ 0))); - - assertWithMessage("Second snapshot of cpu infos").that(actualCpuInfos) - .containsExactlyElementsIn(expectedCpuInfos); + + SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos(); + SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>(); + expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0, + FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_230_000, + /* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610, + /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050, + /* idleTimeMillis= */ 409_036_950, /* iowaitTimeMillis= */ 1_322_810, + /* irqTimeMillis= */ 8_146_740, /* softirqTimeMillis= */ 428_970, + /* stealTimeMillis= */ 81_950, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, + FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000, + /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280, + /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020, + /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960, + /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130, + /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2, + FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, + /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000, + /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280, + /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020, + /* idleTimeMillis= */ 402_717_120, /* iowaitTimeMillis= */ 1_166_960, + /* irqTimeMillis= */ 14_796_940, /* softirqTimeMillis= */ 1_478_130, + /* stealTimeMillis= */ 88_780, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + expectedCpuInfos.append(3, new CpuInfoReader.CpuInfo(/* cpuCore= */ 3, + FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, + /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000, + /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_349_610, + /* niceTimeMillis= */ 7_850_930, /* systemTimeMillis= */ 52_127_050, + /* idleTimeMillis= */ 409_136_950, /* iowaitTimeMillis= */ 1_332_810, + /* irqTimeMillis= */ 8_136_740, /* softirqTimeMillis= */ 438_970, + /* stealTimeMillis= */ 71_950, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + + compareCpuInfos("CPU infos first snapshot without time_in_state file", expectedCpuInfos, + actualCpuInfos); + + cpuInfoReader.setCpuFreqDir(getCacheFile(VALID_CPUFREQ_WITHOUT_TIME_IN_STATE_2_DIR)); + cpuInfoReader.setProcStatFile(getCacheFile(VALID_PROC_STAT_2)); + + actualCpuInfos = cpuInfoReader.readCpuInfos(); + + expectedCpuInfos.clear(); + expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0, + FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000, + /* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000, + /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000, + /* idleTimeMillis= */ 110_000_000, /* iowaitTimeMillis= */ 1_100_000, + /* irqTimeMillis= */ 1_400_000, /* softirqTimeMillis= */ 80_000, + /* stealTimeMillis= */ 21_000, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, + FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 2_800_000, + /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 900_000, + /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000, + /* idleTimeMillis= */ 1_000_000, /* iowaitTimeMillis= */ 90_000, + /* irqTimeMillis= */ 200_000, /* softirqTimeMillis= */ 100_000, + /* stealTimeMillis= */ 100_000, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2, + FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, + /* isOnline= */ true, /* curCpuFreqKHz= */ 2_000_000, + /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000, + /* niceTimeMillis= */ 2_000_000, /* systemTimeMillis= */ 0, + /* idleTimeMillis= */ 10_000_000, /* iowaitTimeMillis= */ 1_000_000, + /* irqTimeMillis= */ 20_000_000, /* softirqTimeMillis= */ 1_000_000, + /* stealTimeMillis= */ 100_000, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + expectedCpuInfos.append(3, new CpuInfoReader.CpuInfo(/* cpuCore= */ 3, + FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, + /* isOnline= */ true, /* curCpuFreqKHz= */ 2_000_000, + /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 2_000_000, + /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 1_000_000, + /* idleTimeMillis= */ 100_000, /* iowaitTimeMillis= */ 100_000, + /* irqTimeMillis= */ 100_000, /* softirqTimeMillis= */ 1_000_000, + /* stealTimeMillis= */ 1_000, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + + compareCpuInfos("CPU infos second snapshot without time_in_state file", expectedCpuInfos, + actualCpuInfos); } @Test public void testReadCpuInfoWithCorruptedCpuset() throws Exception { - mCpuInfoReader = new CpuInfoReader(getCacheFile(CORRUPTED_CPUSET_DIR), + CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(CORRUPTED_CPUSET_DIR), getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR), getCacheFile(VALID_PROC_STAT)); - mCpuInfoReader.init(); - List<CpuInfoReader.CpuInfo> actualCpuInfos = mCpuInfoReader.readCpuInfos(); - List<CpuInfoReader.CpuInfo> expectedCpuInfos = List.of( - new CpuInfoReader.CpuInfo(/* cpuCore= */ 0, FLAG_CPUSET_CATEGORY_TOP_APP, - /* curCpuFreqKHz= */ 488_095, /* maxCpuFreqKHz= */ 2_500_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610, - /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050, - /* idleTimeMillis= */ 409_036_950, - /* iowaitTimeMillis= */ 1_322_810, /* irqTimeMillis= */ 8_146_740, - /* softirqTimeMillis= */ 428_970, /* stealTimeMillis= */ 81_950, - /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)), - new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, FLAG_CPUSET_CATEGORY_TOP_APP, - /* curCpuFreqKHz= */ 502_380, /* maxCpuFreqKHz= */ 2_800_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280, - /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020, - /* idleTimeMillis= */ 402_707_120, - /* iowaitTimeMillis= */ 1_186_960, /* irqTimeMillis= */ 14_786_940, - /* softirqTimeMillis= */ 1_498_130, /* stealTimeMillis= */ 78_780, - /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)), - new CpuInfoReader.CpuInfo(/* cpuCore= */ 2, FLAG_CPUSET_CATEGORY_TOP_APP, - /* curCpuFreqKHz= */ 464_285, /* maxCpuFreqKHz= */ 2_000_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280, - /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020, - /* idleTimeMillis= */ 402_717_120, - /* iowaitTimeMillis= */ 1_166_960, /* irqTimeMillis= */ 14_796_940, - /* softirqTimeMillis= */ 1_478_130, /* stealTimeMillis= */ 88_780, - /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0))); - - assertWithMessage("Cpu infos").that(actualCpuInfos) - .containsExactlyElementsIn(expectedCpuInfos); + + SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos(); + SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>(); + expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0, + FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_230_000, + /* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ 488_095, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610, + /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050, + /* idleTimeMillis= */ 409_036_950, /* iowaitTimeMillis= */ 1_322_810, + /* irqTimeMillis= */ 8_146_740, /* softirqTimeMillis= */ 428_970, + /* stealTimeMillis= */ 81_950, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, + FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000, + /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 502_380, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280, + /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020, + /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960, + /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130, + /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2, + FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000, + /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ 464_285, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280, + /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020, + /* idleTimeMillis= */ 402_717_120, /* iowaitTimeMillis= */ 1_166_960, + /* irqTimeMillis= */ 14_796_940, /* softirqTimeMillis= */ 1_478_130, + /* stealTimeMillis= */ 88_780, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + + compareCpuInfos("CPU infos with corrupted cpuset", expectedCpuInfos, actualCpuInfos); } @Test public void testReadCpuInfoWithCorruptedCpufreq() throws Exception { - mCpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR), + CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(VALID_CPUSET_DIR), getCacheFile(CORRUPTED_CPUFREQ_DIR), getCacheFile(VALID_PROC_STAT)); - mCpuInfoReader.init(); - List<CpuInfoReader.CpuInfo> actualCpuInfos = mCpuInfoReader.readCpuInfos(); - List<CpuInfoReader.CpuInfo> expectedCpuInfos = List.of( - new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, FLAG_CPUSET_CATEGORY_TOP_APP, - /* curCpuFreqKHz= */ 3_000_000, /* maxCpuFreqKHz= */ 1_000_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280, - /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020, - /* idleTimeMillis= */ 402_707_120, - /* iowaitTimeMillis= */ 1_186_960, /* irqTimeMillis= */ 14_786_940, - /* softirqTimeMillis= */ 1_498_130, /* stealTimeMillis= */ 78_780, - /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)), - new CpuInfoReader.CpuInfo(/* cpuCore= */ 2, - FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, - /* curCpuFreqKHz= */ 9, /* maxCpuFreqKHz= */ 2, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280, - /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020, - /* idleTimeMillis= */ 402_717_120, - /* iowaitTimeMillis= */ 1_166_960, /* irqTimeMillis= */ 14_796_940, - /* softirqTimeMillis= */ 1_478_130, /* stealTimeMillis= */ 88_780, - /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)), - new CpuInfoReader.CpuInfo(/* cpuCore= */ 3, - FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, - /* curCpuFreqKHz= */ 9, /* maxCpuFreqKHz= */ 2, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_349_610, - /* niceTimeMillis= */ 7_850_930, /* systemTimeMillis= */ 52_127_050, - /* idleTimeMillis= */ 409_136_950, - /* iowaitTimeMillis= */ 1_332_810, /* irqTimeMillis= */ 8_136_740, - /* softirqTimeMillis= */ 438_970, /* stealTimeMillis= */ 71_950, - /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0))); - - assertWithMessage("Cpu infos").that(actualCpuInfos) - .containsExactlyElementsIn(expectedCpuInfos); + + SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos(); + SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>(); + expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, + FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 3_000_000, + /* maxCpuFreqKHz= */ 1_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280, + /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020, + /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960, + /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130, + /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2, + FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, + /* isOnline= */ true, /* curCpuFreqKHz= */ 9, /* maxCpuFreqKHz= */ 2, + /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280, + /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020, + /* idleTimeMillis= */ 402_717_120, /* iowaitTimeMillis= */ 1_166_960, + /* irqTimeMillis= */ 14_796_940, /* softirqTimeMillis= */ 1_478_130, + /* stealTimeMillis= */ 88_780, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + expectedCpuInfos.append(3, new CpuInfoReader.CpuInfo(/* cpuCore= */ 3, + FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, + /* isOnline= */ true, /* curCpuFreqKHz= */ 9, /* maxCpuFreqKHz= */ 2, + /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_349_610, + /* niceTimeMillis= */ 7_850_930, /* systemTimeMillis= */ 52_127_050, + /* idleTimeMillis= */ 409_136_950, /* iowaitTimeMillis= */ 1_332_810, + /* irqTimeMillis= */ 8_136_740, /* softirqTimeMillis= */ 438_970, + /* stealTimeMillis= */ 71_950, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + + compareCpuInfos("CPU infos with corrupted CPU frequency", expectedCpuInfos, + actualCpuInfos); } @Test - public void testReadCpuInfoCorruptedProcStat() throws Exception { - mCpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR), + public void testReadCpuInfoWithCorruptedProcStat() throws Exception { + CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(VALID_CPUSET_DIR), getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR), getCacheFile(CORRUPTED_PROC_STAT)); - mCpuInfoReader.init(); - List<CpuInfoReader.CpuInfo> actualCpuInfos = mCpuInfoReader.readCpuInfos(); - List<CpuInfoReader.CpuInfo> expectedCpuInfos = List.of( - new CpuInfoReader.CpuInfo(/* cpuCore= */ 0, FLAG_CPUSET_CATEGORY_TOP_APP, - /* curCpuFreqKHz= */ 488_095, /* maxCpuFreqKHz= */ 2_500_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610, - /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050, - /* idleTimeMillis= */ 409_036_950, - /* iowaitTimeMillis= */ 1_322_810, /* irqTimeMillis= */ 8_146_740, - /* softirqTimeMillis= */ 428_970, /* stealTimeMillis= */ 81_950, - /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0)), - new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, - FLAG_CPUSET_CATEGORY_TOP_APP, - /* curCpuFreqKHz= */ 502_380, /* maxCpuFreqKHz= */ 2_800_000, - new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280, - /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020, - /* idleTimeMillis= */ 402_707_120, - /* iowaitTimeMillis= */ 1_186_960, /* irqTimeMillis= */ 14_786_940, - /* softirqTimeMillis= */ 1_498_130, /* stealTimeMillis= */ 78_780, - /* guestTimeMillis= */ 0, /* guestNiceTimeMillis= */ 0))); - - assertWithMessage("Cpu infos").that(actualCpuInfos) - .containsExactlyElementsIn(expectedCpuInfos); + + SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos(); + SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>(); + expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0, + FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_230_000, + /* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ 488_095, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610, + /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050, + /* idleTimeMillis= */ 409_036_950, /* iowaitTimeMillis= */ 1_322_810, + /* irqTimeMillis= */ 8_146_740, /* softirqTimeMillis= */ 428_970, + /* stealTimeMillis= */ 81_950, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, + FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000, + /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 502_380, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280, + /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020, + /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960, + /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130, + /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + + compareCpuInfos("CPU infos with corrupted proc stat", expectedCpuInfos, actualCpuInfos); } @Test public void testReadCpuInfoWithEmptyCpuset() throws Exception { File emptyDir = getCacheFile(EMPTY_DIR); assertWithMessage("Make empty dir %s", emptyDir).that(emptyDir.mkdir()).isTrue(); - mCpuInfoReader = new CpuInfoReader(emptyDir, getCacheFile( + CpuInfoReader cpuInfoReader = new CpuInfoReader(emptyDir, getCacheFile( VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR), getCacheFile(VALID_PROC_STAT)); - assertWithMessage("Init CPU reader info").that(mCpuInfoReader.init()).isFalse(); + assertWithMessage("Init CPU reader info").that(cpuInfoReader.init()).isFalse(); - assertWithMessage("Cpu infos").that(mCpuInfoReader.readCpuInfos()).isEmpty(); + assertWithMessage("Cpu infos with empty cpuset").that(cpuInfoReader.readCpuInfos()) + .isNull(); } @Test public void testReadCpuInfoWithEmptyCpufreq() throws Exception { File emptyDir = getCacheFile(EMPTY_DIR); assertWithMessage("Make empty dir %s", emptyDir).that(emptyDir.mkdir()).isTrue(); - mCpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR), emptyDir, + CpuInfoReader cpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR), emptyDir, getCacheFile(VALID_PROC_STAT)); - assertWithMessage("Init CPU reader info").that(mCpuInfoReader.init()).isFalse(); + assertWithMessage("Init CPU reader info").that(cpuInfoReader.init()).isFalse(); - assertWithMessage("Cpu infos").that(mCpuInfoReader.readCpuInfos()).isEmpty(); + assertWithMessage("Cpu infos with empty CPU frequency").that(cpuInfoReader.readCpuInfos()) + .isNull(); } @Test @@ -403,10 +419,25 @@ public final class CpuInfoReaderTest extends ExtendedMockitoTestCase { File emptyFile = getCacheFile(EMPTY_FILE); assertWithMessage("Create empty file %s", emptyFile).that(emptyFile.createNewFile()) .isTrue(); - mCpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR), + CpuInfoReader cpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR), getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR), getCacheFile(EMPTY_FILE)); - assertWithMessage("Cpu infos").that(mCpuInfoReader.readCpuInfos()).isEmpty(); + assertWithMessage("Cpu infos with empty proc stat").that(cpuInfoReader.readCpuInfos()) + .isNull(); + } + + private static void compareCpuInfos(String message, + SparseArray<CpuInfoReader.CpuInfo> expected, + SparseArray<CpuInfoReader.CpuInfo> actual) { + assertWithMessage("%s. Total CPU infos", message).that(actual.size()) + .isEqualTo(expected.size()); + for (int i = 0; i < expected.size(); i++) { + int cpuCoreId = expected.keyAt(i); + CpuInfoReader.CpuInfo expectedCpuInfo = expected.valueAt(i); + CpuInfoReader.CpuInfo actualCpuInfo = actual.get(cpuCoreId); + assertWithMessage("%s. Core %d's CPU info", message, cpuCoreId).that(actualCpuInfo) + .isEqualTo(expectedCpuInfo); + } } private File getCacheFile(String assetName) { @@ -424,11 +455,18 @@ public final class CpuInfoReaderTest extends ExtendedMockitoTestCase { return; } assertWithMessage("Make target directory %s", target).that(target.mkdir()).isTrue(); - for (int i = 0; i < assets.length; i++) { - copyAssets(String.format("%s%s%s", assetPath, File.separator, assets[i]), targetRoot); + for (String assetName : assets) { + copyAssets(String.format("%s%s%s", assetPath, File.separator, assetName), targetRoot); } } + private static CpuInfoReader newCpuInfoReader(File cpusetDir, File cpuFreqDir, + File procStatFile) { + CpuInfoReader cpuInfoReader = new CpuInfoReader(cpusetDir, cpuFreqDir, procStatFile); + assertWithMessage("Initialize CPU info reader").that(cpuInfoReader.init()).isTrue(); + return cpuInfoReader; + } + private static boolean deleteDirectory(File rootDir) { if (!rootDir.exists() || !rootDir.isDirectory()) { return false; |