Loading core/java/com/android/internal/os/BatteryStatsImpl.java +36 −8 Original line number Diff line number Diff line Loading @@ -110,7 +110,10 @@ import java.util.Calendar; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; Loading Loading @@ -12334,7 +12337,7 @@ public class BatteryStatsImpl extends BatteryStats { @VisibleForTesting public void readKernelUidCpuActiveTimesLocked(boolean onBattery) { final long startTimeMs = mClocks.uptimeMillis(); mKernelUidCpuActiveTimeReader.readDelta((uid, cpuActiveTimesUs) -> { mKernelUidCpuActiveTimeReader.readDelta((uid, cpuActiveTimesMs) -> { uid = mapUid(uid); if (Process.isIsolated(uid)) { mKernelUidCpuActiveTimeReader.removeUid(uid); Loading @@ -12347,7 +12350,7 @@ public class BatteryStatsImpl extends BatteryStats { return; } final Uid u = getUidStatsLocked(uid); u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesUs, onBattery); u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesMs, onBattery); }); final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs; Loading @@ -12363,7 +12366,7 @@ public class BatteryStatsImpl extends BatteryStats { @VisibleForTesting public void readKernelUidCpuClusterTimesLocked(boolean onBattery) { final long startTimeMs = mClocks.uptimeMillis(); mKernelUidCpuClusterTimeReader.readDelta((uid, cpuClusterTimesUs) -> { mKernelUidCpuClusterTimeReader.readDelta((uid, cpuClusterTimesMs) -> { uid = mapUid(uid); if (Process.isIsolated(uid)) { mKernelUidCpuClusterTimeReader.removeUid(uid); Loading @@ -12376,7 +12379,7 @@ public class BatteryStatsImpl extends BatteryStats { return; } final Uid u = getUidStatsLocked(uid); u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesUs, onBattery); u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesMs, onBattery); }); final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs; Loading Loading @@ -13326,17 +13329,20 @@ public class BatteryStatsImpl extends BatteryStats { = "read_binary_cpu_time"; public static final String KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS = "proc_state_cpu_times_read_delay_ms"; public static final String KEY_KERNEL_UID_READERS_THROTTLE_TIME = "kernel_uid_readers_throttle_time"; private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true; private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true; private static final boolean DEFAULT_READ_BINARY_CPU_TIME = false; private static final long DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS = 5_000; private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 10_000; public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE; public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME; // Not used right now. public boolean READ_BINARY_CPU_TIME = DEFAULT_READ_BINARY_CPU_TIME; public long PROC_STATE_CPU_TIMES_READ_DELAY_MS = DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS; public long KERNEL_UID_READERS_THROTTLE_TIME = DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME; private ContentResolver mResolver; private final KeyValueListParser mParser = new KeyValueListParser(','); Loading Loading @@ -13374,11 +13380,14 @@ public class BatteryStatsImpl extends BatteryStats { DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE)); TRACK_CPU_ACTIVE_CLUSTER_TIME = mParser.getBoolean( KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME, DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME); READ_BINARY_CPU_TIME = mParser.getBoolean( KEY_READ_BINARY_CPU_TIME, DEFAULT_READ_BINARY_CPU_TIME); updateReadBinaryCpuTime(READ_BINARY_CPU_TIME, mParser.getBoolean(KEY_READ_BINARY_CPU_TIME, DEFAULT_READ_BINARY_CPU_TIME)); updateProcStateCpuTimesReadDelayMs(PROC_STATE_CPU_TIMES_READ_DELAY_MS, mParser.getLong(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS, DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS)); updateKernelUidReadersThrottleTime(KERNEL_UID_READERS_THROTTLE_TIME, mParser.getLong(KEY_KERNEL_UID_READERS_THROTTLE_TIME, DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME)); } } Loading @@ -13394,6 +13403,13 @@ public class BatteryStatsImpl extends BatteryStats { } } private void updateReadBinaryCpuTime(boolean oldEnabled, boolean isEnabled) { READ_BINARY_CPU_TIME = isEnabled; if (oldEnabled != isEnabled) { mKernelUidCpuFreqTimeReader.setReadBinary(isEnabled); } } private void updateProcStateCpuTimesReadDelayMs(long oldDelayMillis, long newDelayMillis) { PROC_STATE_CPU_TIMES_READ_DELAY_MS = newDelayMillis; if (oldDelayMillis != newDelayMillis) { Loading @@ -13403,6 +13419,16 @@ public class BatteryStatsImpl extends BatteryStats { } } private void updateKernelUidReadersThrottleTime(long oldTimeMs, long newTimeMs) { KERNEL_UID_READERS_THROTTLE_TIME = newTimeMs; if (oldTimeMs != newTimeMs) { mKernelUidCpuFreqTimeReader.setThrottleInterval(KERNEL_UID_READERS_THROTTLE_TIME); mKernelUidCpuActiveTimeReader.setThrottleInterval(KERNEL_UID_READERS_THROTTLE_TIME); mKernelUidCpuClusterTimeReader .setThrottleInterval(KERNEL_UID_READERS_THROTTLE_TIME); } } public void dumpLocked(PrintWriter pw) { pw.print(KEY_TRACK_CPU_TIMES_BY_PROC_STATE); pw.print("="); pw.println(TRACK_CPU_TIMES_BY_PROC_STATE); Loading @@ -13412,6 +13438,8 @@ public class BatteryStatsImpl extends BatteryStats { pw.println(READ_BINARY_CPU_TIME); pw.print(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS); pw.print("="); pw.println(PROC_STATE_CPU_TIMES_READ_DELAY_MS); pw.print(KEY_KERNEL_UID_READERS_THROTTLE_TIME); pw.print("="); pw.println(KERNEL_UID_READERS_THROTTLE_TIME); } } core/java/com/android/internal/os/CpuPowerCalculator.java +3 −2 Original line number Diff line number Diff line Loading @@ -50,13 +50,14 @@ public class CpuPowerCalculator extends PowerCalculator { cpuPowerMaUs += cpuSpeedStepPower; } } cpuPowerMaUs += u.getCpuActiveTime() * mProfile.getAveragePower( cpuPowerMaUs += u.getCpuActiveTime() * 1000 * mProfile.getAveragePower( PowerProfile.POWER_CPU_ACTIVE); long[] cpuClusterTimes = u.getCpuClusterTimes(); if (cpuClusterTimes != null) { if (cpuClusterTimes.length == numClusters) { for (int i = 0; i < numClusters; i++) { double power = cpuClusterTimes[i] * mProfile.getAveragePowerForCpuCluster(i); double power = cpuClusterTimes[i] * 1000 * mProfile.getAveragePowerForCpuCluster(i); cpuPowerMaUs += power; if (DEBUG) { Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + i + " clusterTimeUs=" Loading core/java/com/android/internal/os/KernelCpuProcReader.java 0 → 100644 +159 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.os; import android.os.StrictMode; import android.os.SystemClock; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; /** * Reads cpu time proc files with throttling (adjustable interval). * * KernelCpuProcReader is implemented as singletons for built-in kernel proc files. Get___Instance() * method will return corresponding reader instance. In order to prevent frequent GC, * KernelCpuProcReader reuses a {@link ByteBuffer} to store data read from proc files. * * A KernelCpuProcReader instance keeps an error counter. When the number of read errors within that * instance accumulates to 5, this instance will reject all further read requests. * * Each KernelCpuProcReader instance also has a throttler. Throttle interval can be adjusted via * {@link #setThrottleInterval(long)} method. Default throttle interval is 3000ms. If current * timestamp based on {@link SystemClock#elapsedRealtime()} is less than throttle interval from * the last read timestamp, {@link #readBytes()} will return previous result. * * A KernelCpuProcReader instance is thread-unsafe. Caller needs to hold a lock on this object while * accessing its instance methods or digesting the return values. */ public class KernelCpuProcReader { private static final String TAG = "KernelCpuProcReader"; private static final int ERROR_THRESHOLD = 5; // Throttle interval in milliseconds private static final long DEFAULT_THROTTLE_INTERVAL = 3000L; private static final int INITIAL_BUFFER_SIZE = 8 * 1024; private static final int MAX_BUFFER_SIZE = 1024 * 1024; private static final String PROC_UID_FREQ_TIME = "/proc/uid_cpupower/time_in_state"; private static final String PROC_UID_ACTIVE_TIME = "/proc/uid_cpupower/concurrent_active_time"; private static final String PROC_UID_CLUSTER_TIME = "/proc/uid_cpupower/concurrent_policy_time"; private static final KernelCpuProcReader mFreqTimeReader = new KernelCpuProcReader( PROC_UID_FREQ_TIME); private static final KernelCpuProcReader mActiveTimeReader = new KernelCpuProcReader( PROC_UID_ACTIVE_TIME); private static final KernelCpuProcReader mClusterTimeReader = new KernelCpuProcReader( PROC_UID_CLUSTER_TIME); public static KernelCpuProcReader getFreqTimeReaderInstance() { return mFreqTimeReader; } public static KernelCpuProcReader getActiveTimeReaderInstance() { return mActiveTimeReader; } public static KernelCpuProcReader getClusterTimeReaderInstance() { return mClusterTimeReader; } private int mErrors; private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL; private long mLastReadTime = Long.MIN_VALUE; private final Path mProc; private ByteBuffer mBuffer; @VisibleForTesting public KernelCpuProcReader(String procFile) { mProc = Paths.get(procFile); mBuffer = ByteBuffer.allocateDirect(INITIAL_BUFFER_SIZE); mBuffer.clear(); } /** * Reads all bytes from the corresponding proc file. * * If elapsed time since last call to this method is less than the throttle interval, it will * return previous result. When IOException accumulates to 5, it will always return null. This * method is thread-unsafe, so is the return value. Caller needs to hold a lock on this * object while calling this method and digesting its return value. * * @return a {@link ByteBuffer} containing all bytes from the proc file. */ public ByteBuffer readBytes() { if (mErrors >= ERROR_THRESHOLD) { return null; } if (SystemClock.elapsedRealtime() < mLastReadTime + mThrottleInterval) { if (mBuffer.limit() > 0 && mBuffer.limit() < mBuffer.capacity()) { // mBuffer has data. return mBuffer.asReadOnlyBuffer().order(ByteOrder.nativeOrder()); } return null; } mLastReadTime = SystemClock.elapsedRealtime(); mBuffer.clear(); final int oldMask = StrictMode.allowThreadDiskReadsMask(); try (FileChannel fc = FileChannel.open(mProc, StandardOpenOption.READ)) { while (fc.read(mBuffer) == mBuffer.capacity()) { if (!resize()) { mErrors++; Slog.e(TAG, "Proc file is too large: " + mProc); return null; } fc.position(0); } } catch (IOException e) { mErrors++; Slog.e(TAG, "Error reading: " + mProc, e); return null; } finally { StrictMode.setThreadPolicyMask(oldMask); } mBuffer.flip(); return mBuffer.asReadOnlyBuffer().order(ByteOrder.nativeOrder()); } /** * Sets the throttle interval. Set to 0 will disable throttling. Thread-unsafe, holding a lock * on this object is recommended. * * @param throttleInterval throttle interval in milliseconds */ public void setThrottleInterval(long throttleInterval) { if (throttleInterval >= 0) { mThrottleInterval = throttleInterval; } } private boolean resize() { if (mBuffer.capacity() >= MAX_BUFFER_SIZE) { return false; } int newSize = Math.min(mBuffer.capacity() << 1, MAX_BUFFER_SIZE); // Slog.i(TAG, "Resize buffer " + mBuffer.capacity() + " => " + newSize); mBuffer = ByteBuffer.allocateDirect(newSize); return true; } } core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java +93 −86 Original line number Diff line number Diff line Loading @@ -17,130 +17,137 @@ package com.android.internal.os; import android.annotation.Nullable; import android.os.StrictMode; import android.os.SystemClock; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; import com.android.internal.annotations.VisibleForTesting; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.IntBuffer; /** * Reads /proc/uid_concurrent_active_time which has the format: * active: X (X is # cores) * [uid0]: [time-0] [time-1] [time-2] ... (# entries = # cores) * [uid1]: [time-0] [time-1] [time-2] ... ... * Reads binary proc file /proc/uid_cpupower/concurrent_active_time and reports CPU active time to * BatteryStats to compute {@link PowerProfile#POWER_CPU_ACTIVE}. * * concurrent_active_time is an array of u32's in the following format: * [n, uid0, time0a, time0b, ..., time0n, * uid1, time1a, time1b, ..., time1n, * uid2, time2a, time2b, ..., time2n, etc.] * where n is the total number of cpus (num_possible_cpus) * ... * Time-N means the CPU time a UID spent running concurrently with N other processes. * timeXn means the CPU time that a UID X spent running concurrently with n other processes. * The file contains a monotonically increasing count of time for a single boot. This class * maintains the previous results of a call to {@link #readDelta} in order to provide a * proper delta. * * This class uses a throttler to reject any {@link #readDelta} call within * {@link #mThrottleInterval}. This is different from the throttler in {@link KernelCpuProcReader}, * which has a shorter throttle interval and returns cached result from last read when the request * is throttled. * * This class is NOT thread-safe and NOT designed to be accessed by more than one caller (due to * the nature of {@link #readDelta(Callback)}). */ public class KernelUidCpuActiveTimeReader { private static final boolean DEBUG = false; private static final String TAG = "KernelUidCpuActiveTimeReader"; private static final String UID_TIMES_PROC_FILE = "/proc/uid_concurrent_active_time"; // Throttle interval in milliseconds private static final long DEFAULT_THROTTLE_INTERVAL = 10_000L; private int mCoreCount; private long mLastTimeReadMs; private long mNowTimeMs; private SparseArray<long[]> mLastUidCpuActiveTimeMs = new SparseArray<>(); private final KernelCpuProcReader mProcReader; private long mLastTimeReadMs = Long.MIN_VALUE; private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL; private SparseArray<Double> mLastUidCpuActiveTimeMs = new SparseArray<>(); public interface Callback { /** * Notifies when new data is available. * * @param uid uid int * @param cpuActiveTimeMs cpu active time spent by this uid in milliseconds */ void onUidCpuActiveTime(int uid, long cpuActiveTimeMs); } public void readDelta(@Nullable Callback cb) { final int oldMask = StrictMode.allowThreadDiskReadsMask(); try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) { mNowTimeMs = SystemClock.elapsedRealtime(); readDeltaInternal(reader, cb); mLastTimeReadMs = mNowTimeMs; } catch (IOException e) { Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e); } finally { StrictMode.setThreadPolicyMask(oldMask); } public KernelUidCpuActiveTimeReader() { mProcReader = KernelCpuProcReader.getActiveTimeReaderInstance(); } public void removeUid(int uid) { mLastUidCpuActiveTimeMs.delete(uid); @VisibleForTesting public KernelUidCpuActiveTimeReader(KernelCpuProcReader procReader) { mProcReader = procReader; } public void removeUidsInRange(int startUid, int endUid) { if (endUid < startUid) { Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid); public void readDelta(@Nullable Callback cb) { if (SystemClock.elapsedRealtime() < mLastTimeReadMs + mThrottleInterval) { Slog.w(TAG, "Throttle"); return; } mLastUidCpuActiveTimeMs.put(startUid, null); mLastUidCpuActiveTimeMs.put(endUid, null); final int firstIndex = mLastUidCpuActiveTimeMs.indexOfKey(startUid); final int lastIndex = mLastUidCpuActiveTimeMs.indexOfKey(endUid); mLastUidCpuActiveTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1); synchronized (mProcReader) { final ByteBuffer bytes = mProcReader.readBytes(); if (bytes == null || bytes.remaining() <= 4) { // Error already logged in mProcReader. return; } @VisibleForTesting public void readDeltaInternal(BufferedReader reader, @Nullable Callback cb) throws IOException { String line = reader.readLine(); if (line == null || !line.startsWith("active:")) { Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE)); if ((bytes.remaining() & 3) != 0) { Slog.wtf(TAG, "Cannot parse active time proc bytes to int: " + bytes.remaining()); return; } if (mCoreCount == 0) { mCoreCount = Integer.parseInt(line.substring(line.indexOf(' ')+1)); final IntBuffer buf = bytes.asIntBuffer(); final int cores = buf.get(); if (cores <= 0 || buf.remaining() % (cores + 1) != 0) { Slog.wtf(TAG, "Cpu active time format error: " + buf.remaining() + " / " + (cores + 1)); return; } while ((line = reader.readLine()) != null) { final int index = line.indexOf(' '); final int uid = Integer.parseInt(line.substring(0, index - 1), 10); readTimesForUid(uid, line.substring(index + 1), cb); int numUids = buf.remaining() / (cores + 1); for (int i = 0; i < numUids; i++) { int uid = buf.get(); boolean corrupted = false; double curTime = 0; for (int j = 1; j <= cores; j++) { int time = buf.get(); if (time < 0) { Slog.e(TAG, "Corrupted data from active time proc: " + time); corrupted = true; } else { curTime += (double) time * 10 / j; // Unit is 10ms. } } double delta = curTime - mLastUidCpuActiveTimeMs.get(uid, 0.0); if (delta > 0 && !corrupted) { mLastUidCpuActiveTimeMs.put(uid, curTime); if (cb != null) { cb.onUidCpuActiveTime(uid, (long) delta); } } private void readTimesForUid(int uid, String line, @Nullable Callback cb) { long[] lastActiveTime = mLastUidCpuActiveTimeMs.get(uid); if (lastActiveTime == null) { lastActiveTime = new long[mCoreCount]; mLastUidCpuActiveTimeMs.put(uid, lastActiveTime); } final String[] timesStr = line.split(" "); if (timesStr.length != mCoreCount) { Slog.e(TAG, String.format("# readings don't match # cores, readings: %d, CPU cores: %d", timesStr.length, mCoreCount)); return; } long sumDeltas = 0; final long[] curActiveTime = new long[mCoreCount]; boolean notify = false; for (int i = 0; i < mCoreCount; i++) { // Times read will be in units of 10ms curActiveTime[i] = Long.parseLong(timesStr[i], 10) * 10; long delta = curActiveTime[i] - lastActiveTime[i]; if (delta < 0 || curActiveTime[i] < 0) { if (DEBUG) { final StringBuilder sb = new StringBuilder(); sb.append(String.format("Malformed cpu active time for UID=%d\n", uid)); sb.append(String.format("data=(%d,%d)\n", lastActiveTime[i], curActiveTime[i])); sb.append("times=("); TimeUtils.formatDuration(mLastTimeReadMs, sb); sb.append(","); TimeUtils.formatDuration(mNowTimeMs, sb); sb.append(")"); Slog.e(TAG, sb.toString()); // Slog.i(TAG, "Read uids: " + numUids); } return; mLastTimeReadMs = SystemClock.elapsedRealtime(); } notify |= delta > 0; sumDeltas += delta / (i + 1); public void setThrottleInterval(long throttleInterval) { if (throttleInterval >= 0) { mThrottleInterval = throttleInterval; } if (notify) { System.arraycopy(curActiveTime, 0, lastActiveTime, 0, mCoreCount); if (cb != null) { cb.onUidCpuActiveTime(uid, sumDeltas); } public void removeUid(int uid) { mLastUidCpuActiveTimeMs.delete(uid); } public void removeUidsInRange(int startUid, int endUid) { if (endUid < startUid) { Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid); return; } mLastUidCpuActiveTimeMs.put(startUid, null); mLastUidCpuActiveTimeMs.put(endUid, null); final int firstIndex = mLastUidCpuActiveTimeMs.indexOfKey(startUid); final int lastIndex = mLastUidCpuActiveTimeMs.indexOfKey(endUid); mLastUidCpuActiveTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1); } } core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java +156 −112 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/com/android/internal/os/BatteryStatsImpl.java +36 −8 Original line number Diff line number Diff line Loading @@ -110,7 +110,10 @@ import java.util.Calendar; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; Loading Loading @@ -12334,7 +12337,7 @@ public class BatteryStatsImpl extends BatteryStats { @VisibleForTesting public void readKernelUidCpuActiveTimesLocked(boolean onBattery) { final long startTimeMs = mClocks.uptimeMillis(); mKernelUidCpuActiveTimeReader.readDelta((uid, cpuActiveTimesUs) -> { mKernelUidCpuActiveTimeReader.readDelta((uid, cpuActiveTimesMs) -> { uid = mapUid(uid); if (Process.isIsolated(uid)) { mKernelUidCpuActiveTimeReader.removeUid(uid); Loading @@ -12347,7 +12350,7 @@ public class BatteryStatsImpl extends BatteryStats { return; } final Uid u = getUidStatsLocked(uid); u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesUs, onBattery); u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesMs, onBattery); }); final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs; Loading @@ -12363,7 +12366,7 @@ public class BatteryStatsImpl extends BatteryStats { @VisibleForTesting public void readKernelUidCpuClusterTimesLocked(boolean onBattery) { final long startTimeMs = mClocks.uptimeMillis(); mKernelUidCpuClusterTimeReader.readDelta((uid, cpuClusterTimesUs) -> { mKernelUidCpuClusterTimeReader.readDelta((uid, cpuClusterTimesMs) -> { uid = mapUid(uid); if (Process.isIsolated(uid)) { mKernelUidCpuClusterTimeReader.removeUid(uid); Loading @@ -12376,7 +12379,7 @@ public class BatteryStatsImpl extends BatteryStats { return; } final Uid u = getUidStatsLocked(uid); u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesUs, onBattery); u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesMs, onBattery); }); final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs; Loading Loading @@ -13326,17 +13329,20 @@ public class BatteryStatsImpl extends BatteryStats { = "read_binary_cpu_time"; public static final String KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS = "proc_state_cpu_times_read_delay_ms"; public static final String KEY_KERNEL_UID_READERS_THROTTLE_TIME = "kernel_uid_readers_throttle_time"; private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true; private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true; private static final boolean DEFAULT_READ_BINARY_CPU_TIME = false; private static final long DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS = 5_000; private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 10_000; public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE; public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME; // Not used right now. public boolean READ_BINARY_CPU_TIME = DEFAULT_READ_BINARY_CPU_TIME; public long PROC_STATE_CPU_TIMES_READ_DELAY_MS = DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS; public long KERNEL_UID_READERS_THROTTLE_TIME = DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME; private ContentResolver mResolver; private final KeyValueListParser mParser = new KeyValueListParser(','); Loading Loading @@ -13374,11 +13380,14 @@ public class BatteryStatsImpl extends BatteryStats { DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE)); TRACK_CPU_ACTIVE_CLUSTER_TIME = mParser.getBoolean( KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME, DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME); READ_BINARY_CPU_TIME = mParser.getBoolean( KEY_READ_BINARY_CPU_TIME, DEFAULT_READ_BINARY_CPU_TIME); updateReadBinaryCpuTime(READ_BINARY_CPU_TIME, mParser.getBoolean(KEY_READ_BINARY_CPU_TIME, DEFAULT_READ_BINARY_CPU_TIME)); updateProcStateCpuTimesReadDelayMs(PROC_STATE_CPU_TIMES_READ_DELAY_MS, mParser.getLong(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS, DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS)); updateKernelUidReadersThrottleTime(KERNEL_UID_READERS_THROTTLE_TIME, mParser.getLong(KEY_KERNEL_UID_READERS_THROTTLE_TIME, DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME)); } } Loading @@ -13394,6 +13403,13 @@ public class BatteryStatsImpl extends BatteryStats { } } private void updateReadBinaryCpuTime(boolean oldEnabled, boolean isEnabled) { READ_BINARY_CPU_TIME = isEnabled; if (oldEnabled != isEnabled) { mKernelUidCpuFreqTimeReader.setReadBinary(isEnabled); } } private void updateProcStateCpuTimesReadDelayMs(long oldDelayMillis, long newDelayMillis) { PROC_STATE_CPU_TIMES_READ_DELAY_MS = newDelayMillis; if (oldDelayMillis != newDelayMillis) { Loading @@ -13403,6 +13419,16 @@ public class BatteryStatsImpl extends BatteryStats { } } private void updateKernelUidReadersThrottleTime(long oldTimeMs, long newTimeMs) { KERNEL_UID_READERS_THROTTLE_TIME = newTimeMs; if (oldTimeMs != newTimeMs) { mKernelUidCpuFreqTimeReader.setThrottleInterval(KERNEL_UID_READERS_THROTTLE_TIME); mKernelUidCpuActiveTimeReader.setThrottleInterval(KERNEL_UID_READERS_THROTTLE_TIME); mKernelUidCpuClusterTimeReader .setThrottleInterval(KERNEL_UID_READERS_THROTTLE_TIME); } } public void dumpLocked(PrintWriter pw) { pw.print(KEY_TRACK_CPU_TIMES_BY_PROC_STATE); pw.print("="); pw.println(TRACK_CPU_TIMES_BY_PROC_STATE); Loading @@ -13412,6 +13438,8 @@ public class BatteryStatsImpl extends BatteryStats { pw.println(READ_BINARY_CPU_TIME); pw.print(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS); pw.print("="); pw.println(PROC_STATE_CPU_TIMES_READ_DELAY_MS); pw.print(KEY_KERNEL_UID_READERS_THROTTLE_TIME); pw.print("="); pw.println(KERNEL_UID_READERS_THROTTLE_TIME); } }
core/java/com/android/internal/os/CpuPowerCalculator.java +3 −2 Original line number Diff line number Diff line Loading @@ -50,13 +50,14 @@ public class CpuPowerCalculator extends PowerCalculator { cpuPowerMaUs += cpuSpeedStepPower; } } cpuPowerMaUs += u.getCpuActiveTime() * mProfile.getAveragePower( cpuPowerMaUs += u.getCpuActiveTime() * 1000 * mProfile.getAveragePower( PowerProfile.POWER_CPU_ACTIVE); long[] cpuClusterTimes = u.getCpuClusterTimes(); if (cpuClusterTimes != null) { if (cpuClusterTimes.length == numClusters) { for (int i = 0; i < numClusters; i++) { double power = cpuClusterTimes[i] * mProfile.getAveragePowerForCpuCluster(i); double power = cpuClusterTimes[i] * 1000 * mProfile.getAveragePowerForCpuCluster(i); cpuPowerMaUs += power; if (DEBUG) { Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + i + " clusterTimeUs=" Loading
core/java/com/android/internal/os/KernelCpuProcReader.java 0 → 100644 +159 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.os; import android.os.StrictMode; import android.os.SystemClock; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; /** * Reads cpu time proc files with throttling (adjustable interval). * * KernelCpuProcReader is implemented as singletons for built-in kernel proc files. Get___Instance() * method will return corresponding reader instance. In order to prevent frequent GC, * KernelCpuProcReader reuses a {@link ByteBuffer} to store data read from proc files. * * A KernelCpuProcReader instance keeps an error counter. When the number of read errors within that * instance accumulates to 5, this instance will reject all further read requests. * * Each KernelCpuProcReader instance also has a throttler. Throttle interval can be adjusted via * {@link #setThrottleInterval(long)} method. Default throttle interval is 3000ms. If current * timestamp based on {@link SystemClock#elapsedRealtime()} is less than throttle interval from * the last read timestamp, {@link #readBytes()} will return previous result. * * A KernelCpuProcReader instance is thread-unsafe. Caller needs to hold a lock on this object while * accessing its instance methods or digesting the return values. */ public class KernelCpuProcReader { private static final String TAG = "KernelCpuProcReader"; private static final int ERROR_THRESHOLD = 5; // Throttle interval in milliseconds private static final long DEFAULT_THROTTLE_INTERVAL = 3000L; private static final int INITIAL_BUFFER_SIZE = 8 * 1024; private static final int MAX_BUFFER_SIZE = 1024 * 1024; private static final String PROC_UID_FREQ_TIME = "/proc/uid_cpupower/time_in_state"; private static final String PROC_UID_ACTIVE_TIME = "/proc/uid_cpupower/concurrent_active_time"; private static final String PROC_UID_CLUSTER_TIME = "/proc/uid_cpupower/concurrent_policy_time"; private static final KernelCpuProcReader mFreqTimeReader = new KernelCpuProcReader( PROC_UID_FREQ_TIME); private static final KernelCpuProcReader mActiveTimeReader = new KernelCpuProcReader( PROC_UID_ACTIVE_TIME); private static final KernelCpuProcReader mClusterTimeReader = new KernelCpuProcReader( PROC_UID_CLUSTER_TIME); public static KernelCpuProcReader getFreqTimeReaderInstance() { return mFreqTimeReader; } public static KernelCpuProcReader getActiveTimeReaderInstance() { return mActiveTimeReader; } public static KernelCpuProcReader getClusterTimeReaderInstance() { return mClusterTimeReader; } private int mErrors; private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL; private long mLastReadTime = Long.MIN_VALUE; private final Path mProc; private ByteBuffer mBuffer; @VisibleForTesting public KernelCpuProcReader(String procFile) { mProc = Paths.get(procFile); mBuffer = ByteBuffer.allocateDirect(INITIAL_BUFFER_SIZE); mBuffer.clear(); } /** * Reads all bytes from the corresponding proc file. * * If elapsed time since last call to this method is less than the throttle interval, it will * return previous result. When IOException accumulates to 5, it will always return null. This * method is thread-unsafe, so is the return value. Caller needs to hold a lock on this * object while calling this method and digesting its return value. * * @return a {@link ByteBuffer} containing all bytes from the proc file. */ public ByteBuffer readBytes() { if (mErrors >= ERROR_THRESHOLD) { return null; } if (SystemClock.elapsedRealtime() < mLastReadTime + mThrottleInterval) { if (mBuffer.limit() > 0 && mBuffer.limit() < mBuffer.capacity()) { // mBuffer has data. return mBuffer.asReadOnlyBuffer().order(ByteOrder.nativeOrder()); } return null; } mLastReadTime = SystemClock.elapsedRealtime(); mBuffer.clear(); final int oldMask = StrictMode.allowThreadDiskReadsMask(); try (FileChannel fc = FileChannel.open(mProc, StandardOpenOption.READ)) { while (fc.read(mBuffer) == mBuffer.capacity()) { if (!resize()) { mErrors++; Slog.e(TAG, "Proc file is too large: " + mProc); return null; } fc.position(0); } } catch (IOException e) { mErrors++; Slog.e(TAG, "Error reading: " + mProc, e); return null; } finally { StrictMode.setThreadPolicyMask(oldMask); } mBuffer.flip(); return mBuffer.asReadOnlyBuffer().order(ByteOrder.nativeOrder()); } /** * Sets the throttle interval. Set to 0 will disable throttling. Thread-unsafe, holding a lock * on this object is recommended. * * @param throttleInterval throttle interval in milliseconds */ public void setThrottleInterval(long throttleInterval) { if (throttleInterval >= 0) { mThrottleInterval = throttleInterval; } } private boolean resize() { if (mBuffer.capacity() >= MAX_BUFFER_SIZE) { return false; } int newSize = Math.min(mBuffer.capacity() << 1, MAX_BUFFER_SIZE); // Slog.i(TAG, "Resize buffer " + mBuffer.capacity() + " => " + newSize); mBuffer = ByteBuffer.allocateDirect(newSize); return true; } }
core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java +93 −86 Original line number Diff line number Diff line Loading @@ -17,130 +17,137 @@ package com.android.internal.os; import android.annotation.Nullable; import android.os.StrictMode; import android.os.SystemClock; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; import com.android.internal.annotations.VisibleForTesting; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.IntBuffer; /** * Reads /proc/uid_concurrent_active_time which has the format: * active: X (X is # cores) * [uid0]: [time-0] [time-1] [time-2] ... (# entries = # cores) * [uid1]: [time-0] [time-1] [time-2] ... ... * Reads binary proc file /proc/uid_cpupower/concurrent_active_time and reports CPU active time to * BatteryStats to compute {@link PowerProfile#POWER_CPU_ACTIVE}. * * concurrent_active_time is an array of u32's in the following format: * [n, uid0, time0a, time0b, ..., time0n, * uid1, time1a, time1b, ..., time1n, * uid2, time2a, time2b, ..., time2n, etc.] * where n is the total number of cpus (num_possible_cpus) * ... * Time-N means the CPU time a UID spent running concurrently with N other processes. * timeXn means the CPU time that a UID X spent running concurrently with n other processes. * The file contains a monotonically increasing count of time for a single boot. This class * maintains the previous results of a call to {@link #readDelta} in order to provide a * proper delta. * * This class uses a throttler to reject any {@link #readDelta} call within * {@link #mThrottleInterval}. This is different from the throttler in {@link KernelCpuProcReader}, * which has a shorter throttle interval and returns cached result from last read when the request * is throttled. * * This class is NOT thread-safe and NOT designed to be accessed by more than one caller (due to * the nature of {@link #readDelta(Callback)}). */ public class KernelUidCpuActiveTimeReader { private static final boolean DEBUG = false; private static final String TAG = "KernelUidCpuActiveTimeReader"; private static final String UID_TIMES_PROC_FILE = "/proc/uid_concurrent_active_time"; // Throttle interval in milliseconds private static final long DEFAULT_THROTTLE_INTERVAL = 10_000L; private int mCoreCount; private long mLastTimeReadMs; private long mNowTimeMs; private SparseArray<long[]> mLastUidCpuActiveTimeMs = new SparseArray<>(); private final KernelCpuProcReader mProcReader; private long mLastTimeReadMs = Long.MIN_VALUE; private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL; private SparseArray<Double> mLastUidCpuActiveTimeMs = new SparseArray<>(); public interface Callback { /** * Notifies when new data is available. * * @param uid uid int * @param cpuActiveTimeMs cpu active time spent by this uid in milliseconds */ void onUidCpuActiveTime(int uid, long cpuActiveTimeMs); } public void readDelta(@Nullable Callback cb) { final int oldMask = StrictMode.allowThreadDiskReadsMask(); try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) { mNowTimeMs = SystemClock.elapsedRealtime(); readDeltaInternal(reader, cb); mLastTimeReadMs = mNowTimeMs; } catch (IOException e) { Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e); } finally { StrictMode.setThreadPolicyMask(oldMask); } public KernelUidCpuActiveTimeReader() { mProcReader = KernelCpuProcReader.getActiveTimeReaderInstance(); } public void removeUid(int uid) { mLastUidCpuActiveTimeMs.delete(uid); @VisibleForTesting public KernelUidCpuActiveTimeReader(KernelCpuProcReader procReader) { mProcReader = procReader; } public void removeUidsInRange(int startUid, int endUid) { if (endUid < startUid) { Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid); public void readDelta(@Nullable Callback cb) { if (SystemClock.elapsedRealtime() < mLastTimeReadMs + mThrottleInterval) { Slog.w(TAG, "Throttle"); return; } mLastUidCpuActiveTimeMs.put(startUid, null); mLastUidCpuActiveTimeMs.put(endUid, null); final int firstIndex = mLastUidCpuActiveTimeMs.indexOfKey(startUid); final int lastIndex = mLastUidCpuActiveTimeMs.indexOfKey(endUid); mLastUidCpuActiveTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1); synchronized (mProcReader) { final ByteBuffer bytes = mProcReader.readBytes(); if (bytes == null || bytes.remaining() <= 4) { // Error already logged in mProcReader. return; } @VisibleForTesting public void readDeltaInternal(BufferedReader reader, @Nullable Callback cb) throws IOException { String line = reader.readLine(); if (line == null || !line.startsWith("active:")) { Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE)); if ((bytes.remaining() & 3) != 0) { Slog.wtf(TAG, "Cannot parse active time proc bytes to int: " + bytes.remaining()); return; } if (mCoreCount == 0) { mCoreCount = Integer.parseInt(line.substring(line.indexOf(' ')+1)); final IntBuffer buf = bytes.asIntBuffer(); final int cores = buf.get(); if (cores <= 0 || buf.remaining() % (cores + 1) != 0) { Slog.wtf(TAG, "Cpu active time format error: " + buf.remaining() + " / " + (cores + 1)); return; } while ((line = reader.readLine()) != null) { final int index = line.indexOf(' '); final int uid = Integer.parseInt(line.substring(0, index - 1), 10); readTimesForUid(uid, line.substring(index + 1), cb); int numUids = buf.remaining() / (cores + 1); for (int i = 0; i < numUids; i++) { int uid = buf.get(); boolean corrupted = false; double curTime = 0; for (int j = 1; j <= cores; j++) { int time = buf.get(); if (time < 0) { Slog.e(TAG, "Corrupted data from active time proc: " + time); corrupted = true; } else { curTime += (double) time * 10 / j; // Unit is 10ms. } } double delta = curTime - mLastUidCpuActiveTimeMs.get(uid, 0.0); if (delta > 0 && !corrupted) { mLastUidCpuActiveTimeMs.put(uid, curTime); if (cb != null) { cb.onUidCpuActiveTime(uid, (long) delta); } } private void readTimesForUid(int uid, String line, @Nullable Callback cb) { long[] lastActiveTime = mLastUidCpuActiveTimeMs.get(uid); if (lastActiveTime == null) { lastActiveTime = new long[mCoreCount]; mLastUidCpuActiveTimeMs.put(uid, lastActiveTime); } final String[] timesStr = line.split(" "); if (timesStr.length != mCoreCount) { Slog.e(TAG, String.format("# readings don't match # cores, readings: %d, CPU cores: %d", timesStr.length, mCoreCount)); return; } long sumDeltas = 0; final long[] curActiveTime = new long[mCoreCount]; boolean notify = false; for (int i = 0; i < mCoreCount; i++) { // Times read will be in units of 10ms curActiveTime[i] = Long.parseLong(timesStr[i], 10) * 10; long delta = curActiveTime[i] - lastActiveTime[i]; if (delta < 0 || curActiveTime[i] < 0) { if (DEBUG) { final StringBuilder sb = new StringBuilder(); sb.append(String.format("Malformed cpu active time for UID=%d\n", uid)); sb.append(String.format("data=(%d,%d)\n", lastActiveTime[i], curActiveTime[i])); sb.append("times=("); TimeUtils.formatDuration(mLastTimeReadMs, sb); sb.append(","); TimeUtils.formatDuration(mNowTimeMs, sb); sb.append(")"); Slog.e(TAG, sb.toString()); // Slog.i(TAG, "Read uids: " + numUids); } return; mLastTimeReadMs = SystemClock.elapsedRealtime(); } notify |= delta > 0; sumDeltas += delta / (i + 1); public void setThrottleInterval(long throttleInterval) { if (throttleInterval >= 0) { mThrottleInterval = throttleInterval; } if (notify) { System.arraycopy(curActiveTime, 0, lastActiveTime, 0, mCoreCount); if (cb != null) { cb.onUidCpuActiveTime(uid, sumDeltas); } public void removeUid(int uid) { mLastUidCpuActiveTimeMs.delete(uid); } public void removeUidsInRange(int startUid, int endUid) { if (endUid < startUid) { Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid); return; } mLastUidCpuActiveTimeMs.put(startUid, null); mLastUidCpuActiveTimeMs.put(endUid, null); final int firstIndex = mLastUidCpuActiveTimeMs.indexOfKey(startUid); final int lastIndex = mLastUidCpuActiveTimeMs.indexOfKey(endUid); mLastUidCpuActiveTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1); } }
core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java +156 −112 File changed.Preview size limit exceeded, changes collapsed. Show changes