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

Commit af85741e authored by Sudheer Shanka's avatar Sudheer Shanka
Browse files

Track per-cluster times of each uid in microsec.

Bug: 62805443
Test: Used some apps and verified that battery usage is attributed to
      these apps in Settings>Battery.

Change-Id: I0e604fdc0d1704d7d061b6a711a0017b82a39b8f
parent 584f10e3
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -686,7 +686,7 @@ public abstract class BatteryStats implements Parcelable {
        public abstract long getSystemCpuTimeUs(int which);

        /**
         * Returns the approximate cpu time (in milliseconds) spent at a certain CPU speed for a
         * Returns the approximate cpu time (in microseconds) spent at a certain CPU speed for a
         * given CPU cluster.
         * @param cluster the index of the CPU cluster.
         * @param step the index of the CPU speed. This is not the actual speed of the CPU.
+50 −47
Original line number Diff line number Diff line
@@ -119,7 +119,7 @@ public class BatteryStatsImpl extends BatteryStats {
    private static final int MAGIC = 0xBA757475; // 'BATSTATS'

    // Current on-disk Parcel version
    private static final int VERSION = 162 + (USE_OLD_HISTORY ? 1000 : 0);
    private static final int VERSION = 163 + (USE_OLD_HISTORY ? 1000 : 0);

    // Maximum number of items we will record in the history.
    private static final int MAX_HISTORY_ITEMS;
@@ -5702,7 +5702,7 @@ public class BatteryStatsImpl extends BatteryStats {

        LongSamplingCounter mUserCpuTime;
        LongSamplingCounter mSystemCpuTime;
        LongSamplingCounter[][] mCpuClusterSpeed;
        LongSamplingCounter[][] mCpuClusterSpeedTimesUs;

        LongSamplingCounterArray mCpuFreqTimeMs;
        LongSamplingCounterArray mScreenOffCpuFreqTimeMs;
@@ -6554,12 +6554,12 @@ public class BatteryStatsImpl extends BatteryStats {

        @Override
        public long getTimeAtCpuSpeed(int cluster, int step, int which) {
            if (mCpuClusterSpeed != null) {
                if (cluster >= 0 && cluster < mCpuClusterSpeed.length) {
                    final LongSamplingCounter[] cpuSpeeds = mCpuClusterSpeed[cluster];
                    if (cpuSpeeds != null) {
                        if (step >= 0 && step < cpuSpeeds.length) {
                            final LongSamplingCounter c = cpuSpeeds[step];
            if (mCpuClusterSpeedTimesUs != null) {
                if (cluster >= 0 && cluster < mCpuClusterSpeedTimesUs.length) {
                    final LongSamplingCounter[] cpuSpeedTimesUs = mCpuClusterSpeedTimesUs[cluster];
                    if (cpuSpeedTimesUs != null) {
                        if (step >= 0 && step < cpuSpeedTimesUs.length) {
                            final LongSamplingCounter c = cpuSpeedTimesUs[step];
                            if (c != null) {
                                return c.getCountLocked(which);
                            }
@@ -6710,8 +6710,8 @@ public class BatteryStatsImpl extends BatteryStats {
            mUserCpuTime.reset(false);
            mSystemCpuTime.reset(false);

            if (mCpuClusterSpeed != null) {
                for (LongSamplingCounter[] speeds : mCpuClusterSpeed) {
            if (mCpuClusterSpeedTimesUs != null) {
                for (LongSamplingCounter[] speeds : mCpuClusterSpeedTimesUs) {
                    if (speeds != null) {
                        for (LongSamplingCounter speed : speeds) {
                            if (speed != null) {
@@ -6900,8 +6900,8 @@ public class BatteryStatsImpl extends BatteryStats {
                mUserCpuTime.detach();
                mSystemCpuTime.detach();

                if (mCpuClusterSpeed != null) {
                    for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeed) {
                if (mCpuClusterSpeedTimesUs != null) {
                    for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeedTimesUs) {
                        if (cpuSpeeds != null) {
                            for (LongSamplingCounter c : cpuSpeeds) {
                                if (c != null) {
@@ -7154,10 +7154,10 @@ public class BatteryStatsImpl extends BatteryStats {
            mUserCpuTime.writeToParcel(out);
            mSystemCpuTime.writeToParcel(out);

            if (mCpuClusterSpeed != null) {
            if (mCpuClusterSpeedTimesUs != null) {
                out.writeInt(1);
                out.writeInt(mCpuClusterSpeed.length);
                for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeed) {
                out.writeInt(mCpuClusterSpeedTimesUs.length);
                for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeedTimesUs) {
                    if (cpuSpeeds != null) {
                        out.writeInt(1);
                        out.writeInt(cpuSpeeds.length);
@@ -7451,7 +7451,7 @@ public class BatteryStatsImpl extends BatteryStats {
                    throw new ParcelFormatException("Incompatible number of cpu clusters");
                }

                mCpuClusterSpeed = new LongSamplingCounter[numCpuClusters][];
                mCpuClusterSpeedTimesUs = new LongSamplingCounter[numCpuClusters][];
                for (int cluster = 0; cluster < numCpuClusters; cluster++) {
                    if (in.readInt() != 0) {
                        int numSpeeds = in.readInt();
@@ -7461,18 +7461,19 @@ public class BatteryStatsImpl extends BatteryStats {
                        }

                        final LongSamplingCounter[] cpuSpeeds = new LongSamplingCounter[numSpeeds];
                        mCpuClusterSpeed[cluster] = cpuSpeeds;
                        mCpuClusterSpeedTimesUs[cluster] = cpuSpeeds;
                        for (int speed = 0; speed < numSpeeds; speed++) {
                            if (in.readInt() != 0) {
                                cpuSpeeds[speed] = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
                                cpuSpeeds[speed] = new LongSamplingCounter(
                                        mBsi.mOnBatteryTimeBase, in);
                            }
                        }
                    } else {
                        mCpuClusterSpeed[cluster] = null;
                        mCpuClusterSpeedTimesUs[cluster] = null;
                    }
                }
            } else {
                mCpuClusterSpeed = null;
                mCpuClusterSpeedTimesUs = null;
            }

            mCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(in, mBsi.mOnBatteryTimeBase);
@@ -10427,46 +10428,48 @@ public class BatteryStatsImpl extends BatteryStats {
            }
        }

        long totalCpuClustersTime = 0;
        long totalCpuClustersTimeMs = 0;
        // Read the time spent for each cluster at various cpu frequencies.
        final long[][] clusterSpeeds = new long[mKernelCpuSpeedReaders.length][];
        final long[][] clusterSpeedTimesMs = new long[mKernelCpuSpeedReaders.length][];
        for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
            clusterSpeeds[cluster] = mKernelCpuSpeedReaders[cluster].readDelta();
            if (clusterSpeeds[cluster] != null) {
                for (int speed = clusterSpeeds[cluster].length - 1; speed >= 0; --speed) {
                    totalCpuClustersTime += clusterSpeeds[cluster][speed];
            clusterSpeedTimesMs[cluster] = mKernelCpuSpeedReaders[cluster].readDelta();
            if (clusterSpeedTimesMs[cluster] != null) {
                for (int speed = clusterSpeedTimesMs[cluster].length - 1; speed >= 0; --speed) {
                    totalCpuClustersTimeMs += clusterSpeedTimesMs[cluster][speed];
                }
            }
        }
        if (totalCpuClustersTime != 0) {
        if (totalCpuClustersTimeMs != 0) {
            // We have cpu times per freq aggregated over all uids but we need the times per uid.
            // So, we distribute total time spent by an uid to different cpu freqs based on the
            // amount of time cpu was running at that freq.
            final int updatedUidsCount = updatedUids.size();
            for (int i = 0; i < updatedUidsCount; ++i) {
                final Uid u = getUidStatsLocked(updatedUids.keyAt(i));
                final long appCpuTimeMs = updatedUids.valueAt(i) / 1000;
                final long appCpuTimeUs = updatedUids.valueAt(i);
                // Add the cpu speeds to this UID.
                final int numClusters = mPowerProfile.getNumCpuClusters();
                if (u.mCpuClusterSpeed == null || u.mCpuClusterSpeed.length !=
                if (u.mCpuClusterSpeedTimesUs == null || u.mCpuClusterSpeedTimesUs.length !=
                        numClusters) {
                    u.mCpuClusterSpeed = new LongSamplingCounter[numClusters][];
                    u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[numClusters][];
                }

                for (int cluster = 0; cluster < clusterSpeeds.length; cluster++) {
                    final int speedsInCluster = clusterSpeeds[cluster].length;
                    if (u.mCpuClusterSpeed[cluster] == null || speedsInCluster !=
                            u.mCpuClusterSpeed[cluster].length) {
                        u.mCpuClusterSpeed[cluster] = new LongSamplingCounter[speedsInCluster];
                for (int cluster = 0; cluster < clusterSpeedTimesMs.length; cluster++) {
                    final int speedsInCluster = clusterSpeedTimesMs[cluster].length;
                    if (u.mCpuClusterSpeedTimesUs[cluster] == null || speedsInCluster !=
                            u.mCpuClusterSpeedTimesUs[cluster].length) {
                        u.mCpuClusterSpeedTimesUs[cluster]
                                = new LongSamplingCounter[speedsInCluster];
                    }

                    final LongSamplingCounter[] cpuSpeeds = u.mCpuClusterSpeed[cluster];
                    final LongSamplingCounter[] cpuSpeeds = u.mCpuClusterSpeedTimesUs[cluster];
                    for (int speed = 0; speed < speedsInCluster; speed++) {
                        if (cpuSpeeds[speed] == null) {
                            cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase);
                        }
                        cpuSpeeds[speed].addCountLocked(appCpuTimeMs * clusterSpeeds[cluster][speed]
                                / totalCpuClustersTime);
                        cpuSpeeds[speed].addCountLocked(appCpuTimeUs
                                * clusterSpeedTimesMs[cluster][speed]
                                / totalCpuClustersTimeMs);
                    }
                }
            }
@@ -11750,7 +11753,7 @@ public class BatteryStatsImpl extends BatteryStats {
                    throw new ParcelFormatException("Incompatible cpu cluster arrangement");
                }

                u.mCpuClusterSpeed = new LongSamplingCounter[numClusters][];
                u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[numClusters][];
                for (int cluster = 0; cluster < numClusters; cluster++) {
                    if (in.readInt() != 0) {
                        final int NSB = in.readInt();
@@ -11760,20 +11763,20 @@ public class BatteryStatsImpl extends BatteryStats {
                                    NSB);
                        }

                        u.mCpuClusterSpeed[cluster] = new LongSamplingCounter[NSB];
                        u.mCpuClusterSpeedTimesUs[cluster] = new LongSamplingCounter[NSB];
                        for (int speed = 0; speed < NSB; speed++) {
                            if (in.readInt() != 0) {
                                u.mCpuClusterSpeed[cluster][speed] = new LongSamplingCounter(
                                u.mCpuClusterSpeedTimesUs[cluster][speed] = new LongSamplingCounter(
                                        mOnBatteryTimeBase);
                                u.mCpuClusterSpeed[cluster][speed].readSummaryFromParcelLocked(in);
                                u.mCpuClusterSpeedTimesUs[cluster][speed].readSummaryFromParcelLocked(in);
                            }
                        }
                    } else {
                        u.mCpuClusterSpeed[cluster] = null;
                        u.mCpuClusterSpeedTimesUs[cluster] = null;
                    }
                }
            } else {
                u.mCpuClusterSpeed = null;
                u.mCpuClusterSpeedTimesUs = null;
            }

            u.mCpuFreqTimeMs = LongSamplingCounterArray.readSummaryFromParcelLocked(
@@ -12181,10 +12184,10 @@ public class BatteryStatsImpl extends BatteryStats {
            u.mUserCpuTime.writeSummaryFromParcelLocked(out);
            u.mSystemCpuTime.writeSummaryFromParcelLocked(out);

            if (u.mCpuClusterSpeed != null) {
            if (u.mCpuClusterSpeedTimesUs != null) {
                out.writeInt(1);
                out.writeInt(u.mCpuClusterSpeed.length);
                for (LongSamplingCounter[] cpuSpeeds : u.mCpuClusterSpeed) {
                out.writeInt(u.mCpuClusterSpeedTimesUs.length);
                for (LongSamplingCounter[] cpuSpeeds : u.mCpuClusterSpeedTimesUs) {
                    if (cpuSpeeds != null) {
                        out.writeInt(1);
                        out.writeInt(cpuSpeeds.length);
+8 −6
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.util.Log;
public class CpuPowerCalculator extends PowerCalculator {
    private static final String TAG = "CpuPowerCalculator";
    private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
    private static final long MICROSEC_IN_HR = (long) 60 * 60 * 1000 * 1000;
    private final PowerProfile mProfile;

    public CpuPowerCalculator(PowerProfile profile) {
@@ -35,21 +36,22 @@ public class CpuPowerCalculator extends PowerCalculator {
        app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
        final int numClusters = mProfile.getNumCpuClusters();

        double cpuPowerMaMs = 0;
        double cpuPowerMaUs = 0;
        for (int cluster = 0; cluster < numClusters; cluster++) {
            final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster);
            for (int speed = 0; speed < speedsForCluster; speed++) {
                final double cpuSpeedStepPower = u.getTimeAtCpuSpeed(cluster, speed, statsType) *
                final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType);
                final double cpuSpeedStepPower = timeUs *
                        mProfile.getAveragePowerForCpu(cluster, speed);
                if (DEBUG) {
                    Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
                            + speed + " power="
                            + BatteryStatsHelper.makemAh(cpuSpeedStepPower / (60 * 60 * 1000)));
                            + speed + " timeUs=" + timeUs + " power="
                            + BatteryStatsHelper.makemAh(cpuSpeedStepPower / MICROSEC_IN_HR));
                }
                cpuPowerMaMs += cpuSpeedStepPower;
                cpuPowerMaUs += cpuSpeedStepPower;
            }
        }
        app.cpuPowerMah = cpuPowerMaMs / (60 * 60 * 1000);
        app.cpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR;

        if (DEBUG && (app.cpuTimeMs != 0 || app.cpuPowerMah != 0)) {
            Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + app.cpuTimeMs + " ms power="
+12 −12
Original line number Diff line number Diff line
@@ -39,8 +39,8 @@ public class KernelCpuSpeedReader {
    private static final String TAG = "KernelCpuSpeedReader";

    private final String mProcFile;
    private final long[] mLastSpeedTimes;
    private final long[] mDeltaSpeedTimes;
    private final long[] mLastSpeedTimesMs;
    private final long[] mDeltaSpeedTimesMs;

    // How long a CPU jiffy is in milliseconds.
    private final long mJiffyMillis;
@@ -51,8 +51,8 @@ public class KernelCpuSpeedReader {
    public KernelCpuSpeedReader(int cpuNumber, int numSpeedSteps) {
        mProcFile = String.format("/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state",
                cpuNumber);
        mLastSpeedTimes = new long[numSpeedSteps];
        mDeltaSpeedTimes = new long[numSpeedSteps];
        mLastSpeedTimesMs = new long[numSpeedSteps];
        mDeltaSpeedTimesMs = new long[numSpeedSteps];
        long jiffyHz = Libcore.os.sysconf(OsConstants._SC_CLK_TCK);
        mJiffyMillis = 1000/jiffyHz;
    }
@@ -68,27 +68,27 @@ public class KernelCpuSpeedReader {
            TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
            String line;
            int speedIndex = 0;
            while (speedIndex < mLastSpeedTimes.length && (line = reader.readLine()) != null) {
            while (speedIndex < mLastSpeedTimesMs.length && (line = reader.readLine()) != null) {
                splitter.setString(line);
                Long.parseLong(splitter.next());
                splitter.next();

                long time = Long.parseLong(splitter.next()) * mJiffyMillis;
                if (time < mLastSpeedTimes[speedIndex]) {
                if (time < mLastSpeedTimesMs[speedIndex]) {
                    // The stats reset when the cpu hotplugged. That means that the time
                    // we read is offset from 0, so the time is the delta.
                    mDeltaSpeedTimes[speedIndex] = time;
                    mDeltaSpeedTimesMs[speedIndex] = time;
                } else {
                    mDeltaSpeedTimes[speedIndex] = time - mLastSpeedTimes[speedIndex];
                    mDeltaSpeedTimesMs[speedIndex] = time - mLastSpeedTimesMs[speedIndex];
                }
                mLastSpeedTimes[speedIndex] = time;
                mLastSpeedTimesMs[speedIndex] = time;
                speedIndex++;
            }
        } catch (IOException e) {
            Slog.e(TAG, "Failed to read cpu-freq: " + e.getMessage());
            Arrays.fill(mDeltaSpeedTimes, 0);
            Arrays.fill(mDeltaSpeedTimesMs, 0);
        } finally {
            StrictMode.setThreadPolicy(policy);
        }
        return mDeltaSpeedTimes;
        return mDeltaSpeedTimesMs;
    }
}