Loading services/core/java/com/android/server/cpu/CpuInfoReader.java +58 −8 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import java.nio.file.Files; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; Loading Loading @@ -80,13 +81,14 @@ public final class CpuInfoReader { /** package **/ @interface CpusetCategory{} // TODO(b/242722241): Protect updatable variables with a local lock. private final File mCpusetDir; private final long mMinReadIntervalMillis; private final SparseIntArray mCpusetCategoriesByCpus = new SparseIntArray(); private final SparseArray<File> mCpuFreqPolicyDirsById = new SparseArray<>(); private final SparseArray<StaticPolicyInfo> mStaticPolicyInfoById = new SparseArray<>(); private final SparseArray<LongSparseLongArray> mTimeInStateByPolicyId = new SparseArray<>(); private final AtomicBoolean mShouldReadCpusetCategories; private File mCpusetDir; private File mCpuFreqDir; private File mProcStatFile; private SparseArray<CpuUsageStats> mCumulativeCpuUsageStats = new SparseArray<>(); Loading @@ -106,10 +108,13 @@ public final class CpuInfoReader { mCpuFreqDir = cpuFreqDir; mProcStatFile = procStatFile; mMinReadIntervalMillis = minReadIntervalMillis; mShouldReadCpusetCategories = new AtomicBoolean(true); } /** * Initializes CpuInfoReader and returns a boolean to indicate whether the reader is enabled. * * <p>Returns {@code true} on success. Otherwise, returns {@code false}. */ public boolean init() { if (mCpuFreqPolicyDirsById.size() > 0) { Loading Loading @@ -139,8 +144,7 @@ public final class CpuInfoReader { Slogf.e(TAG, "Missing proc stat file at %s", mProcStatFile.getAbsolutePath()); return false; } readCpusetCategories(); if (mCpusetCategoriesByCpus.size() == 0) { if (!readCpusetCategories()) { Slogf.e(TAG, "Failed to read cpuset information from %s", mCpusetDir.getAbsolutePath()); return false; } Loading @@ -163,10 +167,19 @@ public final class CpuInfoReader { return true; } public void stopPeriodicCpusetReading() { mShouldReadCpusetCategories.set(false); if (!readCpusetCategories()) { Slogf.e(TAG, "Failed to read cpuset information from %s", mCpusetDir.getAbsolutePath()); mIsEnabled = false; } } /** * 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. * <p>Returns SparseArray keyed by CPU core ID; {@code null} on error or when disabled. */ @Nullable public SparseArray<CpuInfo> readCpuInfos() { Loading @@ -183,6 +196,12 @@ public final class CpuInfoReader { } mLastReadUptimeMillis = uptimeMillis; mLastReadCpuInfos = null; if (mShouldReadCpusetCategories.get() && !readCpusetCategories()) { Slogf.e(TAG, "Failed to read cpuset information from %s", mCpusetDir.getAbsolutePath()); mIsEnabled = false; return null; } SparseArray<CpuUsageStats> cpuUsageStatsByCpus = readLatestCpuUsageStats(); if (cpuUsageStatsByCpus == null || cpuUsageStatsByCpus.size() == 0) { Slogf.e(TAG, "Failed to read latest CPU usage stats"); Loading Loading @@ -324,7 +343,7 @@ public final class CpuInfoReader { /** * Sets the CPU frequency for testing. * * <p>Return {@code true} on success. Otherwise, returns {@code false}. * <p>Returns {@code true} on success. Otherwise, returns {@code false}. */ @VisibleForTesting boolean setCpuFreqDir(File cpuFreqDir) { Loading Loading @@ -354,7 +373,7 @@ public final class CpuInfoReader { /** * Sets the proc stat file for testing. * * <p>Return true on success. Otherwise, returns false. * <p>Returns {@code true} on success. Otherwise, returns {@code false}. */ @VisibleForTesting boolean setProcStatFile(File procStatFile) { Loading @@ -366,6 +385,21 @@ public final class CpuInfoReader { return true; } /** * Set the cpuset directory for testing. * * <p>Returns {@code true} on success. Otherwise, returns {@code false}. */ @VisibleForTesting boolean setCpusetDir(File cpusetDir) { if (!cpusetDir.exists() && !cpusetDir.isDirectory()) { Slogf.e(TAG, "Missing or invalid cpuset directory at %s", cpusetDir.getAbsolutePath()); return false; } mCpusetDir = cpusetDir; return true; } private void populateCpuFreqPolicyDirsById(File[] policyDirs) { mCpuFreqPolicyDirsById.clear(); for (int i = 0; i < policyDirs.length; i++) { Loading @@ -381,12 +415,27 @@ public final class CpuInfoReader { } } private void readCpusetCategories() { /** * Reads cpuset categories by CPU. * * <p>The cpusets are read from the cpuset category specific directories * under the /dev/cpuset directory. The cpuset categories are subject to change at any point * during system bootup, as determined by the init rules specified within the init.rc files. * Therefore, it's necessary to read the cpuset categories each time before accessing CPU usage * statistics until the system boot completes. Once the boot is complete, the latest changes to * the cpuset categories will take a few seconds to propagate. Thus, on boot complete, * the periodic reading is stopped with a delay of * {@link CpuMonitorService#STOP_PERIODIC_CPUSET_READING_DELAY_MILLISECONDS}. * * <p>Returns {@code true} on success. Otherwise, returns {@code false}. */ private boolean readCpusetCategories() { File[] cpusetDirs = mCpusetDir.listFiles(File::isDirectory); if (cpusetDirs == null) { Slogf.e(TAG, "Missing cpuset directories at %s", mCpusetDir.getAbsolutePath()); return; return false; } mCpusetCategoriesByCpus.clear(); for (int i = 0; i < cpusetDirs.length; i++) { File dir = cpusetDirs[i]; @CpusetCategory int cpusetCategory; Loading Loading @@ -418,6 +467,7 @@ public final class CpuInfoReader { } } } return mCpusetCategoriesByCpus.size() > 0; } private void readStaticPolicyInfo() { Loading services/core/java/com/android/server/cpu/CpuMonitorService.java +26 −2 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static com.android.server.cpu.CpuAvailabilityMonitoringConfig.CPUSET_ALL; import static com.android.server.cpu.CpuAvailabilityMonitoringConfig.CPUSET_BACKGROUND; import static com.android.server.cpu.CpuInfoReader.FLAG_CPUSET_CATEGORY_BACKGROUND; import static com.android.server.cpu.CpuInfoReader.FLAG_CPUSET_CATEGORY_TOP_APP; import static com.android.server.SystemService.PHASE_BOOT_COMPLETED; import android.annotation.Nullable; import android.content.Context; Loading Loading @@ -82,6 +83,15 @@ public final class CpuMonitorService extends SystemService { // frequently. Should this duration be increased as well when this happens? private static final long LATEST_AVAILABILITY_DURATION_MILLISECONDS = TimeUnit.SECONDS.toMillis(30); /** * Delay to stop the periodic cpuset reading after boot complete. * * Device specific implementations can update cpuset on boot complete. This may take * a few seconds to propagate. So, wait for a few minutes before stopping the periodic cpuset * reading. */ private static final long STOP_PERIODIC_CPUSET_READING_DELAY_MILLISECONDS = TimeUnit.MINUTES.toMillis(2); private final Context mContext; private final HandlerThread mHandlerThread; Loading @@ -90,6 +100,7 @@ public final class CpuMonitorService extends SystemService { private final long mNormalMonitoringIntervalMillis; private final long mDebugMonitoringIntervalMillis; private final long mLatestAvailabilityDurationMillis; private final long mStopPeriodicCpusetReadingDelayMillis; private final Object mLock = new Object(); @GuardedBy("mLock") private final SparseArrayMap<CpuMonitorInternal.CpuAvailabilityCallback, Loading Loading @@ -153,13 +164,15 @@ public final class CpuMonitorService extends SystemService { this(context, new CpuInfoReader(), new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, /* allowIo= */ true), Build.IS_USERDEBUG || Build.IS_ENG, NORMAL_MONITORING_INTERVAL_MILLISECONDS, DEBUG_MONITORING_INTERVAL_MILLISECONDS, LATEST_AVAILABILITY_DURATION_MILLISECONDS); DEBUG_MONITORING_INTERVAL_MILLISECONDS, LATEST_AVAILABILITY_DURATION_MILLISECONDS, STOP_PERIODIC_CPUSET_READING_DELAY_MILLISECONDS); } @VisibleForTesting CpuMonitorService(Context context, CpuInfoReader cpuInfoReader, HandlerThread handlerThread, boolean shouldDebugMonitor, long normalMonitoringIntervalMillis, long debugMonitoringIntervalMillis, long latestAvailabilityDurationMillis) { long debugMonitoringIntervalMillis, long latestAvailabilityDurationMillis, long stopPeriodicCpusetReadingDelayMillis) { super(context); mContext = context; mHandlerThread = handlerThread; Loading @@ -167,6 +180,7 @@ public final class CpuMonitorService extends SystemService { mNormalMonitoringIntervalMillis = normalMonitoringIntervalMillis; mDebugMonitoringIntervalMillis = debugMonitoringIntervalMillis; mLatestAvailabilityDurationMillis = latestAvailabilityDurationMillis; mStopPeriodicCpusetReadingDelayMillis = stopPeriodicCpusetReadingDelayMillis; mCpuInfoReader = cpuInfoReader; mCpusetInfosByCpuset = new SparseArray<>(2); mCpusetInfosByCpuset.append(CPUSET_ALL, new CpusetInfo(CPUSET_ALL)); Loading Loading @@ -200,6 +214,16 @@ public final class CpuMonitorService extends SystemService { } } @Override public void onBootPhase(int phase) { if (phase != PHASE_BOOT_COMPLETED) { return; } Slogf.i(TAG, "Stopping periodic cpuset reading on boot complete"); mHandler.postDelayed(() -> mCpuInfoReader.stopPeriodicCpusetReading(), mStopPeriodicCpusetReadingDelayMillis); } @VisibleForTesting long getCurrentMonitoringIntervalMillis() { synchronized (mLock) { Loading services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpuset_2/background/cpus 0 → 100644 +1 −0 Original line number Diff line number Diff line 0-1 services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpuset_2/top-app/cpus 0 → 100644 +1 −0 Original line number Diff line number Diff line 0-3 services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java +191 −86 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
services/core/java/com/android/server/cpu/CpuInfoReader.java +58 −8 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import java.nio.file.Files; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; Loading Loading @@ -80,13 +81,14 @@ public final class CpuInfoReader { /** package **/ @interface CpusetCategory{} // TODO(b/242722241): Protect updatable variables with a local lock. private final File mCpusetDir; private final long mMinReadIntervalMillis; private final SparseIntArray mCpusetCategoriesByCpus = new SparseIntArray(); private final SparseArray<File> mCpuFreqPolicyDirsById = new SparseArray<>(); private final SparseArray<StaticPolicyInfo> mStaticPolicyInfoById = new SparseArray<>(); private final SparseArray<LongSparseLongArray> mTimeInStateByPolicyId = new SparseArray<>(); private final AtomicBoolean mShouldReadCpusetCategories; private File mCpusetDir; private File mCpuFreqDir; private File mProcStatFile; private SparseArray<CpuUsageStats> mCumulativeCpuUsageStats = new SparseArray<>(); Loading @@ -106,10 +108,13 @@ public final class CpuInfoReader { mCpuFreqDir = cpuFreqDir; mProcStatFile = procStatFile; mMinReadIntervalMillis = minReadIntervalMillis; mShouldReadCpusetCategories = new AtomicBoolean(true); } /** * Initializes CpuInfoReader and returns a boolean to indicate whether the reader is enabled. * * <p>Returns {@code true} on success. Otherwise, returns {@code false}. */ public boolean init() { if (mCpuFreqPolicyDirsById.size() > 0) { Loading Loading @@ -139,8 +144,7 @@ public final class CpuInfoReader { Slogf.e(TAG, "Missing proc stat file at %s", mProcStatFile.getAbsolutePath()); return false; } readCpusetCategories(); if (mCpusetCategoriesByCpus.size() == 0) { if (!readCpusetCategories()) { Slogf.e(TAG, "Failed to read cpuset information from %s", mCpusetDir.getAbsolutePath()); return false; } Loading @@ -163,10 +167,19 @@ public final class CpuInfoReader { return true; } public void stopPeriodicCpusetReading() { mShouldReadCpusetCategories.set(false); if (!readCpusetCategories()) { Slogf.e(TAG, "Failed to read cpuset information from %s", mCpusetDir.getAbsolutePath()); mIsEnabled = false; } } /** * 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. * <p>Returns SparseArray keyed by CPU core ID; {@code null} on error or when disabled. */ @Nullable public SparseArray<CpuInfo> readCpuInfos() { Loading @@ -183,6 +196,12 @@ public final class CpuInfoReader { } mLastReadUptimeMillis = uptimeMillis; mLastReadCpuInfos = null; if (mShouldReadCpusetCategories.get() && !readCpusetCategories()) { Slogf.e(TAG, "Failed to read cpuset information from %s", mCpusetDir.getAbsolutePath()); mIsEnabled = false; return null; } SparseArray<CpuUsageStats> cpuUsageStatsByCpus = readLatestCpuUsageStats(); if (cpuUsageStatsByCpus == null || cpuUsageStatsByCpus.size() == 0) { Slogf.e(TAG, "Failed to read latest CPU usage stats"); Loading Loading @@ -324,7 +343,7 @@ public final class CpuInfoReader { /** * Sets the CPU frequency for testing. * * <p>Return {@code true} on success. Otherwise, returns {@code false}. * <p>Returns {@code true} on success. Otherwise, returns {@code false}. */ @VisibleForTesting boolean setCpuFreqDir(File cpuFreqDir) { Loading Loading @@ -354,7 +373,7 @@ public final class CpuInfoReader { /** * Sets the proc stat file for testing. * * <p>Return true on success. Otherwise, returns false. * <p>Returns {@code true} on success. Otherwise, returns {@code false}. */ @VisibleForTesting boolean setProcStatFile(File procStatFile) { Loading @@ -366,6 +385,21 @@ public final class CpuInfoReader { return true; } /** * Set the cpuset directory for testing. * * <p>Returns {@code true} on success. Otherwise, returns {@code false}. */ @VisibleForTesting boolean setCpusetDir(File cpusetDir) { if (!cpusetDir.exists() && !cpusetDir.isDirectory()) { Slogf.e(TAG, "Missing or invalid cpuset directory at %s", cpusetDir.getAbsolutePath()); return false; } mCpusetDir = cpusetDir; return true; } private void populateCpuFreqPolicyDirsById(File[] policyDirs) { mCpuFreqPolicyDirsById.clear(); for (int i = 0; i < policyDirs.length; i++) { Loading @@ -381,12 +415,27 @@ public final class CpuInfoReader { } } private void readCpusetCategories() { /** * Reads cpuset categories by CPU. * * <p>The cpusets are read from the cpuset category specific directories * under the /dev/cpuset directory. The cpuset categories are subject to change at any point * during system bootup, as determined by the init rules specified within the init.rc files. * Therefore, it's necessary to read the cpuset categories each time before accessing CPU usage * statistics until the system boot completes. Once the boot is complete, the latest changes to * the cpuset categories will take a few seconds to propagate. Thus, on boot complete, * the periodic reading is stopped with a delay of * {@link CpuMonitorService#STOP_PERIODIC_CPUSET_READING_DELAY_MILLISECONDS}. * * <p>Returns {@code true} on success. Otherwise, returns {@code false}. */ private boolean readCpusetCategories() { File[] cpusetDirs = mCpusetDir.listFiles(File::isDirectory); if (cpusetDirs == null) { Slogf.e(TAG, "Missing cpuset directories at %s", mCpusetDir.getAbsolutePath()); return; return false; } mCpusetCategoriesByCpus.clear(); for (int i = 0; i < cpusetDirs.length; i++) { File dir = cpusetDirs[i]; @CpusetCategory int cpusetCategory; Loading Loading @@ -418,6 +467,7 @@ public final class CpuInfoReader { } } } return mCpusetCategoriesByCpus.size() > 0; } private void readStaticPolicyInfo() { Loading
services/core/java/com/android/server/cpu/CpuMonitorService.java +26 −2 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static com.android.server.cpu.CpuAvailabilityMonitoringConfig.CPUSET_ALL; import static com.android.server.cpu.CpuAvailabilityMonitoringConfig.CPUSET_BACKGROUND; import static com.android.server.cpu.CpuInfoReader.FLAG_CPUSET_CATEGORY_BACKGROUND; import static com.android.server.cpu.CpuInfoReader.FLAG_CPUSET_CATEGORY_TOP_APP; import static com.android.server.SystemService.PHASE_BOOT_COMPLETED; import android.annotation.Nullable; import android.content.Context; Loading Loading @@ -82,6 +83,15 @@ public final class CpuMonitorService extends SystemService { // frequently. Should this duration be increased as well when this happens? private static final long LATEST_AVAILABILITY_DURATION_MILLISECONDS = TimeUnit.SECONDS.toMillis(30); /** * Delay to stop the periodic cpuset reading after boot complete. * * Device specific implementations can update cpuset on boot complete. This may take * a few seconds to propagate. So, wait for a few minutes before stopping the periodic cpuset * reading. */ private static final long STOP_PERIODIC_CPUSET_READING_DELAY_MILLISECONDS = TimeUnit.MINUTES.toMillis(2); private final Context mContext; private final HandlerThread mHandlerThread; Loading @@ -90,6 +100,7 @@ public final class CpuMonitorService extends SystemService { private final long mNormalMonitoringIntervalMillis; private final long mDebugMonitoringIntervalMillis; private final long mLatestAvailabilityDurationMillis; private final long mStopPeriodicCpusetReadingDelayMillis; private final Object mLock = new Object(); @GuardedBy("mLock") private final SparseArrayMap<CpuMonitorInternal.CpuAvailabilityCallback, Loading Loading @@ -153,13 +164,15 @@ public final class CpuMonitorService extends SystemService { this(context, new CpuInfoReader(), new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, /* allowIo= */ true), Build.IS_USERDEBUG || Build.IS_ENG, NORMAL_MONITORING_INTERVAL_MILLISECONDS, DEBUG_MONITORING_INTERVAL_MILLISECONDS, LATEST_AVAILABILITY_DURATION_MILLISECONDS); DEBUG_MONITORING_INTERVAL_MILLISECONDS, LATEST_AVAILABILITY_DURATION_MILLISECONDS, STOP_PERIODIC_CPUSET_READING_DELAY_MILLISECONDS); } @VisibleForTesting CpuMonitorService(Context context, CpuInfoReader cpuInfoReader, HandlerThread handlerThread, boolean shouldDebugMonitor, long normalMonitoringIntervalMillis, long debugMonitoringIntervalMillis, long latestAvailabilityDurationMillis) { long debugMonitoringIntervalMillis, long latestAvailabilityDurationMillis, long stopPeriodicCpusetReadingDelayMillis) { super(context); mContext = context; mHandlerThread = handlerThread; Loading @@ -167,6 +180,7 @@ public final class CpuMonitorService extends SystemService { mNormalMonitoringIntervalMillis = normalMonitoringIntervalMillis; mDebugMonitoringIntervalMillis = debugMonitoringIntervalMillis; mLatestAvailabilityDurationMillis = latestAvailabilityDurationMillis; mStopPeriodicCpusetReadingDelayMillis = stopPeriodicCpusetReadingDelayMillis; mCpuInfoReader = cpuInfoReader; mCpusetInfosByCpuset = new SparseArray<>(2); mCpusetInfosByCpuset.append(CPUSET_ALL, new CpusetInfo(CPUSET_ALL)); Loading Loading @@ -200,6 +214,16 @@ public final class CpuMonitorService extends SystemService { } } @Override public void onBootPhase(int phase) { if (phase != PHASE_BOOT_COMPLETED) { return; } Slogf.i(TAG, "Stopping periodic cpuset reading on boot complete"); mHandler.postDelayed(() -> mCpuInfoReader.stopPeriodicCpusetReading(), mStopPeriodicCpusetReadingDelayMillis); } @VisibleForTesting long getCurrentMonitoringIntervalMillis() { synchronized (mLock) { Loading
services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpuset_2/background/cpus 0 → 100644 +1 −0 Original line number Diff line number Diff line 0-1
services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpuset_2/top-app/cpus 0 → 100644 +1 −0 Original line number Diff line number Diff line 0-3
services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java +191 −86 File changed.Preview size limit exceeded, changes collapsed. Show changes