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

Commit a7a4cccf authored by Adam Lesinski's avatar Adam Lesinski
Browse files

BatteryStats: Record cpu power usage from /proc/uid_cputime

For now we are just recording the power usage and not using it
to calculate battery power usage or app blame. If it looks like
it is accurate, we'll adopt the values from the kernel instead of
estimating ourselves.

Bug:21498425
Change-Id: I6617e3c0ff279a65f4ff84472082f36fe4beb336
parent daee1e5b
Loading
Loading
Loading
Loading
+24 −2
Original line number Diff line number Diff line
@@ -184,6 +184,7 @@ public abstract class BatteryStats implements Parcelable {
    private static final String UID_DATA = "uid";
    private static final String APK_DATA = "apk";
    private static final String PROCESS_DATA = "pr";
    private static final String CPU_DATA = "cpu";
    private static final String SENSOR_DATA = "sr";
    private static final String VIBRATOR_DATA = "vib";
    private static final String FOREGROUND_DATA = "fg";
@@ -456,9 +457,14 @@ public abstract class BatteryStats implements Parcelable {
         */
        public abstract long getSystemCpuTimeUs(int which);

        /**
         * Get the total cpu power consumed (in milli-ampere-microseconds).
         */
        public abstract long getCpuPowerMaUs(int which);

        /**
         * Returns the approximate cpu time (in milliseconds) spent at a certain CPU speed.
         * @param speedStep the index of the CPU speed. This is not the actual speed of the CPU.
         * @param step the index of the CPU speed. This is not the actual speed of the CPU.
         * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
         * @see BatteryStats#getCpuSpeedSteps()
         */
@@ -2905,6 +2911,14 @@ public abstract class BatteryStats implements Parcelable {
                dumpLine(pw, uid, category, STATE_TIME_DATA, stateTimes);
            }

            final long userCpuTimeUs = u.getUserCpuTimeUs(which);
            final long systemCpuTimeUs = u.getSystemCpuTimeUs(which);
            final long powerCpuMaUs = u.getCpuPowerMaUs(which);
            if (userCpuTimeUs > 0 || systemCpuTimeUs > 0 || powerCpuMaUs > 0) {
                dumpLine(pw, uid, category, CPU_DATA, userCpuTimeUs / 1000, systemCpuTimeUs / 1000,
                        powerCpuMaUs / 1000);
            }

            final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats
                    = u.getProcessStats();
            for (int ipr=processStats.size()-1; ipr>=0; ipr--) {
@@ -2970,6 +2984,10 @@ public abstract class BatteryStats implements Parcelable {
        printer.print(BatteryStatsHelper.makemAh(power));
    }

    private void printmAh(StringBuilder sb, double power) {
        sb.append(BatteryStatsHelper.makemAh(power));
    }

    /**
     * Temporary for settings.
     */
@@ -4028,13 +4046,17 @@ public abstract class BatteryStats implements Parcelable {

            final long userCpuTimeUs = u.getUserCpuTimeUs(which);
            final long systemCpuTimeUs = u.getSystemCpuTimeUs(which);
            if (userCpuTimeUs > 0 || systemCpuTimeUs > 0) {
            final long powerCpuMaUs = u.getCpuPowerMaUs(which);
            if (userCpuTimeUs > 0 || systemCpuTimeUs > 0 || powerCpuMaUs > 0) {
                sb.setLength(0);
                sb.append(prefix);
                sb.append("    Total cpu time: u=");
                formatTimeMs(sb, userCpuTimeUs / 1000);
                sb.append("s=");
                formatTimeMs(sb, systemCpuTimeUs / 1000);
                sb.append("p=");
                printmAh(sb, powerCpuMaUs / (1000.0 * 1000.0 * 60.0 * 60.0));
                sb.append("mAh");
                pw.println(sb.toString());
            }

+18 −3
Original line number Diff line number Diff line
@@ -106,7 +106,7 @@ public final class BatteryStatsImpl extends BatteryStats {
    private static final int MAGIC = 0xBA757475; // 'BATSTATS'

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

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

        LongSamplingCounter mUserCpuTime = new LongSamplingCounter(mOnBatteryTimeBase);
        LongSamplingCounter mSystemCpuTime = new LongSamplingCounter(mOnBatteryTimeBase);
        LongSamplingCounter mCpuPower = new LongSamplingCounter(mOnBatteryTimeBase);
        LongSamplingCounter[] mSpeedBins;

        /**
@@ -4977,6 +4978,11 @@ public final class BatteryStatsImpl extends BatteryStats {
            return mSystemCpuTime.getCountLocked(which);
        }

        @Override
        public long getCpuPowerMaUs(int which) {
            return mCpuPower.getCountLocked(which);
        }

        @Override
        public long getTimeAtCpuSpeed(int step, int which) {
            if (step >= 0 && step < mSpeedBins.length) {
@@ -5097,6 +5103,7 @@ public final class BatteryStatsImpl extends BatteryStats {

            mUserCpuTime.reset(false);
            mSystemCpuTime.reset(false);
            mCpuPower.reset(false);
            for (int i = 0; i < mSpeedBins.length; i++) {
                LongSamplingCounter c = mSpeedBins[i];
                if (c != null) {
@@ -5248,6 +5255,7 @@ public final class BatteryStatsImpl extends BatteryStats {

                mUserCpuTime.detach();
                mSystemCpuTime.detach();
                mCpuPower.detach();
                for (int i = 0; i < mSpeedBins.length; i++) {
                    LongSamplingCounter c = mSpeedBins[i];
                    if (c != null) {
@@ -5427,6 +5435,7 @@ public final class BatteryStatsImpl extends BatteryStats {

            mUserCpuTime.writeToParcel(out);
            mSystemCpuTime.writeToParcel(out);
            mCpuPower.writeToParcel(out);

            out.writeInt(mSpeedBins.length);
            for (int i = 0; i < mSpeedBins.length; i++) {
@@ -5618,6 +5627,7 @@ public final class BatteryStatsImpl extends BatteryStats {

            mUserCpuTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
            mSystemCpuTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
            mCpuPower = new LongSamplingCounter(mOnBatteryTimeBase, in);

            int bins = in.readInt();
            int steps = getCpuSpeedSteps();
@@ -7964,7 +7974,8 @@ public final class BatteryStatsImpl extends BatteryStats {
        mKernelUidCpuTimeReader.readDelta(!mOnBatteryInternal ? null :
                new KernelUidCpuTimeReader.Callback() {
                    @Override
                    public void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs) {
                    public void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs,
                                             long powerMaUs) {
                        final Uid u = getUidStatsLocked(mapUid(uid));

                        // Accumulate the total system and user time.
@@ -7978,7 +7989,7 @@ public final class BatteryStatsImpl extends BatteryStats {
                            TimeUtils.formatDuration(userTimeUs / 1000, sb);
                            sb.append(" s=");
                            TimeUtils.formatDuration(systemTimeUs / 1000, sb);
                            sb.append("\n");
                            sb.append(" p=").append(powerMaUs / 1000).append("mAms\n");
                        }

                        if (numWakelocksF > 0) {
@@ -7994,11 +8005,13 @@ public final class BatteryStatsImpl extends BatteryStats {
                            TimeUtils.formatDuration(userTimeUs / 1000, sb);
                            sb.append(" s=");
                            TimeUtils.formatDuration(systemTimeUs / 1000, sb);
                            sb.append(" p=").append(powerMaUs / 1000).append("mAms");
                            Slog.d(TAG, sb.toString());
                        }

                        u.mUserCpuTime.addCountLocked(userTimeUs);
                        u.mSystemCpuTime.addCountLocked(systemTimeUs);
                        u.mCpuPower.addCountLocked(powerMaUs);

                        // Add the cpu speeds to this UID. These are used as a ratio
                        // for computing the power this UID used.
@@ -9229,6 +9242,7 @@ public final class BatteryStatsImpl extends BatteryStats {

            u.mUserCpuTime.readSummaryFromParcelLocked(in);
            u.mSystemCpuTime.readSummaryFromParcelLocked(in);
            u.mCpuPower.readSummaryFromParcelLocked(in);

            int NSB = in.readInt();
            if (NSB > 100) {
@@ -9575,6 +9589,7 @@ public final class BatteryStatsImpl extends BatteryStats {

            u.mUserCpuTime.writeSummaryFromParcelLocked(out);
            u.mSystemCpuTime.writeSummaryFromParcelLocked(out);
            u.mCpuPower.writeSummaryFromParcelLocked(out);

            out.writeInt(u.mSpeedBins.length);
            for (int i = 0; i < u.mSpeedBins.length; i++) {
+31 −14
Original line number Diff line number Diff line
@@ -30,7 +30,7 @@ import java.io.IOException;
/**
 * Reads /proc/uid_cputime/show_uid_stat which has the line format:
 *
 * uid: user_time_micro_seconds system_time_micro_seconds
 * uid: user_time_micro_seconds system_time_micro_seconds power_in_milli-amp-micro_seconds
 *
 * This provides the time a UID's processes spent executing in user-space and kernel-space.
 * The file contains a monotonically increasing count of time for a single boot. This class
@@ -46,11 +46,18 @@ public class KernelUidCpuTimeReader {
     * Callback interface for processing each line of the proc file.
     */
    public interface Callback {
        void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs);
        /**
         * @param uid UID of the app
         * @param userTimeUs time spent executing in user space in microseconds
         * @param systemTimeUs time spent executing in kernel space in microseconds
         * @param powerMaUs power consumed executing, in milli-ampere microseconds
         */
        void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs, long powerMaUs);
    }

    private SparseLongArray mLastUserTimeUs = new SparseLongArray();
    private SparseLongArray mLastSystemTimeUs = new SparseLongArray();
    private SparseLongArray mLastPowerMaUs = new SparseLongArray();
    private long mLastTimeReadUs = 0;

    /**
@@ -70,50 +77,60 @@ public class KernelUidCpuTimeReader {
                final int uid = Integer.parseInt(uidStr.substring(0, uidStr.length() - 1), 10);
                final long userTimeUs = Long.parseLong(splitter.next(), 10);
                final long systemTimeUs = Long.parseLong(splitter.next(), 10);
                final long powerMaUs = Long.parseLong(splitter.next(), 10) / 1000;

                if (callback != null) {
                    long userTimeDeltaUs = userTimeUs;
                    long systemTimeDeltaUs = systemTimeUs;
                    long powerDeltaMaUs = powerMaUs;
                    int index = mLastUserTimeUs.indexOfKey(uid);
                    if (index >= 0) {
                        userTimeDeltaUs -= mLastUserTimeUs.valueAt(index);
                        systemTimeDeltaUs -= mLastSystemTimeUs.valueAt(index);
                        powerDeltaMaUs -= mLastPowerMaUs.valueAt(index);

                        final long timeDiffUs = nowUs - mLastTimeReadUs;
                        if (userTimeDeltaUs < 0 || systemTimeDeltaUs < 0 ||
                        if (userTimeDeltaUs < 0 || systemTimeDeltaUs < 0 || powerDeltaMaUs < 0 ||
                                userTimeDeltaUs > timeDiffUs || systemTimeDeltaUs > timeDiffUs) {
                            StringBuilder sb = new StringBuilder("Malformed cpu data!\n");
                            StringBuilder sb = new StringBuilder("Malformed cpu data for UID=");
                            sb.append(uid).append("!\n");
                            sb.append("Time between reads: ");
                            TimeUtils.formatDuration(timeDiffUs / 1000, sb);
                            sb.append("ms\n");
                            sb.append("\n");
                            sb.append("Previous times: u=");
                            TimeUtils.formatDuration(mLastUserTimeUs.valueAt(index) / 1000, sb);
                            sb.append("ms s=");
                            sb.append(" s=");
                            TimeUtils.formatDuration(mLastSystemTimeUs.valueAt(index) / 1000, sb);
                            sb.append("ms\n");
                            sb.append(" p=").append(mLastPowerMaUs.valueAt(index) / 1000);
                            sb.append("mAms\n");

                            sb.append("Current times: u=");
                            TimeUtils.formatDuration(userTimeUs / 1000, sb);
                            sb.append("ms s=");
                            sb.append(" s=");
                            TimeUtils.formatDuration(systemTimeUs / 1000, sb);
                            sb.append("ms\n");
                            sb.append("Delta for UID=").append(uid).append(": u=");
                            sb.append(" p=").append(powerMaUs / 1000);
                            sb.append("mAms\n");
                            sb.append("Delta: u=");
                            TimeUtils.formatDuration(userTimeDeltaUs / 1000, sb);
                            sb.append("ms s=");
                            sb.append(" s=");
                            TimeUtils.formatDuration(systemTimeDeltaUs / 1000, sb);
                            sb.append("ms");
                            sb.append(" p=").append(powerDeltaMaUs / 1000).append("mAms");
                            Slog.wtf(TAG, sb.toString());

                            userTimeDeltaUs = 0;
                            systemTimeDeltaUs = 0;
                            powerDeltaMaUs = 0;
                        }
                    }

                    if (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0) {
                        callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs);
                    if (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0 || powerDeltaMaUs != 0) {
                        callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs,
                                powerDeltaMaUs);
                    }
                }
                mLastUserTimeUs.put(uid, userTimeUs);
                mLastSystemTimeUs.put(uid, systemTimeUs);
                mLastPowerMaUs.put(uid, powerMaUs);
            }
        } catch (IOException e) {
            Slog.e(TAG, "Failed to read uid_cputime", e);