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

Commit 2ab01442 authored by Mike Ma's avatar Mike Ma
Browse files

Binary Cpu Time Proc File Reader

Kernel now exposes cpu time proc files in binary format to save cpu
cycles. New proc files are:
Per uid freq time: /proc/uid_cpupower/time_in_state
Per uid active time: /proc/uid_cpupower/concurrent_active_time
Per uid cluster time: /proc/uid_cpupower/concurrent_policy_time
(See PowerProfile.java on how these data are used)
KernelUidCpuActiveTimeReader, KernelUidCpuClusterTimeReader and
KernelUidCpuFreqTimeReader are modified to use these new data.

Meanwhile, statsd needs these data too. So the actual reading
fuctionality is delegated to singletons in KernelCpuProcReader to
accomodate concurrent access of these proc files by more than one
party. When multiple read requests hit the singleton within the
throttle interval, the singleton will return stale data.

Optimization
KernelCpuProcReader reuses the same ByteBuffer to save results.
KernelUid***Reader all reuse the same long[] array to send results
back to BatteryStatsImpl. Estimated saving of ~200 KB GC effort
per read request.

Add a flag to control throttle interval:
kernel_uid_readers_throttle_time

Bug: 72763654
Bug: 71906435
Bug: 71905885
Bug: 70517018
Fixes: 73166477
Fixes: 72172569
Test: BatteryStatsCpuTimesTest
Test: KernelUidCpuActiveTimeReaderTest
Test: KernelUidCpuClusterTimeReaderTest
Test: KernelUidCpuFreqTimeReaderTest
Test: KernelCpuProcReaderTest
Change-Id: I1012667ce3b9eb35e37882a058bd4bceccabbbe9
parent 28ff9cee
Loading
Loading
Loading
Loading
+36 −8
Original line number Original line Diff line number Diff line
@@ -110,7 +110,10 @@ import java.util.Calendar;
import java.util.HashMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Iterator;
import java.util.Map;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantLock;
@@ -12194,7 +12197,7 @@ public class BatteryStatsImpl extends BatteryStats {
    @VisibleForTesting
    @VisibleForTesting
    public void readKernelUidCpuActiveTimesLocked(boolean onBattery) {
    public void readKernelUidCpuActiveTimesLocked(boolean onBattery) {
        final long startTimeMs = mClocks.uptimeMillis();
        final long startTimeMs = mClocks.uptimeMillis();
        mKernelUidCpuActiveTimeReader.readDelta((uid, cpuActiveTimesUs) -> {
        mKernelUidCpuActiveTimeReader.readDelta((uid, cpuActiveTimesMs) -> {
            uid = mapUid(uid);
            uid = mapUid(uid);
            if (Process.isIsolated(uid)) {
            if (Process.isIsolated(uid)) {
                mKernelUidCpuActiveTimeReader.removeUid(uid);
                mKernelUidCpuActiveTimeReader.removeUid(uid);
@@ -12207,7 +12210,7 @@ public class BatteryStatsImpl extends BatteryStats {
                return;
                return;
            }
            }
            final Uid u = getUidStatsLocked(uid);
            final Uid u = getUidStatsLocked(uid);
            u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesUs, onBattery);
            u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesMs, onBattery);
        });
        });
        final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
        final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
@@ -12223,7 +12226,7 @@ public class BatteryStatsImpl extends BatteryStats {
    @VisibleForTesting
    @VisibleForTesting
    public void readKernelUidCpuClusterTimesLocked(boolean onBattery) {
    public void readKernelUidCpuClusterTimesLocked(boolean onBattery) {
        final long startTimeMs = mClocks.uptimeMillis();
        final long startTimeMs = mClocks.uptimeMillis();
        mKernelUidCpuClusterTimeReader.readDelta((uid, cpuClusterTimesUs) -> {
        mKernelUidCpuClusterTimeReader.readDelta((uid, cpuClusterTimesMs) -> {
            uid = mapUid(uid);
            uid = mapUid(uid);
            if (Process.isIsolated(uid)) {
            if (Process.isIsolated(uid)) {
                mKernelUidCpuClusterTimeReader.removeUid(uid);
                mKernelUidCpuClusterTimeReader.removeUid(uid);
@@ -12236,7 +12239,7 @@ public class BatteryStatsImpl extends BatteryStats {
                return;
                return;
            }
            }
            final Uid u = getUidStatsLocked(uid);
            final Uid u = getUidStatsLocked(uid);
            u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesUs, onBattery);
            u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesMs, onBattery);
        });
        });
        final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
        final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
@@ -13186,17 +13189,20 @@ public class BatteryStatsImpl extends BatteryStats {
                = "read_binary_cpu_time";
                = "read_binary_cpu_time";
        public static final String KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS
        public static final String KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS
                = "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_TIMES_BY_PROC_STATE = true;
        private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = 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 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_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_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE;
        public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME;
        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 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 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 ContentResolver mResolver;
        private final KeyValueListParser mParser = new KeyValueListParser(',');
        private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -13234,11 +13240,14 @@ public class BatteryStatsImpl extends BatteryStats {
                                DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE));
                                DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE));
                TRACK_CPU_ACTIVE_CLUSTER_TIME = mParser.getBoolean(
                TRACK_CPU_ACTIVE_CLUSTER_TIME = mParser.getBoolean(
                        KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME, DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME);
                        KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME, DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME);
                READ_BINARY_CPU_TIME = mParser.getBoolean(
                updateReadBinaryCpuTime(READ_BINARY_CPU_TIME,
                        KEY_READ_BINARY_CPU_TIME, DEFAULT_READ_BINARY_CPU_TIME);
                        mParser.getBoolean(KEY_READ_BINARY_CPU_TIME, DEFAULT_READ_BINARY_CPU_TIME));
                updateProcStateCpuTimesReadDelayMs(PROC_STATE_CPU_TIMES_READ_DELAY_MS,
                updateProcStateCpuTimesReadDelayMs(PROC_STATE_CPU_TIMES_READ_DELAY_MS,
                        mParser.getLong(KEY_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));
                                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));
            }
            }
        }
        }
