Report CPU online status & avg time in state freq.

- CpuInfoReader must verify all frequency policy directories for the
  presence of time_in_state file before marking it as missing because
  this file might be missing when all cores on a policy are offline.
  Thus, check all policies.
- Split the CPU info cache into static and dynamic CPU info stats. This
  helps preseves all the information read from the sysfs interface.
  With the previous implementation, some critical information required
  to make certain decision (later in the implementation) were lost.
  - For instance, the related & affected CPU cores information are
    required to decide whether a CPU core is online or offline.
  - The current & max CPU frequency should come from the same source -
    either both from the frequency reported by the CPU hardware or
    the frequency determined by the scaling governor.
- Handle the case where a CPU core is offline and the latest CPU usage
  stats are missing. This will be useful when collection bugreport dump.
- Fix the return types to avoid boxing/unboxing ints/longs.

Test: atest CpuInfoReaderTest
Bug: 242722241

Change-Id: I5c51ac8b699ffe616b64de279ebab12968723160
This commit is contained in:
Lakshman Annadorai
2022-11-22 11:57:20 -08:00
parent f1707975d4
commit cc844ea5c3
3 changed files with 685 additions and 413 deletions

View File

@@ -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();
return null;
}
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);
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 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));
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 readAvgTimeInStateCpuFrequency(int policyId, File policyDir) {
LongSparseLongArray latestTimeInState = readTimeInState(policyDir);
if (latestTimeInState == null || latestTimeInState.size() == 0) {
return CpuInfo.MISSING_FREQUENCY;
}
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);
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);
}
mTimeInStateByPolicy.put(policyDirName, latestTimeInState);
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 + '}';
}
}
}

View File

@@ -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);
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)));
mCpuInfoReader.setCpuFreqDir(getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_2_DIR));
mCpuInfoReader.setProcStatFile(getCacheFile(VALID_PROC_STAT_2));
compareCpuInfos("CPU infos first snapshot", expectedCpuInfos, actualCpuInfos);
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)));
cpuInfoReader.setCpuFreqDir(getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_2_DIR));
cpuInfoReader.setProcStatFile(getCacheFile(VALID_PROC_STAT_2));
assertWithMessage("Second snapshot of cpu infos").that(actualCpuInfos)
.containsExactlyElementsIn(expectedCpuInfos);
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);
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)));
mCpuInfoReader.setCpuFreqDir(getCacheFile(VALID_CPUFREQ_WITHOUT_TIME_IN_STATE_2_DIR));
mCpuInfoReader.setProcStatFile(getCacheFile(VALID_PROC_STAT_2));
compareCpuInfos("CPU infos first snapshot without time_in_state file", expectedCpuInfos,
actualCpuInfos);
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)));
cpuInfoReader.setCpuFreqDir(getCacheFile(VALID_CPUFREQ_WITHOUT_TIME_IN_STATE_2_DIR));
cpuInfoReader.setProcStatFile(getCacheFile(VALID_PROC_STAT_2));
assertWithMessage("Second snapshot of cpu infos").that(actualCpuInfos)
.containsExactlyElementsIn(expectedCpuInfos);
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;