diff options
| author | TreeHugger Robot <treehugger-gerrit@google.com> | 2020-12-18 21:43:43 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2020-12-18 21:43:43 +0000 |
| commit | 36ca6d3452868f9076c2a76c6a3d00a31bf17440 (patch) | |
| tree | 8ee8ce81d813fe141721c5527d6be846e128be0a /core/java | |
| parent | ada970f4739cffccd25f8d27533c35510ee1e9c7 (diff) | |
| parent | c9e9058347e4f99f7e580869055aae039e05d8ee (diff) | |
Merge "Revert "Use eBPF-based time-in-state monitoring for groups of threads""
Diffstat (limited to 'core/java')
3 files changed, 303 insertions, 136 deletions
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 5aedd1ea90e5..bdd96c24ade4 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -1106,6 +1106,16 @@ public class BatteryStatsImpl extends BatteryStats { private long[] mCpuFreqs; /** + * Times spent by the system server process grouped by cluster and CPU speed. + */ + private LongSamplingCounterArray mSystemServerCpuTimesUs; + + /** + * Times spent by the system server threads grouped by cluster and CPU speed. + */ + private LongSamplingCounterArray mSystemServerThreadCpuTimesUs; + + /** * Times spent by the system server threads handling incoming binder requests. */ private LongSamplingCounterArray mBinderThreadCpuTimesUs; @@ -10844,14 +10854,6 @@ public class BatteryStatsImpl extends BatteryStats { } } - /** - * Starts tracking CPU time-in-state for threads of the system server process, - * keeping a separate account of threads receiving incoming binder calls. - */ - public void startTrackingSystemServerCpuTime() { - mSystemServerCpuThreadReader.startTrackingThreadCpuTime(); - } - public void setCallback(BatteryCallback cb) { mCallback = cb; } @@ -11504,6 +11506,8 @@ public class BatteryStatsImpl extends BatteryStats { MeasuredEnergyStats.resetIfNotNull(mGlobalMeasuredEnergyStats); + resetIfNotNull(mSystemServerCpuTimesUs, false, elapsedRealtimeUs); + resetIfNotNull(mSystemServerThreadCpuTimesUs, false, elapsedRealtimeUs); resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs); mLastHistoryStepDetails = null; @@ -12703,17 +12707,27 @@ public class BatteryStatsImpl extends BatteryStats { return; } - if (mBinderThreadCpuTimesUs == null) { + 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); if (DEBUG_BINDER_STATS) { - Slog.d(TAG, "System server threads per CPU cluster (incoming binder threads)"); + Slog.d(TAG, "System server threads per CPU cluster (binder threads/total threads/%)"); + long totalCpuTimeMs = 0; + long totalThreadTimeMs = 0; long binderThreadTimeMs = 0; int cpuIndex = 0; - final long[] binderThreadCpuTimesUs = mBinderThreadCpuTimesUs.getCountsLocked( - BatteryStats.STATS_SINCE_CHARGED); + final long[] systemServerCpuTimesUs = + mSystemServerCpuTimesUs.getCountsLocked(0); + final long[] systemServerThreadCpuTimesUs = + mSystemServerThreadCpuTimesUs.getCountsLocked(0); + final long[] binderThreadCpuTimesUs = + mBinderThreadCpuTimesUs.getCountsLocked(0); int index = 0; int numCpuClusters = mPowerProfile.getNumCpuClusters(); for (int cluster = 0; cluster < numCpuClusters; cluster++) { @@ -12724,15 +12738,28 @@ public class BatteryStatsImpl extends BatteryStats { if (speed != 0) { sb.append(", "); } + long totalCountMs = systemServerThreadCpuTimesUs[index] / 1000; long binderCountMs = binderThreadCpuTimesUs[index] / 1000; - sb.append(TextUtils.formatSimple("%10d", binderCountMs)); + sb.append(String.format("%d/%d(%.1f%%)", + binderCountMs, + totalCountMs, + totalCountMs != 0 ? (double) binderCountMs * 100 / totalCountMs : 0)); + totalCpuTimeMs += systemServerCpuTimesUs[index] / 1000; + totalThreadTimeMs += totalCountMs; binderThreadTimeMs += binderCountMs; index++; } cpuIndex += mPowerProfile.getNumCoresInCpuCluster(cluster); Slog.d(TAG, sb.toString()); } + + Slog.d(TAG, "Total system server CPU time (ms): " + totalCpuTimeMs); + Slog.d(TAG, "Total system server thread time (ms): " + totalThreadTimeMs); + Slog.d(TAG, String.format("Total Binder thread time (ms): %d (%.1f%%)", + binderThreadTimeMs, + binderThreadTimeMs != 0 + ? (double) binderThreadTimeMs * 100 / totalThreadTimeMs : 0)); } } @@ -14007,16 +14034,60 @@ public class BatteryStatsImpl extends BatteryStats { } - /** - * Estimates the time spent by the system server handling incoming binder requests. - */ @Override public long[] getSystemServiceTimeAtCpuSpeeds() { - if (mBinderThreadCpuTimesUs == null) { + // Estimates the time spent by the system server handling incoming binder requests. + // + // The data that we can get from the kernel is this: + // - CPU duration for a (thread - cluster - CPU speed) combination + // - CPU duration for a (UID - cluster - CPU speed) combination + // + // The configuration we have in the Power Profile is this: + // - Average CPU power for a (cluster - CPU speed) combination. + // + // The model used by BatteryStats can be illustrated with this example: + // + // - Let's say the system server has 10 threads. + // - These 10 threads spent 1000 ms of CPU time in aggregate + // - Of the 10 threads 4 were execute exclusively incoming binder calls. + // - These 4 "binder" threads consumed 600 ms of CPU time in aggregate + // - The real time spent by the system server process doing all of this is, say, 200 ms. + // + // We will assume that power consumption is proportional to the time spent by the CPU + // across all threads. This is a crude assumption, but we don't have more detailed data. + // Thus, + // binderRealTime = realTime * aggregateBinderThreadTime / aggregateAllThreadTime + // + // In our example, + // binderRealTime = 200 * 600 / 1000 = 120ms + // + // We can then multiply this estimated time by the average power to obtain an estimate + // of the total power consumed by incoming binder calls for the given cluster/speed + // combination. + + if (mSystemServerCpuTimesUs == null) { return null; } - return mBinderThreadCpuTimesUs.getCountsLocked(BatteryStats.STATS_SINCE_CHARGED); + final long[] systemServerCpuTimesUs = mSystemServerCpuTimesUs.getCountsLocked( + BatteryStats.STATS_SINCE_CHARGED); + final long [] systemServerThreadCpuTimesUs = mSystemServerThreadCpuTimesUs.getCountsLocked( + BatteryStats.STATS_SINCE_CHARGED); + final long[] binderThreadCpuTimesUs = mBinderThreadCpuTimesUs.getCountsLocked( + BatteryStats.STATS_SINCE_CHARGED); + + final int size = systemServerCpuTimesUs.length; + final long[] results = new long[size]; + + for (int i = 0; i < size; i++) { + if (systemServerThreadCpuTimesUs[i] == 0) { + continue; + } + + results[i] = systemServerCpuTimesUs[i] * binderThreadCpuTimesUs[i] + / systemServerThreadCpuTimesUs[i]; + } + return results; } /** @@ -14412,7 +14483,7 @@ public class BatteryStatsImpl extends BatteryStats { } updateSystemServiceCallStats(); - if (mBinderThreadCpuTimesUs != null) { + if (mSystemServerThreadCpuTimesUs != null) { pw.println("Per UID System server binder time in ms:"); long[] systemServiceTimeAtCpuSpeeds = getSystemServiceTimeAtCpuSpeeds(); for (int i = 0; i < size; i++) { @@ -15999,6 +16070,9 @@ public class BatteryStatsImpl extends BatteryStats { mUidStats.append(uid, u); } + mSystemServerCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, mOnBatteryTimeBase); + mSystemServerThreadCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, + mOnBatteryTimeBase); mBinderThreadCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, mOnBatteryTimeBase); } @@ -16207,6 +16281,8 @@ public class BatteryStatsImpl extends BatteryStats { } else { out.writeInt(0); } + LongSamplingCounterArray.writeToParcel(out, mSystemServerCpuTimesUs); + LongSamplingCounterArray.writeToParcel(out, mSystemServerThreadCpuTimesUs); LongSamplingCounterArray.writeToParcel(out, mBinderThreadCpuTimesUs); } diff --git a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java index 4d2a08a4bcf3..e6a962312a00 100644 --- a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java +++ b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java @@ -16,12 +16,23 @@ package com.android.internal.os; +import static android.os.Process.PROC_OUT_LONG; +import static android.os.Process.PROC_SPACE_TERM; + import android.annotation.Nullable; +import android.os.Process; +import android.system.Os; +import android.system.OsConstants; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import java.io.IOException; +import java.nio.file.DirectoryIteratorException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; /** @@ -34,65 +45,93 @@ public class KernelSingleProcessCpuThreadReader { private static final String TAG = "KernelSingleProcCpuThreadRdr"; private static final boolean DEBUG = false; + private static final boolean NATIVE_ENABLED = true; + + /** + * The name of the file to read CPU statistics from, must be found in {@code + * /proc/$PID/task/$TID} + */ + private static final String CPU_STATISTICS_FILENAME = "time_in_state"; + + private static final String PROC_STAT_FILENAME = "stat"; + + /** Directory under /proc/$PID containing CPU stats files for threads */ + public static final String THREAD_CPU_STATS_DIRECTORY = "task"; + + /** 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} */ + private static final Path INITIAL_TIME_IN_STATE_PATH = Paths.get("self/time_in_state"); + + /** See https://man7.org/linux/man-pages/man5/proc.5.html */ + private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] { + PROC_SPACE_TERM, + PROC_SPACE_TERM, + PROC_SPACE_TERM, + PROC_SPACE_TERM, + PROC_SPACE_TERM, + PROC_SPACE_TERM, + PROC_SPACE_TERM, + PROC_SPACE_TERM, + PROC_SPACE_TERM, + PROC_SPACE_TERM, + PROC_SPACE_TERM, + PROC_SPACE_TERM, + PROC_SPACE_TERM, + PROC_SPACE_TERM | PROC_OUT_LONG, // 14: utime + PROC_SPACE_TERM | PROC_OUT_LONG, // 15: stime + // Ignore remaining fields + }; + + private final long[] mProcessFullStatsData = new long[2]; + + private static final int PROCESS_FULL_STAT_UTIME = 0; + private static final int PROCESS_FULL_STAT_STIME = 1; + + /** Used to read and parse {@code time_in_state} files */ + private final ProcTimeInStateReader mProcTimeInStateReader; private final int mPid; - private final CpuTimeInStateReader mCpuTimeInStateReader; + /** Where the proc filesystem is mounted */ + private final Path mProcPath; - private int[] mSelectedThreadNativeTids = new int[0]; // Sorted + // How long a CPU jiffy is in milliseconds. + private final long mJiffyMillis; - /** - * Count of frequencies read from the {@code time_in_state} file. - */ - private int mFrequencyCount; + // Path: /proc/<pid>/stat + private final String mProcessStatFilePath; - private boolean mIsTracking; + // Path: /proc/<pid>/task + private final Path mThreadsDirectoryPath; /** - * A CPU time-in-state provider for testing. Imitates the behavior of the corresponding - * methods in frameworks/native/libs/cputimeinstate/cputimeinstate.c + * Count of frequencies read from the {@code time_in_state} file. Read from {@link + * #mProcTimeInStateReader#getCpuFrequenciesKhz()}. */ - @VisibleForTesting - public interface CpuTimeInStateReader { - /** - * Returns the overall number of cluster-frequency combinations. - */ - int getCpuFrequencyCount(); - - /** - * Returns true to indicate success. - * - * Called from native. - */ - boolean startTrackingProcessCpuTimes(int tgid); - - /** - * Returns true to indicate success. - * - * Called from native. - */ - boolean startAggregatingTaskCpuTimes(int pid, int aggregationKey); - - /** - * Must return an array of strings formatted like this: - * "aggKey:t0_0 t0_1...:t1_0 t1_1..." - * Times should be provided in nanoseconds. - * - * Called from native. - */ - String[] getAggregatedTaskCpuFreqTimes(int pid); - } + private int mFrequencyCount; /** * Create with a path where `proc` is mounted. Used primarily for testing * * @param pid PID of the process whose threads are to be read. + * @param procPath where `proc` is mounted (to find, see {@code mount | grep ^proc}) */ @VisibleForTesting - public KernelSingleProcessCpuThreadReader(int pid, - @Nullable CpuTimeInStateReader cpuTimeInStateReader) throws IOException { + public KernelSingleProcessCpuThreadReader( + int pid, + Path procPath) throws IOException { mPid = pid; - mCpuTimeInStateReader = cpuTimeInStateReader; + mProcPath = procPath; + mProcTimeInStateReader = new ProcTimeInStateReader( + mProcPath.resolve(INITIAL_TIME_IN_STATE_PATH)); + long jiffyHz = Os.sysconf(OsConstants._SC_CLK_TCK); + mJiffyMillis = 1000 / jiffyHz; + mProcessStatFilePath = + mProcPath.resolve(String.valueOf(mPid)).resolve(PROC_STAT_FILENAME).toString(); + mThreadsDirectoryPath = + mProcPath.resolve(String.valueOf(mPid)).resolve(THREAD_CPU_STATS_DIRECTORY); } /** @@ -103,7 +142,7 @@ public class KernelSingleProcessCpuThreadReader { @Nullable public static KernelSingleProcessCpuThreadReader create(int pid) { try { - return new KernelSingleProcessCpuThreadReader(pid, null); + return new KernelSingleProcessCpuThreadReader(pid, DEFAULT_PROC_PATH); } catch (IOException e) { Slog.e(TAG, "Failed to initialize KernelSingleProcessCpuThreadReader", e); return null; @@ -111,98 +150,146 @@ public class KernelSingleProcessCpuThreadReader { } /** - * Starts tracking aggregated CPU time-in-state of all threads of the process with the PID - * supplied in the constructor. - */ - public void startTrackingThreadCpuTimes() { - if (!mIsTracking) { - if (!startTrackingProcessCpuTimes(mPid, mCpuTimeInStateReader)) { - Slog.e(TAG, "Failed to start tracking process CPU times for " + mPid); - } - if (mSelectedThreadNativeTids.length > 0) { - if (!startAggregatingThreadCpuTimes(mSelectedThreadNativeTids, - mCpuTimeInStateReader)) { - Slog.e(TAG, "Failed to start tracking aggregated thread CPU times for " - + Arrays.toString(mSelectedThreadNativeTids)); - } - } - mIsTracking = true; - } - } - - /** - * @param nativeTids an 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. - */ - public void setSelectedThreadIds(int[] nativeTids) { - mSelectedThreadNativeTids = nativeTids.clone(); - if (mIsTracking) { - startAggregatingThreadCpuTimes(mSelectedThreadNativeTids, mCpuTimeInStateReader); - } - } - - /** - * Get the CPU frequencies that correspond to the times reported in {@link ProcessCpuUsage}. + * Get the CPU frequencies that correspond to the times reported in {@link + * ProcessCpuUsage#processCpuTimesMillis} etc. */ public int getCpuFrequencyCount() { if (mFrequencyCount == 0) { - mFrequencyCount = getCpuFrequencyCount(mCpuTimeInStateReader); + mFrequencyCount = mProcTimeInStateReader.getFrequenciesKhz().length; } return mFrequencyCount; } /** - * Get the total CPU usage of the process with the PID specified in the - * constructor. The CPU usage time is aggregated across all threads and may - * exceed the time the entire process has been running. + * 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 for PID " + mPid); + Slog.d(TAG, "Reading CPU thread usages with directory " + mProcPath + " process ID " + + mPid); + } + + int cpuFrequencyCount = getCpuFrequencyCount(); + ProcessCpuUsage processCpuUsage = new ProcessCpuUsage(cpuFrequencyCount); + + if (NATIVE_ENABLED) { + boolean result = readProcessCpuUsage(mProcPath.toString(), mPid, + selectedThreadIds, processCpuUsage.processCpuTimesMillis, + processCpuUsage.threadCpuTimesMillis, + processCpuUsage.selectedThreadCpuTimesMillis); + if (!result) { + return null; + } + return processCpuUsage; } - ProcessCpuUsage processCpuUsage = new ProcessCpuUsage(getCpuFrequencyCount()); + if (!isSorted(selectedThreadIds)) { + throw new IllegalArgumentException("selectedThreadIds is not sorted: " + + Arrays.toString(selectedThreadIds)); + } - boolean result = readProcessCpuUsage(mPid, - processCpuUsage.threadCpuTimesMillis, - processCpuUsage.selectedThreadCpuTimesMillis, - mCpuTimeInStateReader); - if (!result) { + if (!Process.readProcFile(mProcessStatFilePath, PROCESS_FULL_STATS_FORMAT, null, + mProcessFullStatsData, null)) { + Slog.e(TAG, "Failed to read process stat file " + mProcessStatFilePath); return null; } - if (DEBUG) { - Slog.d(TAG, "threadCpuTimesMillis = " - + Arrays.toString(processCpuUsage.threadCpuTimesMillis)); - Slog.d(TAG, "selectedThreadCpuTimesMillis = " - + Arrays.toString(processCpuUsage.selectedThreadCpuTimesMillis)); + long utime = mProcessFullStatsData[PROCESS_FULL_STAT_UTIME]; + long stime = mProcessFullStatsData[PROCESS_FULL_STAT_STIME]; + + long processCpuTimeMillis = (utime + stime) * mJiffyMillis; + + try (DirectoryStream<Path> threadPaths = Files.newDirectoryStream(mThreadsDirectoryPath)) { + for (Path threadDirectory : threadPaths) { + readThreadCpuUsage(processCpuUsage, selectedThreadIds, threadDirectory); + } + } catch (IOException | DirectoryIteratorException e) { + // Expected when a process finishes + 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]; + } + + for (int i = cpuFrequencyCount - 1; i >= 0; i--) { + processCpuUsage.processCpuTimesMillis[i] = + processCpuTimeMillis * processCpuUsage.threadCpuTimesMillis[i] + / totalCpuTimeAllThreads; } return processCpuUsage; } + /** + * Reads a thread's CPU usage and aggregates the per-cluster per-frequency CPU times. + * + * @param threadDirectory the {@code /proc} directory of the thread + */ + private void readThreadCpuUsage(ProcessCpuUsage processCpuUsage, int[] selectedThreadIds, + Path threadDirectory) { + // Get the thread ID from the directory name + final int threadId; + try { + final String directoryName = threadDirectory.getFileName().toString(); + threadId = Integer.parseInt(directoryName); + } catch (NumberFormatException e) { + Slog.w(TAG, "Failed to parse thread ID when iterating over /proc/*/task", e); + 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; + } + + 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, all of its threads and a selected subset of its threads */ public static class ProcessCpuUsage { + 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]; } } - private native int getCpuFrequencyCount(CpuTimeInStateReader reader); - - private native boolean startTrackingProcessCpuTimes(int pid, CpuTimeInStateReader reader); - - private native boolean startAggregatingThreadCpuTimes(int[] selectedThreadIds, - CpuTimeInStateReader reader); + 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; + } - private native boolean readProcessCpuUsage(int pid, - long[] threadCpuTimesMillis, - long[] selectedThreadCpuTimesMillis, - CpuTimeInStateReader reader); + private native boolean readProcessCpuUsage(String procPath, int pid, int[] selectedThreadIds, + long[] processCpuTimesMillis, long[] threadCpuTimesMillis, + long[] selectedThreadCpuTimesMillis); } diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java index fbad75e93a17..fbbee94feacc 100644 --- a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java +++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java @@ -22,6 +22,8 @@ import android.os.Process; import com.android.internal.annotations.VisibleForTesting; import java.io.IOException; +import java.nio.file.Path; +import java.util.Arrays; /** * Reads /proc/UID/task/TID/time_in_state files to obtain statistics on CPU usage @@ -29,7 +31,9 @@ import java.io.IOException; */ public class SystemServerCpuThreadReader { private final KernelSingleProcessCpuThreadReader mKernelCpuThreadReader; + private int[] mBinderThreadNativeTids = new int[0]; // Sorted + private long[] mLastProcessCpuTimeUs; private long[] mLastThreadCpuTimesUs; private long[] mLastBinderThreadCpuTimesUs; @@ -37,6 +41,8 @@ public class SystemServerCpuThreadReader { * Times (in microseconds) spent by the system server UID. */ public static class SystemServiceCpuThreadTimes { + // The entire process + public long[] processCpuTimesUs; // All threads public long[] threadCpuTimesUs; // Just the threads handling incoming binder calls @@ -55,10 +61,8 @@ public class SystemServerCpuThreadReader { } @VisibleForTesting - public SystemServerCpuThreadReader(int pid, - KernelSingleProcessCpuThreadReader.CpuTimeInStateReader cpuTimeInStateReader) - throws IOException { - this(new KernelSingleProcessCpuThreadReader(pid, cpuTimeInStateReader)); + public SystemServerCpuThreadReader(Path procPath, int pid) throws IOException { + this(new KernelSingleProcessCpuThreadReader(pid, procPath)); } @VisibleForTesting @@ -66,15 +70,9 @@ public class SystemServerCpuThreadReader { mKernelCpuThreadReader = kernelCpuThreadReader; } - /** - * Start tracking CPU time-in-state for the process specified in the constructor. - */ - public void startTrackingThreadCpuTime() { - mKernelCpuThreadReader.startTrackingThreadCpuTimes(); - } - public void setBinderThreadNativeTids(int[] nativeTids) { - mKernelCpuThreadReader.setSelectedThreadIds(nativeTids); + mBinderThreadNativeTids = nativeTids.clone(); + Arrays.sort(mBinderThreadNativeTids); } /** @@ -83,27 +81,33 @@ public class SystemServerCpuThreadReader { @Nullable public SystemServiceCpuThreadTimes readDelta() { final int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequencyCount(); - if (mLastThreadCpuTimesUs == null) { + 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]; } final KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage = - mKernelCpuThreadReader.getProcessCpuUsage(); + mKernelCpuThreadReader.getProcessCpuUsage(mBinderThreadNativeTids); if (processCpuUsage == null) { return null; } 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, threadCpuTimesUs - mLastThreadCpuTimesUs[i]); mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] = Math.max(0, binderThreadCpuTimesUs - mLastBinderThreadCpuTimesUs[i]); + mLastProcessCpuTimeUs[i] = processCpuTimesUs; mLastThreadCpuTimesUs[i] = threadCpuTimesUs; mLastBinderThreadCpuTimesUs[i] = binderThreadCpuTimesUs; } |