@@ -13254,6 +13263,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) {
        private void updateProcStateCpuTimesReadDelayMs(long oldDelayMillis, long newDelayMillis) {
            PROC_STATE_CPU_TIMES_READ_DELAY_MS = newDelayMillis;
            PROC_STATE_CPU_TIMES_READ_DELAY_MS = newDelayMillis;
            if (oldDelayMillis != newDelayMillis) {
            if (oldDelayMillis != newDelayMillis) {
@@ -13263,6 +13279,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) {
        public void dumpLocked(PrintWriter pw) {
            pw.print(KEY_TRACK_CPU_TIMES_BY_PROC_STATE); pw.print("=");
            pw.print(KEY_TRACK_CPU_TIMES_BY_PROC_STATE); pw.print("=");
            pw.println(TRACK_CPU_TIMES_BY_PROC_STATE);
            pw.println(TRACK_CPU_TIMES_BY_PROC_STATE);
@@ -13272,6 +13298,8 @@ public class BatteryStatsImpl extends BatteryStats {
            pw.println(READ_BINARY_CPU_TIME);
            pw.println(READ_BINARY_CPU_TIME);
            pw.print(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS); pw.print("=");
            pw.print(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS); pw.print("=");
            pw.println(PROC_STATE_CPU_TIMES_READ_DELAY_MS);
            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);
        }
        }
    }
    }
+3 −2
Original line number Original line Diff line number Diff line
@@ -50,13 +50,14 @@ public class CpuPowerCalculator extends PowerCalculator {
                cpuPowerMaUs += cpuSpeedStepPower;
                cpuPowerMaUs += cpuSpeedStepPower;
            }
            }
        }
        }
        cpuPowerMaUs += u.getCpuActiveTime() * mProfile.getAveragePower(
        cpuPowerMaUs += u.getCpuActiveTime() * 1000 * mProfile.getAveragePower(
                PowerProfile.POWER_CPU_ACTIVE);
                PowerProfile.POWER_CPU_ACTIVE);
        long[] cpuClusterTimes = u.getCpuClusterTimes();
        long[] cpuClusterTimes = u.getCpuClusterTimes();
        if (cpuClusterTimes != null) {
        if (cpuClusterTimes != null) {
            if (cpuClusterTimes.length == numClusters) {
            if (cpuClusterTimes.length == numClusters) {
                for (int i = 0; i < numClusters; i++) {
                for (int i = 0; i < numClusters; i++) {
                    double power = cpuClusterTimes[i] * mProfile.getAveragePowerForCpuCluster(i);
                    double power =
                            cpuClusterTimes[i] * 1000 * mProfile.getAveragePowerForCpuCluster(i);
                    cpuPowerMaUs += power;
                    cpuPowerMaUs += power;
                    if (DEBUG) {
                    if (DEBUG) {
                        Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + i + " clusterTimeUs="
                        Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + i + " clusterTimeUs="
+159 −0
Original line number Original line 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;
    }
}
+93 −86
Original line number Original line Diff line number Diff line
@@ -17,130 +17,137 @@
package com.android.internal.os;
package com.android.internal.os;


import android.annotation.Nullable;
import android.annotation.Nullable;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemClock;
import android.util.Slog;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseArray;
import android.util.TimeUtils;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;


import java.io.BufferedReader;
import java.nio.ByteBuffer;
import java.io.FileReader;
import java.nio.IntBuffer;
import java.io.IOException;


/**
/**
 * Reads /proc/uid_concurrent_active_time which has the format:
 * Reads binary proc file /proc/uid_cpupower/concurrent_active_time and reports CPU active time to
 * active: X (X is # cores)
 * BatteryStats to compute {@link PowerProfile#POWER_CPU_ACTIVE}.
 * [uid0]: [time-0] [time-1] [time-2] ... (# entries = # cores)
 *
 * [uid1]: [time-0] [time-1] [time-2] ... ...
 * 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
 * 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
 * maintains the previous results of a call to {@link #readDelta} in order to provide a
 * proper delta.
 * 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 {
public class KernelUidCpuActiveTimeReader {
    private static final boolean DEBUG = false;
    private static final String TAG = "KernelUidCpuActiveTimeReader";
    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 final KernelCpuProcReader mProcReader;
    private long mLastTimeReadMs;
    private long mLastTimeReadMs = Long.MIN_VALUE;
    private long mNowTimeMs;
    private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL;
    private SparseArray<long[]> mLastUidCpuActiveTimeMs = new SparseArray<>();
    private SparseArray<Double> mLastUidCpuActiveTimeMs = new SparseArray<>();


    public interface Callback {
    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);
        void onUidCpuActiveTime(int uid, long cpuActiveTimeMs);
    }
    }


    public void readDelta(@Nullable Callback cb) {
    public KernelUidCpuActiveTimeReader() {
        final int oldMask = StrictMode.allowThreadDiskReadsMask();
        mProcReader = KernelCpuProcReader.getActiveTimeReaderInstance();
        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 void removeUid(int uid) {
    @VisibleForTesting
        mLastUidCpuActiveTimeMs.delete(uid);
    public KernelUidCpuActiveTimeReader(KernelCpuProcReader procReader) {
        mProcReader = procReader;
    }
    }


    public void removeUidsInRange(int startUid, int endUid) {
    public void readDelta(@Nullable Callback cb) {
        if (endUid < startUid) {
        if (SystemClock.elapsedRealtime() < mLastTimeReadMs + mThrottleInterval) {
            Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
            Slog.w(TAG, "Throttle");
            return;
            return;
        }
        }
        mLastUidCpuActiveTimeMs.put(startUid, null);
        synchronized (mProcReader) {
        mLastUidCpuActiveTimeMs.put(endUid, null);
            final ByteBuffer bytes = mProcReader.readBytes();
        final int firstIndex = mLastUidCpuActiveTimeMs.indexOfKey(startUid);
            if (bytes == null || bytes.remaining() <= 4) {
        final int lastIndex = mLastUidCpuActiveTimeMs.indexOfKey(endUid);
                // Error already logged in mProcReader.
        mLastUidCpuActiveTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
                return;
            }
            }

            if ((bytes.remaining() & 3) != 0) {
    @VisibleForTesting
                Slog.wtf(TAG,
    public void readDeltaInternal(BufferedReader reader, @Nullable Callback cb) throws IOException {
                        "Cannot parse active time proc bytes to int: " + bytes.remaining());
        String line = reader.readLine();
        if (line == null || !line.startsWith("active:")) {
            Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE));
                return;
                return;
            }
            }
        if (mCoreCount == 0) {
            final IntBuffer buf = bytes.asIntBuffer();
            mCoreCount = Integer.parseInt(line.substring(line.indexOf(' ')+1));
            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) {
            int numUids = buf.remaining() / (cores + 1);
            final int index = line.indexOf(' ');
            for (int i = 0; i < numUids; i++) {
            final int uid = Integer.parseInt(line.substring(0, index - 1), 10);
                int uid = buf.get();
            readTimesForUid(uid, line.substring(index + 1), cb);
                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;
            // Slog.i(TAG, "Read uids: " + numUids);
        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());
        }
        }
                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);
    }
    }
}
}
+156 −112

File changed.

Preview size limit exceeded, changes collapsed.

Loading