summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
authorDmitri Plotnikov <dplotnikov@google.com>2020-10-06 19:51:58 -0700
committerDmitri Plotnikov <dplotnikov@google.com>2020-10-06 19:55:42 -0700
commit492ed242a302db1e10a2208cb49adbb08748fc92 (patch)
tree7417bc34cec4a477057ee99fe13a5cd66d70ee47 /core/java
parente86bb69506f38d2fd9ea6fb19c59f78b61114e13 (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')
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java31
-rw-r--r--core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java96
-rw-r--r--core/java/com/android/internal/os/SystemServerCpuThreadReader.java66
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;
}
}