Loading core/java/com/android/internal/os/KernelCpuThreadReader.java +56 −16 Original line number Diff line number Diff line Loading @@ -102,6 +102,16 @@ public class KernelCpuThreadReader { */ private static final int ID_ERROR = -1; /** * 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 */ private static final String OTHER_THREADS_NAME = "__OTHER_THREADS"; /** * When checking whether to report data for a thread, we check the UID of the thread's owner * against this predicate Loading Loading @@ -140,7 +150,8 @@ 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 */ Loading Loading @@ -263,14 +274,24 @@ public class KernelCpuThreadReader { + " and user ID " + uid); } int[] filteredThreadsCpuUsage = null; final Path allThreadsPath = processPath.resolve("task"); final ArrayList<ThreadCpuUsage> threadCpuUsages = new ArrayList<>(); try (DirectoryStream<Path> threadPaths = Files.newDirectoryStream(allThreadsPath)) { for (Path threadDirectory : threadPaths) { ThreadCpuUsage threadCpuUsage = getThreadCpuUsage(threadDirectory); if (threadCpuUsage != null) { threadCpuUsages.add(threadCpuUsage); if (threadCpuUsage == null) { continue; } if (mMinimumTotalCpuUsageMillis < totalCpuUsage(threadCpuUsage.usageTimesMillis)) { if (filteredThreadsCpuUsage == null) { filteredThreadsCpuUsage = new int[mFrequenciesKhz.length]; } filteredThreadsCpuUsage = sumCpuUsage(filteredThreadsCpuUsage, threadCpuUsage.usageTimesMillis); continue; } threadCpuUsages.add(threadCpuUsage); } } catch (IOException e) { // Expected when a process finishes Loading @@ -282,6 +303,12 @@ public class KernelCpuThreadReader { return null; } // 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)); } if (DEBUG) { Slog.d(TAG, "Read CPU usage of " + threadCpuUsages.size() + " threads"); } Loading Loading @@ -368,15 +395,6 @@ public class KernelCpuThreadReader { } int[] cpuUsages = mFrequencyBucketCreator.getBucketedValues(cpuUsagesLong); // Check if the total CPU usage below the threshold int totalCpuUsage = 0; for (int i = 0; i < cpuUsages.length; i++) { totalCpuUsage += cpuUsages[i]; } if (totalCpuUsage < mMinimumTotalCpuUsageMillis) { return null; } return new ThreadCpuUsage(threadId, threadName, cpuUsages); } Loading Loading @@ -423,6 +441,28 @@ public class KernelCpuThreadReader { } } /** * Get the sum of all CPU usage across all frequencies */ private static int totalCpuUsage(int[] cpuUsage) { int total = 0; for (int i = 0; i < cpuUsage.length; i++) { total += cpuUsage[i]; } return total; } /** * 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++) { summed[i] = a[i] + b[i]; } return summed; } /** * Puts frequencies and usage times into buckets */ Loading core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java +46 −0 Original line number Diff line number Diff line Loading @@ -218,6 +218,51 @@ public class KernelCpuThreadReaderTest { } @Test public void testReader_otherThreads() throws IOException { final Path processPath = mProcDirectory.toPath().resolve("self"); setupDirectory( processPath, new int[]{1, 2, 3}, "process", 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"), new KernelCpuThreadReader.Injector() { @Override public int myPid() { return 1000; } @Override public int myUid() { return 0; } @Override public int getUidForPid(int pid) { return 0; } }); checkResults( kernelCpuThreadReader.getCurrentProcessCpuUsage(), kernelCpuThreadReader.getCpuFrequenciesKhz(), 0, 1000, new int[]{-1, 3}, "process", new String[]{"__OTHER_THREADS", "thread3"}, new int[]{1000, 2000}, new int[][]{{100, 1000}, {0, 3000}} ); } private void setupDirectory(Path processPath, int[] threadIds, String processName, String[] threadNames, int[] cpuFrequencies, int[][] cpuTimes) throws IOException { // Make /proc/$PID Loading Loading @@ -259,6 +304,7 @@ public class KernelCpuThreadReaderTest { assertEquals(processId, processCpuUsage.processId); assertEquals(uid, processCpuUsage.uid); assertEquals(processName, processCpuUsage.processName); assertEquals(threadIds.length, processCpuUsage.threadCpuUsages.size()); // Sort the thread CPU usages to compare with test case final ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages = Loading Loading
core/java/com/android/internal/os/KernelCpuThreadReader.java +56 −16 Original line number Diff line number Diff line Loading @@ -102,6 +102,16 @@ public class KernelCpuThreadReader { */ private static final int ID_ERROR = -1; /** * 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 */ private static final String OTHER_THREADS_NAME = "__OTHER_THREADS"; /** * When checking whether to report data for a thread, we check the UID of the thread's owner * against this predicate Loading Loading @@ -140,7 +150,8 @@ 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 */ Loading Loading @@ -263,14 +274,24 @@ public class KernelCpuThreadReader { + " and user ID " + uid); } int[] filteredThreadsCpuUsage = null; final Path allThreadsPath = processPath.resolve("task"); final ArrayList<ThreadCpuUsage> threadCpuUsages = new ArrayList<>(); try (DirectoryStream<Path> threadPaths = Files.newDirectoryStream(allThreadsPath)) { for (Path threadDirectory : threadPaths) { ThreadCpuUsage threadCpuUsage = getThreadCpuUsage(threadDirectory); if (threadCpuUsage != null) { threadCpuUsages.add(threadCpuUsage); if (threadCpuUsage == null) { continue; } if (mMinimumTotalCpuUsageMillis < totalCpuUsage(threadCpuUsage.usageTimesMillis)) { if (filteredThreadsCpuUsage == null) { filteredThreadsCpuUsage = new int[mFrequenciesKhz.length]; } filteredThreadsCpuUsage = sumCpuUsage(filteredThreadsCpuUsage, threadCpuUsage.usageTimesMillis); continue; } threadCpuUsages.add(threadCpuUsage); } } catch (IOException e) { // Expected when a process finishes Loading @@ -282,6 +303,12 @@ public class KernelCpuThreadReader { return null; } // 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)); } if (DEBUG) { Slog.d(TAG, "Read CPU usage of " + threadCpuUsages.size() + " threads"); } Loading Loading @@ -368,15 +395,6 @@ public class KernelCpuThreadReader { } int[] cpuUsages = mFrequencyBucketCreator.getBucketedValues(cpuUsagesLong); // Check if the total CPU usage below the threshold int totalCpuUsage = 0; for (int i = 0; i < cpuUsages.length; i++) { totalCpuUsage += cpuUsages[i]; } if (totalCpuUsage < mMinimumTotalCpuUsageMillis) { return null; } return new ThreadCpuUsage(threadId, threadName, cpuUsages); } Loading Loading @@ -423,6 +441,28 @@ public class KernelCpuThreadReader { } } /** * Get the sum of all CPU usage across all frequencies */ private static int totalCpuUsage(int[] cpuUsage) { int total = 0; for (int i = 0; i < cpuUsage.length; i++) { total += cpuUsage[i]; } return total; } /** * 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++) { summed[i] = a[i] + b[i]; } return summed; } /** * Puts frequencies and usage times into buckets */ Loading
core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java +46 −0 Original line number Diff line number Diff line Loading @@ -218,6 +218,51 @@ public class KernelCpuThreadReaderTest { } @Test public void testReader_otherThreads() throws IOException { final Path processPath = mProcDirectory.toPath().resolve("self"); setupDirectory( processPath, new int[]{1, 2, 3}, "process", 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"), new KernelCpuThreadReader.Injector() { @Override public int myPid() { return 1000; } @Override public int myUid() { return 0; } @Override public int getUidForPid(int pid) { return 0; } }); checkResults( kernelCpuThreadReader.getCurrentProcessCpuUsage(), kernelCpuThreadReader.getCpuFrequenciesKhz(), 0, 1000, new int[]{-1, 3}, "process", new String[]{"__OTHER_THREADS", "thread3"}, new int[]{1000, 2000}, new int[][]{{100, 1000}, {0, 3000}} ); } private void setupDirectory(Path processPath, int[] threadIds, String processName, String[] threadNames, int[] cpuFrequencies, int[][] cpuTimes) throws IOException { // Make /proc/$PID Loading Loading @@ -259,6 +304,7 @@ public class KernelCpuThreadReaderTest { assertEquals(processId, processCpuUsage.processId); assertEquals(uid, processCpuUsage.uid); assertEquals(processName, processCpuUsage.processName); assertEquals(threadIds.length, processCpuUsage.threadCpuUsages.size()); // Sort the thread CPU usages to compare with test case final ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages = Loading