diff options
6 files changed, 192 insertions, 331 deletions
diff --git a/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java index 0c30302d63a3..1f261882b2d3 100644 --- a/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java +++ b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java @@ -47,7 +47,7 @@ public class KernelCpuThreadReaderPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); assertNotNull(mKernelCpuThreadReader); while (state.keepRunning()) { - this.mKernelCpuThreadReader.getCurrentProcessCpuUsage(); + this.mKernelCpuThreadReader.getProcessCpuUsage(); } } } diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java index 248e0266b70b..e4de1586bc51 100644 --- a/core/java/com/android/internal/os/KernelCpuThreadReader.java +++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java @@ -32,15 +32,18 @@ import java.util.ArrayList; import java.util.function.Predicate; /** - * Given a process, will iterate over the child threads of the process, and return the CPU usage - * statistics for each child thread. The CPU usage statistics contain the amount of time spent in a - * frequency band. + * Iterates over processes, and all threads owned by those processes, and return the CPU usage for + * each thread. The CPU usage statistics contain the amount of time spent in a frequency band. CPU + * usage is collected using {@link ProcTimeInStateReader}. + * + * <p>We only collect CPU data for processes and threads that are owned by certain UIDs. These UIDs + * are configured via {@link #setUidPredicate}. * * <p>Frequencies are bucketed together to reduce the amount of data created. This means that we - * return less frequencies than provided by {@link ProcTimeInStateReader}. The number of - * frequencies is configurable by {@link #setNumBuckets}. Frequencies are reported as the lowest - * frequency in that range. Frequencies are spread as evenly as possible across the buckets. The - * buckets do not cross over the little/big frequencies reported. + * return less frequencies than provided by {@link ProcTimeInStateReader}. The number of frequencies + * is configurable by {@link #setNumBuckets}. Frequencies are reported as the lowest frequency in + * that range. Frequencies are spread as evenly as possible across the buckets. The buckets do not + * cross over the little/big frequencies reported. * * <p>N.B.: In order to bucket across little/big frequencies correctly, we assume that the {@code * time_in_state} file contains every little core frequency in ascending order, followed by every @@ -60,56 +63,39 @@ public class KernelCpuThreadReader { private static final String CPU_STATISTICS_FILENAME = "time_in_state"; /** - * The name of the file to read process command line invocation from, must be found in - * {@code /proc/$PID/} + * The name of the file to read process command line invocation from, must be found in {@code + * /proc/$PID/} */ private static final String PROCESS_NAME_FILENAME = "cmdline"; /** - * The name of the file to read thread name from, must be found in - * {@code /proc/$PID/task/$TID} + * The name of the file to read thread name from, must be found in {@code /proc/$PID/task/$TID} */ private static final String THREAD_NAME_FILENAME = "comm"; - /** - * Glob pattern for the process directory names under {@code proc} - */ + /** Glob pattern for the process directory names under {@code proc} */ private static final String PROCESS_DIRECTORY_FILTER = "[0-9]*"; - /** - * Default process name when the name can't be read - */ + /** Default process name when the name can't be read */ private static final String DEFAULT_PROCESS_NAME = "unknown_process"; - /** - * Default thread name when the name can't be read - */ + /** Default thread name when the name can't be read */ private static final String DEFAULT_THREAD_NAME = "unknown_thread"; - /** - * Default mount location of the {@code proc} filesystem - */ + /** Default mount location of the {@code proc} filesystem */ private static final Path DEFAULT_PROC_PATH = Paths.get("/proc"); - /** - * The initial {@code time_in_state} file for {@link ProcTimeInStateReader} - */ + /** The initial {@code time_in_state} file for {@link ProcTimeInStateReader} */ private static final Path DEFAULT_INITIAL_TIME_IN_STATE_PATH = DEFAULT_PROC_PATH.resolve("self/time_in_state"); - /** - * Value returned when there was an error getting an integer ID value (e.g. PID, UID) - */ + /** Value returned when there was an error getting an integer ID value (e.g. PID, UID) */ private static final int ID_ERROR = -1; - /** - * Thread ID used when reporting CPU used by other threads - */ + /** Thread ID used when reporting CPU used by other threads */ private static final int OTHER_THREADS_ID = -1; - /** - * Thread name used when reporting CPU used by other threads - */ + /** Thread name used when reporting CPU used by other threads */ private static final String OTHER_THREADS_NAME = "__OTHER_THREADS"; /** @@ -124,9 +110,7 @@ public class KernelCpuThreadReader { */ private int mMinimumTotalCpuUsageMillis; - /** - * Where the proc filesystem is mounted - */ + /** Where the proc filesystem is mounted */ private final Path mProcPath; /** @@ -135,14 +119,10 @@ public class KernelCpuThreadReader { */ private int[] mFrequenciesKhz; - /** - * Used to read and parse {@code time_in_state} files - */ + /** Used to read and parse {@code time_in_state} files */ private final ProcTimeInStateReader mProcTimeInStateReader; - /** - * Used to sort frequencies and usage times into buckets - */ + /** Used to sort frequencies and usage times into buckets */ private FrequencyBucketCreator mFrequencyBucketCreator; private final Injector mInjector; @@ -150,10 +130,9 @@ public class KernelCpuThreadReader { /** * Create with a path where `proc` is mounted. Used primarily for testing * - * @param procPath where `proc` is mounted (to find, see {@code mount | grep - * ^proc}) + * @param procPath where `proc` is mounted (to find, see {@code mount | grep ^proc}) * @param initialTimeInStatePath where the initial {@code time_in_state} file exists to define - * format + * format */ @VisibleForTesting public KernelCpuThreadReader( @@ -162,7 +141,8 @@ public class KernelCpuThreadReader { int minimumTotalCpuUsageMillis, Path procPath, Path initialTimeInStatePath, - Injector injector) throws IOException { + Injector injector) + throws IOException { mUidPredicate = uidPredicate; mMinimumTotalCpuUsageMillis = minimumTotalCpuUsageMillis; mProcPath = procPath; @@ -205,7 +185,7 @@ public class KernelCpuThreadReader { * #setUidPredicate}. */ @Nullable - public ArrayList<ProcessCpuUsage> getProcessCpuUsageByUids() { + public ArrayList<ProcessCpuUsage> getProcessCpuUsage() { if (DEBUG) { Slog.d(TAG, "Reading CPU thread usages for processes owned by UIDs"); } @@ -213,7 +193,7 @@ public class KernelCpuThreadReader { final ArrayList<ProcessCpuUsage> processCpuUsages = new ArrayList<>(); try (DirectoryStream<Path> processPaths = - Files.newDirectoryStream(mProcPath, PROCESS_DIRECTORY_FILTER)) { + Files.newDirectoryStream(mProcPath, PROCESS_DIRECTORY_FILTER)) { for (Path processPath : processPaths) { final int processId = getProcessId(processPath); final int uid = mInjector.getUidForPid(processId); @@ -231,7 +211,7 @@ public class KernelCpuThreadReader { } } } catch (IOException e) { - Slog.w("Failed to iterate over process paths", e); + Slog.w(TAG, "Failed to iterate over process paths", e); return null; } @@ -248,30 +228,68 @@ public class KernelCpuThreadReader { } /** - * Read all of the CPU usage statistics for each child thread of the current process - * - * @return process CPU usage containing usage of all child threads + * Get the CPU frequencies that correspond to the times reported in {@link + * ThreadCpuUsage#usageTimesMillis} */ @Nullable - public ProcessCpuUsage getCurrentProcessCpuUsage() { - return getProcessCpuUsage(mProcPath.resolve("self"), mInjector.myPid(), mInjector.myUid()); + public int[] getCpuFrequenciesKhz() { + return mFrequenciesKhz; + } + + /** Set the number of frequency buckets to use */ + void setNumBuckets(int numBuckets) { + if (numBuckets < 1) { + Slog.w(TAG, "Number of buckets must be at least 1, but was " + numBuckets); + return; + } + // If `numBuckets` hasn't changed since the last set, do nothing + if (mFrequenciesKhz != null && mFrequenciesKhz.length == numBuckets) { + return; + } + mFrequencyBucketCreator = + new FrequencyBucketCreator(mProcTimeInStateReader.getFrequenciesKhz(), numBuckets); + mFrequenciesKhz = + mFrequencyBucketCreator.getBucketMinFrequencies( + mProcTimeInStateReader.getFrequenciesKhz()); + } + + /** Set the UID predicate for {@link #getProcessCpuUsage} */ + void setUidPredicate(Predicate<Integer> uidPredicate) { + mUidPredicate = uidPredicate; + } + + /** + * If a thread has strictly less than {@code minimumTotalCpuUsageMillis} total CPU usage, it + * will not be reported + */ + void setMinimumTotalCpuUsageMillis(int minimumTotalCpuUsageMillis) { + if (minimumTotalCpuUsageMillis < 0) { + Slog.w(TAG, "Negative minimumTotalCpuUsageMillis: " + minimumTotalCpuUsageMillis); + return; + } + mMinimumTotalCpuUsageMillis = minimumTotalCpuUsageMillis; } /** * Read all of the CPU usage statistics for each child thread of a process * * @param processPath the {@code /proc} path of the thread - * @param processId the ID of the process - * @param uid the ID of the user who owns the process + * @param processId the ID of the process + * @param uid the ID of the user who owns the process * @return process CPU usage containing usage of all child threads. Null if the process exited - * and its {@code proc} directory was removed while collecting information + * and its {@code proc} directory was removed while collecting information */ @Nullable private ProcessCpuUsage getProcessCpuUsage(Path processPath, int processId, int uid) { if (DEBUG) { - Slog.d(TAG, "Reading CPU thread usages with directory " + processPath - + " process ID " + processId - + " and user ID " + uid); + Slog.d( + TAG, + "Reading CPU thread usages with directory " + + processPath + + " process ID " + + processId + + " and user ID " + + uid); } int[] filteredThreadsCpuUsage = null; @@ -305,64 +323,15 @@ public class KernelCpuThreadReader { // Add the filtered out thread CPU usage under an "other threads" ThreadCpuUsage if (filteredThreadsCpuUsage != null) { - threadCpuUsages.add(new ThreadCpuUsage( - OTHER_THREADS_ID, OTHER_THREADS_NAME, filteredThreadsCpuUsage)); + threadCpuUsages.add( + new ThreadCpuUsage( + OTHER_THREADS_ID, OTHER_THREADS_NAME, filteredThreadsCpuUsage)); } if (DEBUG) { Slog.d(TAG, "Read CPU usage of " + threadCpuUsages.size() + " threads"); } - return new ProcessCpuUsage( - processId, - getProcessName(processPath), - uid, - threadCpuUsages); - } - - /** - * Set the number of frequency buckets to use - */ - void setNumBuckets(int numBuckets) { - if (numBuckets < 1) { - Slog.w(TAG, "Number of buckets must be at least 1, but was " + numBuckets); - return; - } - // If `numBuckets` hasn't changed since the last set, do nothing - if (mFrequenciesKhz != null && mFrequenciesKhz.length == numBuckets) { - return; - } - mFrequencyBucketCreator = new FrequencyBucketCreator( - mProcTimeInStateReader.getFrequenciesKhz(), numBuckets); - mFrequenciesKhz = mFrequencyBucketCreator.getBucketMinFrequencies( - mProcTimeInStateReader.getFrequenciesKhz()); - } - - /** - * Set the UID predicate for {@link #getProcessCpuUsageByUids} - */ - void setUidPredicate(Predicate<Integer> uidPredicate) { - mUidPredicate = uidPredicate; - } - - /** - * If a thread has strictly less than {@code minimumTotalCpuUsageMillis} total CPU usage, it - * will not be reported - */ - void setMinimumTotalCpuUsageMillis(int minimumTotalCpuUsageMillis) { - if (minimumTotalCpuUsageMillis < 0) { - Slog.w(TAG, "Negative minimumTotalCpuUsageMillis: " + minimumTotalCpuUsageMillis); - return; - } - mMinimumTotalCpuUsageMillis = minimumTotalCpuUsageMillis; - } - - /** - * Get the CPU frequencies that correspond to the times reported in - * {@link ThreadCpuUsage#usageTimesMillis} - */ - @Nullable - public int[] getCpuFrequenciesKhz() { - return mFrequenciesKhz; + return new ProcessCpuUsage(processId, getProcessName(processPath), uid, threadCpuUsages); } /** @@ -370,7 +339,7 @@ public class KernelCpuThreadReader { * * @param threadDirectory the {@code /proc} directory of the thread * @return thread CPU usage. Null if the thread exited and its {@code proc} directory was - * removed while collecting information + * removed while collecting information */ @Nullable private ThreadCpuUsage getThreadCpuUsage(Path threadDirectory) { @@ -398,27 +367,21 @@ public class KernelCpuThreadReader { return new ThreadCpuUsage(threadId, threadName, cpuUsages); } - /** - * Get the command used to start a process - */ + /** Get the command used to start a process */ private String getProcessName(Path processPath) { final Path processNamePath = processPath.resolve(PROCESS_NAME_FILENAME); - final String processName = - ProcStatsUtil.readSingleLineProcFile(processNamePath.toString()); + final String processName = ProcStatsUtil.readSingleLineProcFile(processNamePath.toString()); if (processName != null) { return processName; } return DEFAULT_PROCESS_NAME; } - /** - * Get the name of a thread, given the {@code /proc} path of the thread - */ + /** Get the name of a thread, given the {@code /proc} path of the thread */ private String getThreadName(Path threadPath) { final Path threadNamePath = threadPath.resolve(THREAD_NAME_FILENAME); - final String threadName = - ProcStatsUtil.readNullSeparatedFile(threadNamePath.toString()); + final String threadName = ProcStatsUtil.readNullSeparatedFile(threadNamePath.toString()); if (threadName == null) { return DEFAULT_THREAD_NAME; } @@ -441,9 +404,8 @@ public class KernelCpuThreadReader { } } - /** - * Get the sum of all CPU usage across all frequencies - */ + /** Get the sum of all CPU usage across all frequencies */ + @SuppressWarnings("ForLoopReplaceableByForEach") private static int totalCpuUsage(int[] cpuUsage) { int total = 0; for (int i = 0; i < cpuUsage.length; i++) { @@ -452,9 +414,7 @@ public class KernelCpuThreadReader { return total; } - /** - * Add two CPU frequency usages together - */ + /** Add two CPU frequency usages together */ private static int[] sumCpuUsage(int[] a, int[] b) { int[] summed = new int[a.length]; for (int i = 0; i < a.length; i++) { @@ -463,9 +423,7 @@ public class KernelCpuThreadReader { return summed; } - /** - * Puts frequencies and usage times into buckets - */ + /** Puts frequencies and usage times into buckets */ @VisibleForTesting public static class FrequencyBucketCreator { private final int mNumBuckets; @@ -480,7 +438,7 @@ public class KernelCpuThreadReader { * Buckets based of a list of frequencies * * @param frequencies the frequencies to base buckets off - * @param numBuckets how many buckets to create + * @param numBuckets how many buckets to create */ @VisibleForTesting public FrequencyBucketCreator(long[] frequencies, int numBuckets) { @@ -502,20 +460,20 @@ public class KernelCpuThreadReader { // Ensure that we don't have more buckets than frequencies mLittleNumBuckets = Math.min(littleNumBuckets, mBigFrequenciesStartIndex); - mBigNumBuckets = Math.min( - bigNumBuckets, frequencies.length - mBigFrequenciesStartIndex); + mBigNumBuckets = + Math.min(bigNumBuckets, frequencies.length - mBigFrequenciesStartIndex); mNumBuckets = mLittleNumBuckets + mBigNumBuckets; // Set the size of each little and big bucket. If they have no buckets, the size is zero - mLittleBucketSize = mLittleNumBuckets == 0 ? 0 : - mBigFrequenciesStartIndex / mLittleNumBuckets; - mBigBucketSize = mBigNumBuckets == 0 ? 0 : - (frequencies.length - mBigFrequenciesStartIndex) / mBigNumBuckets; + mLittleBucketSize = + mLittleNumBuckets == 0 ? 0 : mBigFrequenciesStartIndex / mLittleNumBuckets; + mBigBucketSize = + mBigNumBuckets == 0 + ? 0 + : (frequencies.length - mBigFrequenciesStartIndex) / mBigNumBuckets; } - /** - * Find the index where frequencies change from little core to big core - */ + /** Find the index where frequencies change from little core to big core */ @VisibleForTesting public static int getBigFrequenciesStartIndex(long[] frequenciesKhz) { for (int i = 0; i < frequenciesKhz.length - 1; i++) { @@ -527,16 +485,14 @@ public class KernelCpuThreadReader { return frequenciesKhz.length; } - /** - * Get the minimum frequency in each bucket - */ + /** Get the minimum frequency in each bucket */ @VisibleForTesting public int[] getBucketMinFrequencies(long[] frequenciesKhz) { Preconditions.checkArgument(frequenciesKhz.length == mNumFrequencies); // If there's only one bucket, we bucket everything together so the first bucket is the // min frequency if (mNumBuckets == 1) { - return new int[]{(int) frequenciesKhz[0]}; + return new int[] {(int) frequenciesKhz[0]}; } final int[] bucketMinFrequencies = new int[mNumBuckets]; @@ -561,6 +517,7 @@ public class KernelCpuThreadReader { * @return the bucketed usage times */ @VisibleForTesting + @SuppressWarnings("ForLoopReplaceableByForEach") public int[] getBucketedValues(long[] values) { Preconditions.checkArgument(values.length == mNumFrequencies); final int[] bucketed = new int[mNumBuckets]; @@ -580,18 +537,18 @@ public class KernelCpuThreadReader { } // Initialize the big buckets for (int i = mBigFrequenciesStartIndex; i < values.length; i++) { - final int bucketIndex = Math.min( - mLittleNumBuckets + (i - mBigFrequenciesStartIndex) / mBigBucketSize, - mNumBuckets - 1); + final int bucketIndex = + Math.min( + mLittleNumBuckets + + (i - mBigFrequenciesStartIndex) / mBigBucketSize, + mNumBuckets - 1); bucketed[bucketIndex] += values[i]; } return bucketed; } } - /** - * CPU usage of a process - */ + /** CPU usage of a process */ public static class ProcessCpuUsage { public final int processId; public final String processName; @@ -610,46 +567,23 @@ public class KernelCpuThreadReader { } } - /** - * CPU usage of a thread - */ + /** CPU usage of a thread */ public static class ThreadCpuUsage { public final int threadId; public final String threadName; public final int[] usageTimesMillis; - ThreadCpuUsage( - int threadId, - String threadName, - int[] usageTimesMillis) { + ThreadCpuUsage(int threadId, String threadName, int[] usageTimesMillis) { this.threadId = threadId; this.threadName = threadName; this.usageTimesMillis = usageTimesMillis; } } - /** - * Used to inject static methods from {@link Process} - */ + /** Used to inject static methods from {@link Process} */ @VisibleForTesting public static class Injector { - /** - * Get the PID of the current process - */ - public int myPid() { - return Process.myPid(); - } - - /** - * Get the UID that owns the current process - */ - public int myUid() { - return Process.myUid(); - } - - /** - * Get the UID for the process with ID {@code pid} - */ + /** Get the UID for the process with ID {@code pid} */ public int getUidForPid(int pid) { return Process.getUidForPid(pid); } diff --git a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java index b8dfe0d63a66..3851ce6d9cbd 100644 --- a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java +++ b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java @@ -47,33 +47,29 @@ import java.util.regex.Pattern; public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { private static final String TAG = "KernelCpuThreadReaderSettingsObserver"; - /** - * The number of frequency buckets to report - */ + /** The number of frequency buckets to report */ private static final String NUM_BUCKETS_SETTINGS_KEY = "num_buckets"; + private static final int NUM_BUCKETS_DEFAULT = 8; - /** - * List of UIDs to report data for - */ + /** List of UIDs to report data for */ private static final String COLLECTED_UIDS_SETTINGS_KEY = "collected_uids"; + private static final String COLLECTED_UIDS_DEFAULT = "0-0;1000-1000"; - /** - * Minimum total CPU usage to report - */ + /** Minimum total CPU usage to report */ private static final String MINIMUM_TOTAL_CPU_USAGE_MILLIS_SETTINGS_KEY = "minimum_total_cpu_usage_millis"; + private static final int MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT = 10000; private final Context mContext; - @Nullable - private final KernelCpuThreadReader mKernelCpuThreadReader; + @Nullable private final KernelCpuThreadReader mKernelCpuThreadReader; /** - * @return returns a created {@link KernelCpuThreadReader} that will be modified by any - * change in settings, returns null if creation failed + * @return returns a created {@link KernelCpuThreadReader} that will be modified by any change + * in settings, returns null if creation failed */ @Nullable public static KernelCpuThreadReader getSettingsModifiedReader(Context context) { @@ -81,10 +77,10 @@ public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { KernelCpuThreadReaderSettingsObserver settingsObserver = new KernelCpuThreadReaderSettingsObserver(context); // Register the observer to listen for setting changes - Uri settingsUri = - Settings.Global.getUriFor(Settings.Global.KERNEL_CPU_THREAD_READER); - context.getContentResolver().registerContentObserver( - settingsUri, false, settingsObserver, UserHandle.USER_SYSTEM); + Uri settingsUri = Settings.Global.getUriFor(Settings.Global.KERNEL_CPU_THREAD_READER); + context.getContentResolver() + .registerContentObserver( + settingsUri, false, settingsObserver, UserHandle.USER_SYSTEM); // Return the observer's reader return settingsObserver.mKernelCpuThreadReader; } @@ -92,10 +88,11 @@ public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { private KernelCpuThreadReaderSettingsObserver(Context context) { super(BackgroundThread.getHandler()); mContext = context; - mKernelCpuThreadReader = KernelCpuThreadReader.create( - NUM_BUCKETS_DEFAULT, - UidPredicate.fromString(COLLECTED_UIDS_DEFAULT), - MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT); + mKernelCpuThreadReader = + KernelCpuThreadReader.create( + NUM_BUCKETS_DEFAULT, + UidPredicate.fromString(COLLECTED_UIDS_DEFAULT), + MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT); } @Override @@ -103,9 +100,7 @@ public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { updateReader(); } - /** - * Update the reader with new settings - */ + /** Update the reader with new settings */ private void updateReader() { if (mKernelCpuThreadReader == null) { return; @@ -113,8 +108,10 @@ public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { final KeyValueListParser parser = new KeyValueListParser(','); try { - parser.setString(Settings.Global.getString( - mContext.getContentResolver(), Settings.Global.KERNEL_CPU_THREAD_READER)); + parser.setString( + Settings.Global.getString( + mContext.getContentResolver(), + Settings.Global.KERNEL_CPU_THREAD_READER)); } catch (IllegalArgumentException e) { Slog.e(TAG, "Bad settings", e); return; @@ -122,8 +119,9 @@ public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { final UidPredicate uidPredicate; try { - uidPredicate = UidPredicate.fromString( - parser.getString(COLLECTED_UIDS_SETTINGS_KEY, COLLECTED_UIDS_DEFAULT)); + uidPredicate = + UidPredicate.fromString( + parser.getString(COLLECTED_UIDS_SETTINGS_KEY, COLLECTED_UIDS_DEFAULT)); } catch (NumberFormatException e) { Slog.w(TAG, "Failed to get UID predicate", e); return; @@ -132,14 +130,13 @@ public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { mKernelCpuThreadReader.setNumBuckets( parser.getInt(NUM_BUCKETS_SETTINGS_KEY, NUM_BUCKETS_DEFAULT)); mKernelCpuThreadReader.setUidPredicate(uidPredicate); - mKernelCpuThreadReader.setMinimumTotalCpuUsageMillis(parser.getInt( - MINIMUM_TOTAL_CPU_USAGE_MILLIS_SETTINGS_KEY, - MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT)); + mKernelCpuThreadReader.setMinimumTotalCpuUsageMillis( + parser.getInt( + MINIMUM_TOTAL_CPU_USAGE_MILLIS_SETTINGS_KEY, + MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT)); } - /** - * Check whether a UID belongs to a set of UIDs - */ + /** Check whether a UID belongs to a set of UIDs */ @VisibleForTesting public static class UidPredicate implements Predicate<Integer> { private static final Pattern UID_RANGE_PATTERN = Pattern.compile("([0-9]+)-([0-9]+)"); @@ -150,14 +147,14 @@ public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { * Create a UID predicate from a string representing a list of UID ranges * * <p>UID ranges are a pair of integers separated by a '-'. If you want to specify a single - * UID (e.g. UID 1000), you can use {@code 1000-1000}. Lists of ranges are separated by - * a single ';'. For example, this would be a valid string representation: {@code + * UID (e.g. UID 1000), you can use {@code 1000-1000}. Lists of ranges are separated by a + * single ';'. For example, this would be a valid string representation: {@code * "1000-1999;2003-2003;2004-2004;2050-2060"}. * * <p>We do not use ',' to delimit as it is already used in separating different setting * arguments. * - * @throws NumberFormatException if the input string is incorrectly formatted + * @throws NumberFormatException if the input string is incorrectly formatted * @throws IllegalArgumentException if an UID range has a lower end than start */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) @@ -169,9 +166,10 @@ public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { throw new NumberFormatException( "Failed to recognize as number range: " + uidSpecifier); } - acceptedUidRanges.add(Range.create( - Integer.parseInt(uidRangeMatcher.group(1)), - Integer.parseInt(uidRangeMatcher.group(2)))); + acceptedUidRanges.add( + Range.create( + Integer.parseInt(uidRangeMatcher.group(1)), + Integer.parseInt(uidRangeMatcher.group(2)))); } return new UidPredicate(acceptedUidRanges); } @@ -181,6 +179,7 @@ public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { } @Override + @SuppressWarnings("ForLoopReplaceableByForEach") public boolean test(Integer uid) { for (int i = 0; i < mAcceptedUidRanges.size(); i++) { if (mAcceptedUidRanges.get(i).contains(uid)) { diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java index 1c84829c5491..e9cad0acfcc6 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java @@ -35,6 +35,7 @@ import org.junit.runner.RunWith; import java.time.Duration; import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.OptionalDouble; import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; @@ -126,17 +127,21 @@ public class KernelCpuThreadReaderEndToEndTest { final KernelCpuThreadReader kernelCpuThreadReader = KernelCpuThreadReader.create(8, uid -> uid == Process.myUid(), 0); assertNotNull(kernelCpuThreadReader); - final ProcessCpuUsage currentProcessCpuUsage = - kernelCpuThreadReader.getCurrentProcessCpuUsage(); + kernelCpuThreadReader.setUidPredicate(uid -> uid == Process.myUid()); + final Optional<ProcessCpuUsage> currentProcessCpuUsage = + kernelCpuThreadReader.getProcessCpuUsage().stream() + .filter(p -> p.processId == Process.myPid()) + .findFirst(); + assertTrue(currentProcessCpuUsage.isPresent()); // Threads can terminate, as we've finished crawling them from /proc threadFinishedLatch.countDown(); // Check that we've got times for every thread we spawned - final List<ThreadCpuUsage> threadCpuUsages = currentProcessCpuUsage.threadCpuUsages - .stream() - .filter((thread) -> thread.threadName.startsWith(tag)) - .collect(Collectors.toList()); + final List<ThreadCpuUsage> threadCpuUsages = + currentProcessCpuUsage.get().threadCpuUsages.stream() + .filter((thread) -> thread.threadName.startsWith(tag)) + .collect(Collectors.toList()); assertEquals( "Incorrect number of threads returned by KernelCpuThreadReader", numSamples, threadCpuUsages.size()); diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java index e6e7a85ae16e..61209e21dda8 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java @@ -25,6 +25,7 @@ import static org.testng.Assert.assertThrows; import android.content.Context; import android.os.FileUtils; +import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -44,27 +45,10 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.function.Predicate; +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class KernelCpuThreadReaderTest { - - private static final int UID = 1000; - private static final int PROCESS_ID = 1234; - private static final int[] THREAD_IDS = {0, 1000, 1235, 4321}; - private static final String PROCESS_NAME = "test_process"; - private static final String[] THREAD_NAMES = { - "test_thread_1", "test_thread_2", "test_thread_3", "test_thread_4" - }; - private static final int[] THREAD_CPU_FREQUENCIES = { - 1000, 2000, 3000, 4000, - }; - private static final int[][] THREAD_CPU_TIMES = { - {1, 0, 0, 1}, - {0, 0, 0, 0}, - {1000, 1000, 1000, 1000}, - {0, 1, 2, 3}, - }; - private File mProcDirectory; @Before @@ -79,41 +63,6 @@ public class KernelCpuThreadReaderTest { } @Test - public void testReader_currentProcess() throws IOException { - KernelCpuThreadReader.Injector processUtils = - new KernelCpuThreadReader.Injector() { - @Override - public int myPid() { - return PROCESS_ID; - } - - @Override - public int myUid() { - return UID; - } - - @Override - public int getUidForPid(int pid) { - return 0; - } - }; - setupDirectory(mProcDirectory.toPath().resolve("self"), THREAD_IDS, PROCESS_NAME, - THREAD_NAMES, THREAD_CPU_FREQUENCIES, THREAD_CPU_TIMES); - - final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader( - 8, - uid -> 1000 <= uid && uid < 2000, - 0, - mProcDirectory.toPath(), - mProcDirectory.toPath().resolve("self/task/" + THREAD_IDS[0] + "/time_in_state"), - processUtils); - final KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = - kernelCpuThreadReader.getCurrentProcessCpuUsage(); - checkResults(processCpuUsage, kernelCpuThreadReader.getCpuFrequenciesKhz(), UID, PROCESS_ID, - THREAD_IDS, PROCESS_NAME, THREAD_NAMES, THREAD_CPU_FREQUENCIES, THREAD_CPU_TIMES); - } - - @Test public void testReader_byUids() throws IOException { int[] uids = new int[]{0, 2, 3, 4, 5, 6000}; Predicate<Integer> uidPredicate = uid -> uid == 0 || uid >= 4; @@ -121,16 +70,6 @@ public class KernelCpuThreadReaderTest { KernelCpuThreadReader.Injector processUtils = new KernelCpuThreadReader.Injector() { @Override - public int myPid() { - return 0; - } - - @Override - public int myUid() { - return 0; - } - - @Override public int getUidForPid(int pid) { return pid; } @@ -150,7 +89,7 @@ public class KernelCpuThreadReaderTest { mProcDirectory.toPath().resolve(uids[0] + "/task/" + uids[0] + "/time_in_state"), processUtils); ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsageByUids = - kernelCpuThreadReader.getProcessCpuUsageByUids(); + kernelCpuThreadReader.getProcessCpuUsage(); processCpuUsageByUids.sort(Comparator.comparing(usage -> usage.processId)); assertEquals(expectedUids.length, processCpuUsageByUids.size()); @@ -173,16 +112,6 @@ public class KernelCpuThreadReaderTest { KernelCpuThreadReader.Injector processUtils = new KernelCpuThreadReader.Injector() { @Override - public int myPid() { - return 0; - } - - @Override - public int myUid() { - return 0; - } - - @Override public int getUidForPid(int pid) { return pid; } @@ -206,7 +135,7 @@ public class KernelCpuThreadReaderTest { mProcDirectory.toPath().resolve(uids[0] + "/task/" + uids[0] + "/time_in_state"), processUtils); ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsageByUids = - kernelCpuThreadReader.getProcessCpuUsageByUids(); + kernelCpuThreadReader.getProcessCpuUsage(); processCpuUsageByUids.sort(Comparator.comparing(usage -> usage.uid)); assertEquals(expectedUids.length, processCpuUsageByUids.size()); @@ -220,7 +149,7 @@ public class KernelCpuThreadReaderTest { @Test public void testReader_otherThreads() throws IOException { - final Path processPath = mProcDirectory.toPath().resolve("self"); + final Path processPath = mProcDirectory.toPath().resolve("1000"); setupDirectory( processPath, new int[]{1, 2, 3}, @@ -228,39 +157,34 @@ public class KernelCpuThreadReaderTest { new String[]{"thread1", "thread2", "thread3"}, new int[]{1000, 2000}, new int[][]{{0, 100}, {10, 0}, {0, 300}}); - final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader( - 8, - i -> true, - 2000, - mProcDirectory.toPath(), - processPath.resolve("task/1/time_in_state"), + KernelCpuThreadReader.Injector injector = new KernelCpuThreadReader.Injector() { @Override - public int myPid() { - return 1000; - } - - @Override - public int myUid() { - return 0; - } - - @Override public int getUidForPid(int pid) { return 0; } - }); + }; + final KernelCpuThreadReader kernelCpuThreadReader = + new KernelCpuThreadReader( + 8, + uid -> true, + 2000, + mProcDirectory.toPath(), + processPath.resolve("task/1/time_in_state"), + injector); + ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages = + kernelCpuThreadReader.getProcessCpuUsage(); + assertEquals(1, processCpuUsages.size()); checkResults( - kernelCpuThreadReader.getCurrentProcessCpuUsage(), + processCpuUsages.get(0), kernelCpuThreadReader.getCpuFrequenciesKhz(), 0, 1000, - new int[]{-1, 3}, + new int[] {-1, 3}, "process", - new String[]{"__OTHER_THREADS", "thread3"}, - new int[]{1000, 2000}, - new int[][]{{100, 1000}, {0, 3000}} - ); + new String[] {"__OTHER_THREADS", "thread3"}, + new int[] {1000, 2000}, + new int[][] {{10, 100}, {0, 300}}); } private void setupDirectory(Path processPath, int[] threadIds, String processName, @@ -289,8 +213,7 @@ public class KernelCpuThreadReaderTest { final OutputStream timeInStateStream = Files.newOutputStream(threadPath.resolve("time_in_state")); for (int j = 0; j < cpuFrequencies.length; j++) { - final String line = String.valueOf(cpuFrequencies[j]) + " " - + String.valueOf(cpuTimes[i][j]) + "\n"; + final String line = cpuFrequencies[j] + " " + cpuTimes[i][j] + "\n"; timeInStateStream.write(line.getBytes()); } timeInStateStream.close(); diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 2b17d19e1cf7..9f1eace32180 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -1721,7 +1721,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { throw new IllegalStateException("mKernelCpuThreadReader is null"); } ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages = - this.mKernelCpuThreadReader.getProcessCpuUsageByUids(); + this.mKernelCpuThreadReader.getProcessCpuUsage(); if (processCpuUsages == null) { throw new IllegalStateException("processCpuUsages is null"); } |