Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e51e5204 authored by Misha Wagner's avatar Misha Wagner
Browse files

Add thread atom that contains sum of filtered thread CPU usage

This will help us to calculate the total CPU usage of a process, even
with aggressive filtering of threads with low CPU usage..

Test: atest KernelCpuThreadReaderTest#testReader_otherThreads
Bug: 126511586
Change-Id: I9100a9ecda97f53b819ab57da710e10cccef4311
parent dbe517fb
Loading
Loading
Loading
Loading
+56 −16
Original line number Diff line number Diff line
@@ -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
@@ -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
     */
@@ -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
@@ -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");
        }
@@ -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);
    }

@@ -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
     */
+46 −0
Original line number Diff line number Diff line
@@ -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
@@ -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 =