diff options
| author | Dmitri Plotnikov <dplotnikov@google.com> | 2020-10-06 19:51:58 -0700 |
|---|---|---|
| committer | Dmitri Plotnikov <dplotnikov@google.com> | 2020-10-06 19:55:42 -0700 |
| commit | 492ed242a302db1e10a2208cb49adbb08748fc92 (patch) | |
| tree | 7417bc34cec4a477057ee99fe13a5cd66d70ee47 /core/java | |
| parent | e86bb69506f38d2fd9ea6fb19c59f78b61114e13 (diff) | |
Make KernelSingleProcessCpuThreadReader aggregate CPU times
This is done to
- prepare to switch to a native implementation
- reduce memory allocations
Test: atest FrameworksCoreTests:com.android.internal.os.KernelSingleProcessCpuThreadReaderTest
Test: atest FrameworksCoreTests:com.android.internal.os.SystemServerCpuThreadReaderTest
Test: atest FrameworksCoreTests:com.android.internal.os.BatteryStatsBinderCallStatsTest
Test: atest FrameworksCoreTests:com.android.internal.os.SystemServicePowerCalculatorTest
Bug: 169279846
Change-Id: I4ff41ad7110120836518d86a8c74cd18631e96c7
Diffstat (limited to 'core/java')
3 files changed, 80 insertions, 113 deletions
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 308af99d465a..17323ba2f16b 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -1019,8 +1019,6 @@ public class BatteryStatsImpl extends BatteryStats { */ private LongSamplingCounterArray mSystemServerCpuTimesUs; - private long[] mTmpSystemServerCpuTimesUs; - /** * Times spent by the system server threads grouped by cluster and CPU speed. */ @@ -12432,41 +12430,15 @@ public class BatteryStatsImpl extends BatteryStats { return; } - int numCpuClusters = mPowerProfile.getNumCpuClusters(); if (mSystemServerCpuTimesUs == null) { mSystemServerCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase); mSystemServerThreadCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase); mBinderThreadCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase); } + mSystemServerCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.processCpuTimesUs); mSystemServerThreadCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.threadCpuTimesUs); mBinderThreadCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.binderThreadCpuTimesUs); - long totalCpuTimeAllThreads = 0; - for (int index = systemServiceCpuThreadTimes.threadCpuTimesUs.length - 1; index >= 0; - index--) { - totalCpuTimeAllThreads += systemServiceCpuThreadTimes.threadCpuTimesUs[index]; - } - - // Estimate per cluster per frequency CPU time for the entire process - // by distributing the total process CPU time proportionately to how much - // CPU time its threads took on those clusters/frequencies. This algorithm - // works more accurately when when we have equally distributed concurrency. - // TODO(b/169279846): obtain actual process CPU times from the kernel - long processCpuTime = systemServiceCpuThreadTimes.processCpuTimeUs; - if (mTmpSystemServerCpuTimesUs == null) { - mTmpSystemServerCpuTimesUs = - new long[systemServiceCpuThreadTimes.threadCpuTimesUs.length]; - } - for (int index = systemServiceCpuThreadTimes.threadCpuTimesUs.length - 1; index >= 0; - index--) { - mTmpSystemServerCpuTimesUs[index] = - processCpuTime * systemServiceCpuThreadTimes.threadCpuTimesUs[index] - / totalCpuTimeAllThreads; - - } - - mSystemServerCpuTimesUs.addCountLocked(mTmpSystemServerCpuTimesUs); - if (DEBUG_BINDER_STATS) { Slog.d(TAG, "System server threads per CPU cluster (binder threads/total threads/%)"); long totalCpuTimeMs = 0; @@ -12480,6 +12452,7 @@ public class BatteryStatsImpl extends BatteryStats { final long[] binderThreadCpuTimesUs = mBinderThreadCpuTimesUs.getCountsLocked(0); int index = 0; + int numCpuClusters = mPowerProfile.getNumCpuClusters(); for (int cluster = 0; cluster < numCpuClusters; cluster++) { StringBuilder sb = new StringBuilder(); sb.append("cpu").append(cpuIndex).append(": ["); diff --git a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java index 0578b8976037..4f687f186d88 100644 --- a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java +++ b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java @@ -33,8 +33,7 @@ import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; +import java.util.Arrays; /** * Iterates over all threads owned by a given process, and return the CPU usage for @@ -151,7 +150,7 @@ public class KernelSingleProcessCpuThreadReader { /** * Get the CPU frequencies that correspond to the times reported in {@link - * ThreadCpuUsage#usageTimesMillis} + * ProcessCpuUsage#processCpuTimesMillis} etc. */ public int getCpuFrequencyCount() { if (mFrequencyCount == 0) { @@ -163,14 +162,22 @@ public class KernelSingleProcessCpuThreadReader { /** * Get the total and per-thread CPU usage of the process with the PID specified in the * constructor. + * @param selectedThreadIds a SORTED array of native Thread IDs whose CPU times should + * be aggregated as a group. This is expected to be a subset + * of all thread IDs owned by the process. */ @Nullable - public ProcessCpuUsage getProcessCpuUsage() { + public ProcessCpuUsage getProcessCpuUsage(int[] selectedThreadIds) { if (DEBUG) { Slog.d(TAG, "Reading CPU thread usages with directory " + mProcPath + " process ID " + mPid); } + if (!isSorted(selectedThreadIds)) { + throw new IllegalArgumentException("selectedThreadIds is not sorted: " + + Arrays.toString(selectedThreadIds)); + } + if (!Process.readProcFile(mProcessStatFilePath, PROCESS_FULL_STATS_FORMAT, null, mProcessFullStatsData, null)) { Slog.e(TAG, "Failed to read process stat file " + mProcessStatFilePath); @@ -182,39 +189,43 @@ public class KernelSingleProcessCpuThreadReader { long processCpuTimeMillis = (utime + stime) * mJiffyMillis; - final ArrayList<ThreadCpuUsage> threadCpuUsages = new ArrayList<>(); + int cpuFrequencyCount = getCpuFrequencyCount(); + ProcessCpuUsage processCpuUsage = new ProcessCpuUsage(cpuFrequencyCount); try (DirectoryStream<Path> threadPaths = Files.newDirectoryStream(mThreadsDirectoryPath)) { for (Path threadDirectory : threadPaths) { - ThreadCpuUsage threadCpuUsage = getThreadCpuUsage(threadDirectory); - if (threadCpuUsage == null) { - continue; - } - threadCpuUsages.add(threadCpuUsage); + readThreadCpuUsage(processCpuUsage, selectedThreadIds, threadDirectory); } } catch (IOException | DirectoryIteratorException e) { // Expected when a process finishes return null; } - // If we found no threads, then the process has exited while we were reading from it - if (threadCpuUsages.isEmpty()) { - return null; + // Estimate per cluster per frequency CPU time for the entire process + // by distributing the total process CPU time proportionately to how much + // CPU time its threads took on those clusters/frequencies. This algorithm + // works more accurately when when we have equally distributed concurrency. + // TODO(b/169279846): obtain actual process CPU times from the kernel + long totalCpuTimeAllThreads = 0; + for (int i = cpuFrequencyCount - 1; i >= 0; i--) { + totalCpuTimeAllThreads += processCpuUsage.threadCpuTimesMillis[i]; } - if (DEBUG) { - Slog.d(TAG, "Read CPU usage of " + threadCpuUsages.size() + " threads"); + + for (int i = cpuFrequencyCount - 1; i >= 0; i--) { + processCpuUsage.processCpuTimesMillis[i] = + processCpuTimeMillis * processCpuUsage.threadCpuTimesMillis[i] + / totalCpuTimeAllThreads; } - return new ProcessCpuUsage(processCpuTimeMillis, threadCpuUsages); + + return processCpuUsage; } /** - * Get a thread's CPU usage + * Reads a thread's CPU usage and aggregates the per-cluster per-frequency CPU times. * * @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 */ - @Nullable - private ThreadCpuUsage getThreadCpuUsage(Path threadDirectory) { + private void readThreadCpuUsage(ProcessCpuUsage processCpuUsage, int[] selectedThreadIds, + Path threadDirectory) { // Get the thread ID from the directory name final int threadId; try { @@ -222,38 +233,45 @@ public class KernelSingleProcessCpuThreadReader { threadId = Integer.parseInt(directoryName); } catch (NumberFormatException e) { Slog.w(TAG, "Failed to parse thread ID when iterating over /proc/*/task", e); - return null; + return; } // Get the CPU statistics from the directory final Path threadCpuStatPath = threadDirectory.resolve(CPU_STATISTICS_FILENAME); final long[] cpuUsages = mProcTimeInStateReader.getUsageTimesMillis(threadCpuStatPath); if (cpuUsages == null) { - return null; + return; } - return new ThreadCpuUsage(threadId, cpuUsages); + final int cpuFrequencyCount = getCpuFrequencyCount(); + final boolean isSelectedThread = Arrays.binarySearch(selectedThreadIds, threadId) >= 0; + for (int i = cpuFrequencyCount - 1; i >= 0; i--) { + processCpuUsage.threadCpuTimesMillis[i] += cpuUsages[i]; + if (isSelectedThread) { + processCpuUsage.selectedThreadCpuTimesMillis[i] += cpuUsages[i]; + } + } } - /** CPU usage of a process and all of its threads */ + /** CPU usage of a process, all of its threads and a selected subset of its threads */ public static class ProcessCpuUsage { - public final long cpuTimeMillis; - public final List<ThreadCpuUsage> threadCpuUsages; - - ProcessCpuUsage(long cpuTimeMillis, List<ThreadCpuUsage> threadCpuUsages) { - this.cpuTimeMillis = cpuTimeMillis; - this.threadCpuUsages = threadCpuUsages; + public long[] processCpuTimesMillis; + public long[] threadCpuTimesMillis; + public long[] selectedThreadCpuTimesMillis; + + public ProcessCpuUsage(int cpuFrequencyCount) { + processCpuTimesMillis = new long[cpuFrequencyCount]; + threadCpuTimesMillis = new long[cpuFrequencyCount]; + selectedThreadCpuTimesMillis = new long[cpuFrequencyCount]; } } - /** CPU usage of a thread */ - public static class ThreadCpuUsage { - public final int threadId; - public final long[] usageTimesMillis; - - ThreadCpuUsage(int threadId, long[] usageTimesMillis) { - this.threadId = threadId; - this.usageTimesMillis = usageTimesMillis; + private static boolean isSorted(int[] array) { + for (int i = 0; i < array.length - 1; i++) { + if (array[i] > array[i + 1]) { + return false; + } } + return true; } } diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java index d9f0dc0ae795..fbbee94feacc 100644 --- a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java +++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java @@ -24,7 +24,6 @@ import com.android.internal.annotations.VisibleForTesting; import java.io.IOException; import java.nio.file.Path; import java.util.Arrays; -import java.util.List; /** * Reads /proc/UID/task/TID/time_in_state files to obtain statistics on CPU usage @@ -34,10 +33,7 @@ public class SystemServerCpuThreadReader { private final KernelSingleProcessCpuThreadReader mKernelCpuThreadReader; private int[] mBinderThreadNativeTids = new int[0]; // Sorted - private long mProcessCpuTimeUs; - private long[] mThreadCpuTimesUs; - private long[] mBinderThreadCpuTimesUs; - private long mLastProcessCpuTimeUs; + private long[] mLastProcessCpuTimeUs; private long[] mLastThreadCpuTimesUs; private long[] mLastBinderThreadCpuTimesUs; @@ -46,14 +42,15 @@ public class SystemServerCpuThreadReader { */ public static class SystemServiceCpuThreadTimes { // The entire process - public long processCpuTimeUs; + public long[] processCpuTimesUs; // All threads public long[] threadCpuTimesUs; // Just the threads handling incoming binder calls public long[] binderThreadCpuTimesUs; } - private SystemServiceCpuThreadTimes mDeltaCpuThreadTimes = new SystemServiceCpuThreadTimes(); + private final SystemServiceCpuThreadTimes mDeltaCpuThreadTimes = + new SystemServiceCpuThreadTimes(); /** * Creates a configured instance of SystemServerCpuThreadReader. @@ -83,59 +80,38 @@ public class SystemServerCpuThreadReader { */ @Nullable public SystemServiceCpuThreadTimes readDelta() { - int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequencyCount(); - if (mBinderThreadCpuTimesUs == null) { - mThreadCpuTimesUs = new long[numCpuFrequencies]; - mBinderThreadCpuTimesUs = new long[numCpuFrequencies]; - + final int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequencyCount(); + if (mLastProcessCpuTimeUs == null) { + mLastProcessCpuTimeUs = new long[numCpuFrequencies]; mLastThreadCpuTimesUs = new long[numCpuFrequencies]; mLastBinderThreadCpuTimesUs = new long[numCpuFrequencies]; + mDeltaCpuThreadTimes.processCpuTimesUs = new long[numCpuFrequencies]; mDeltaCpuThreadTimes.threadCpuTimesUs = new long[numCpuFrequencies]; mDeltaCpuThreadTimes.binderThreadCpuTimesUs = new long[numCpuFrequencies]; } - mProcessCpuTimeUs = 0; - Arrays.fill(mThreadCpuTimesUs, 0); - Arrays.fill(mBinderThreadCpuTimesUs, 0); - - KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage = - mKernelCpuThreadReader.getProcessCpuUsage(); + final KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage = + mKernelCpuThreadReader.getProcessCpuUsage(mBinderThreadNativeTids); if (processCpuUsage == null) { return null; } - mProcessCpuTimeUs = processCpuUsage.cpuTimeMillis * 1000; - - List<KernelSingleProcessCpuThreadReader.ThreadCpuUsage> threadCpuUsages = - processCpuUsage.threadCpuUsages; - int threadCpuUsagesSize = threadCpuUsages.size(); - for (int i = 0; i < threadCpuUsagesSize; i++) { - KernelSingleProcessCpuThreadReader.ThreadCpuUsage tcu = threadCpuUsages.get(i); - boolean isBinderThread = - Arrays.binarySearch(mBinderThreadNativeTids, tcu.threadId) >= 0; - for (int k = 0; k < numCpuFrequencies; k++) { - long usageTimeUs = tcu.usageTimesMillis[k] * 1000; - mThreadCpuTimesUs[k] += usageTimeUs; - if (isBinderThread) { - mBinderThreadCpuTimesUs[k] += usageTimeUs; - } - } - } - - for (int i = 0; i < mThreadCpuTimesUs.length; i++) { - mDeltaCpuThreadTimes.processCpuTimeUs = - Math.max(0, mProcessCpuTimeUs - mLastProcessCpuTimeUs); + for (int i = numCpuFrequencies - 1; i >= 0; i--) { + long processCpuTimesUs = processCpuUsage.processCpuTimesMillis[i] * 1000; + long threadCpuTimesUs = processCpuUsage.threadCpuTimesMillis[i] * 1000; + long binderThreadCpuTimesUs = processCpuUsage.selectedThreadCpuTimesMillis[i] * 1000; + mDeltaCpuThreadTimes.processCpuTimesUs[i] = + Math.max(0, processCpuTimesUs - mLastProcessCpuTimeUs[i]); mDeltaCpuThreadTimes.threadCpuTimesUs[i] = - Math.max(0, mThreadCpuTimesUs[i] - mLastThreadCpuTimesUs[i]); + Math.max(0, threadCpuTimesUs - mLastThreadCpuTimesUs[i]); mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] = - Math.max(0, mBinderThreadCpuTimesUs[i] - mLastBinderThreadCpuTimesUs[i]); - mLastThreadCpuTimesUs[i] = mThreadCpuTimesUs[i]; - mLastBinderThreadCpuTimesUs[i] = mBinderThreadCpuTimesUs[i]; + Math.max(0, binderThreadCpuTimesUs - mLastBinderThreadCpuTimesUs[i]); + mLastProcessCpuTimeUs[i] = processCpuTimesUs; + mLastThreadCpuTimesUs[i] = threadCpuTimesUs; + mLastBinderThreadCpuTimesUs[i] = binderThreadCpuTimesUs; } - mLastProcessCpuTimeUs = mProcessCpuTimeUs; - return mDeltaCpuThreadTimes; } } |
