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

Commit 3a226059 authored by James Carr's avatar James Carr
Browse files

Addition of memory power to BatteryStats

Initial implementation of some classes that pipe memory power use
information from the kernel to BatteryStats framework.  Reads how much
time has been spent in distinct bandwidth buckets.

Change-Id: Iefb4b4c0a4e0d0f8d7a773075324ebd38ed154f2
parent b720bc30
Loading
Loading
Loading
Loading
+95 −1
Original line number Diff line number Diff line
@@ -49,6 +49,8 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.LogWriter;
import android.util.LongSparseArray;
import android.util.LongSparseLongArray;
import android.util.MutableInt;
import android.util.PrintWriterPrinter;
import android.util.Printer;
@@ -99,6 +101,7 @@ public class BatteryStatsImpl extends BatteryStats {
    private static final boolean DEBUG = false;
    public static final boolean DEBUG_ENERGY = false;
    private static final boolean DEBUG_ENERGY_CPU = DEBUG_ENERGY;
    private static final boolean DEBUG_MEMORY = false;
    private static final boolean DEBUG_HISTORY = false;
    private static final boolean USE_OLD_HISTORY = false;   // for debugging.

@@ -144,6 +147,13 @@ public class BatteryStatsImpl extends BatteryStats {
    private final KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader();
    private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;

    private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats
            = new KernelMemoryBandwidthStats();
    private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>();
    public LongSparseArray<SamplingTimer> getKernelMemoryStats() {
        return mKernelMemoryStats;
    }

    public interface BatteryCallback {
        public void batteryNeedsCpuUpdate();
        public void batteryPowerChanged(boolean onBattery);
@@ -2082,6 +2092,15 @@ public class BatteryStatsImpl extends BatteryStats {
        return kwlt;
    }

    public SamplingTimer getKernelMemoryTimerLocked(long bucket) {
        SamplingTimer kmt = mKernelMemoryStats.get(bucket);
        if (kmt == null) {
            kmt = new SamplingTimer(mClocks, mOnBatteryTimeBase);
            mKernelMemoryStats.put(bucket, kmt);
        }
        return kmt;
    }

    private int writeHistoryTag(HistoryTag tag) {
        Integer idxObj = mHistoryTagPool.get(tag);
        int idx;
@@ -7698,7 +7717,6 @@ public class BatteryStatsImpl extends BatteryStats {
                NUM_BT_TX_LEVELS);
        mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
                ModemActivityInfo.TX_POWER_LEVELS);

        mMobileRadioActiveTimer = new StopwatchTimer(mClocks, null, -400, null, mOnBatteryTimeBase);
        mMobileRadioActivePerAppTimer = new StopwatchTimer(mClocks, null, -401, null,
                mOnBatteryTimeBase);
@@ -8395,6 +8413,13 @@ public class BatteryStatsImpl extends BatteryStats {
            mKernelWakelockStats.clear();
        }

        if (mKernelMemoryStats.size() > 0) {
            for (int i = 0; i < mKernelMemoryStats.size(); i++) {
                mOnBatteryTimeBase.remove(mKernelMemoryStats.valueAt(i));
            }
            mKernelMemoryStats.clear();
        }

        if (mWakeupReasonStats.size() > 0) {
            for (SamplingTimer timer : mWakeupReasonStats.values()) {
                mOnBatteryTimeBase.remove(timer);
@@ -9092,6 +9117,33 @@ public class BatteryStatsImpl extends BatteryStats {
    long mTempTotalCpuUserTimeUs;
    long mTempTotalCpuSystemTimeUs;

    /**
     * Reads the newest memory stats from the kernel.
     */
    public void updateKernelMemoryBandwidthLocked() {
        mKernelMemoryBandwidthStats.updateStats();
        LongSparseLongArray bandwidthEntries = mKernelMemoryBandwidthStats.getBandwidthEntries();
        final int bandwidthEntryCount = bandwidthEntries.size();
        int index;
        for (int i = 0; i < bandwidthEntryCount; i++) {
            SamplingTimer timer;
            if ((index = mKernelMemoryStats.indexOfKey(bandwidthEntries.keyAt(i))) >= 0) {
                timer = mKernelMemoryStats.valueAt(index);
            } else {
                timer = new SamplingTimer(mClocks, mOnBatteryTimeBase);
                mKernelMemoryStats.put(bandwidthEntries.keyAt(i), timer);
            }
            timer.update(bandwidthEntries.valueAt(i), 1);
            if (DEBUG_MEMORY) {
                Slog.d(TAG, String.format("Added entry %d and updated timer to: "
                        + "mUnpluggedReportedTotalTime %d size %d", bandwidthEntries.keyAt(i),
                        mKernelMemoryStats.get(
                                bandwidthEntries.keyAt(i)).mUnpluggedReportedTotalTime,
                        mKernelMemoryStats.size()));
            }
        }
    }

    /**
     * Read and distribute CPU usage across apps. If their are partial wakelocks being held
     * and we are on battery with screen off, we give more of the cpu time to those apps holding
@@ -10372,6 +10424,14 @@ public class BatteryStatsImpl extends BatteryStats {
            }
        }

        int NMS = in.readInt();
        for (int ims = 0; ims < NMS; ims++) {
            if (in.readInt() != 0) {
                long kmstName = in.readLong();
                getKernelMemoryTimerLocked(kmstName).readSummaryFromParcelLocked(in);
            }
        }

        final int NU = in.readInt();
        if (NU > 10000) {
            throw new ParcelFormatException("File corrupt: too many uids " + NU);
@@ -10727,6 +10787,18 @@ public class BatteryStatsImpl extends BatteryStats {
            }
        }

        out.writeInt(mKernelMemoryStats.size());
        for (int i = 0; i < mKernelMemoryStats.size(); i++) {
            Timer kmt = mKernelMemoryStats.valueAt(i);
            if (kmt != null) {
                out.writeInt(1);
                out.writeLong(mKernelMemoryStats.keyAt(i));
                kmt.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
            } else {
                out.writeInt(0);
            }
        }

        final int NU = mUidStats.size();
        out.writeInt(NU);
        for (int iu = 0; iu < NU; iu++) {
@@ -11129,6 +11201,16 @@ public class BatteryStatsImpl extends BatteryStats {
            }
        }

        mKernelMemoryStats.clear();
        int nmt = in.readInt();
        for (int imt = 0; imt < nmt; imt++) {
            if (in.readInt() != 0) {
                Long bucket = in.readLong();
                SamplingTimer kmt = new SamplingTimer(mClocks, mOnBatteryTimeBase, in);
                mKernelMemoryStats.put(bucket, kmt);
            }
        }

        mPartialTimers.clear();
        mFullTimers.clear();
        mWindowTimers.clear();
@@ -11287,6 +11369,18 @@ public class BatteryStatsImpl extends BatteryStats {
            out.writeInt(0);
        }

        out.writeInt(mKernelMemoryStats.size());
        for (int i = 0; i < mKernelMemoryStats.size(); i++) {
            SamplingTimer kmt = mKernelMemoryStats.valueAt(i);
            if (kmt != null) {
                out.writeInt(1);
                out.writeLong(mKernelMemoryStats.keyAt(i));
                kmt.writeToParcel(out, uSecRealtime);
            } else {
                out.writeInt(0);
            }
        }

        if (inclUids) {
            int size = mUidStats.size();
            out.writeInt(size);
+72 −0
Original line number Diff line number Diff line
package com.android.internal.os;

import android.os.StrictMode;
import android.text.TextUtils;
import android.util.LongSparseLongArray;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

/**
 * Reads DDR time spent at various frequencies and stores the data.  Supports diff comparison with
 * other KernelMemoryBandwidthStats objects. The sysfs file has the format:
 *
 * freq time_in_bucket ... time_in_bucket
 *      ...
 * freq time_in_bucket ... time_in_bucket
 *
 * where time is measured in nanoseconds.
 */
public class KernelMemoryBandwidthStats {
    private static final String TAG = "KernelMemoryBandwidthStats";

    final protected LongSparseLongArray mBandwidthEntries = new LongSparseLongArray();
    private static final String mSysfsFile = "/sys/kernel/memory_state_time/show_stat";
    private static final boolean DEBUG = false;

    public void updateStats() {
        StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads();
        try (BufferedReader reader = new BufferedReader(new FileReader(mSysfsFile))) {
            parseStats(reader);
        } catch (IOException e) {
            Slog.e(TAG, "Failed to read memory bandwidth: " + e.getMessage());
            mBandwidthEntries.clear();
        } finally {
            StrictMode.setThreadPolicy(policy);
        }
    }

    @VisibleForTesting
    public void parseStats(BufferedReader reader) throws IOException {
        String line;
        TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
        mBandwidthEntries.clear();
        while ((line = reader.readLine()) != null) {
            splitter.setString(line);
            splitter.next();
            int bandwidth = 0;
            int index;
            do {
                if ((index = mBandwidthEntries.indexOfKey(bandwidth)) >= 0) {
                    mBandwidthEntries.put(bandwidth, mBandwidthEntries.valueAt(index)
                            + Long.parseLong(splitter.next()) / 1000000);
                } else {
                    mBandwidthEntries.put(bandwidth, Long.parseLong(splitter.next()) / 1000000);
                }
                if (DEBUG) {
                    Slog.d(TAG, String.format("bandwidth: %s time: %s", bandwidth,
                            mBandwidthEntries.get(bandwidth)));
                }
                bandwidth++;
            } while(splitter.hasNext());
        }
    }

    public LongSparseLongArray getBandwidthEntries() {
        return mBandwidthEntries;
    }
}
+80 −0
Original line number Diff line number Diff line
package com.android.internal.os;

import android.support.test.filters.SmallTest;
import android.util.LongSparseLongArray;

import junit.framework.TestCase;

import org.junit.Assert;
import org.mockito.Mockito;

import java.io.BufferedReader;

/**
 * Tests for KernelMemoryBandwidthStats parsing and delta calculation, based on memory_state_time.
 */
public class KernelMemoryBandwidthStatsTest extends TestCase {

    /**
     * Standard example of parsing stats.
     * @throws Exception
     */
    @SmallTest
    public void testParseStandard() throws Exception {
        KernelMemoryBandwidthStats stats = new KernelMemoryBandwidthStats();
        BufferedReader mockStandardReader = Mockito.mock(BufferedReader.class);
        Mockito.when(mockStandardReader.readLine()).thenReturn(
                "99000000 0 0 0 0 0 0 0 0 0 0 0 0",
                "149000000 0 0 0 0 0 0 0 0 0 0 0 0",
                "199884800 7301000000 0 2000000 1000000 0 0 0 0 0 0 0 0",
                "299892736 674000000 0 21000000 0 0 0 0 0 0 0 0 0",
                "411959296 1146000000 0 221000000 1000000 0 0 0 0 0 0 0 0",
                "546963456 744000000 0 420000000 0 0 0 1000000 0 0 0 0 0",
                "680919040 182000000 0 1839000000 207000000 1000000 1000000 0 0 0 0 0 0",
                "767950848 0 0 198000000 33000000 4000000 0 1000000 0 0 0 0 0",
                "1016987648 0 0 339000000 362000000 3000000 0 0 0 0 0 0 16000000",
                "1295908864 0 0 20000000 870000000 244000000 0 0 0 0 0 0 33000000",
                "1554907136 0 0 6000000 32000000 631000000 115000000 0 0 0 1000000 0 0",
                "1803943936 2496000000 0 17000000 2000000 377000000 1505000000 278000000 183000000 141000000 486000000 154000000 113000000", null);
        stats.parseStats(mockStandardReader);
        long[] expected = new long[] {12543L, 0L, 3083L, 1508L, 1260L, 1621L, 280L, 183L,
                141L, 487L, 154L, 162L};
        LongSparseLongArray array = stats.getBandwidthEntries();
        for (int i = 2; i < array.size(); i++) {
            assertEquals(i, array.keyAt(i));
            assertEquals(expected[i], array.valueAt(i));
        }
        Mockito.verify(mockStandardReader, Mockito.times(13)).readLine();
    }

    /**
     * When the stats are populated with zeroes (unsupported device), checks that the stats are
     * zero.
     * @throws Exception
     */
    @SmallTest
    public void testParseBackwards() throws Exception {
        KernelMemoryBandwidthStats zeroStats = new KernelMemoryBandwidthStats();
        BufferedReader mockZeroReader = Mockito.mock(BufferedReader.class);
        Mockito.when(mockZeroReader.readLine()).thenReturn(
                "99000000 0 0 0 0 0 0 0 0 0 0 0 0",
                "149000000 0 0 0 0 0 0 0 0 0 0 0 0",
                "199884800 0 0 0 0 0 0 0 0 0 0 0 0",
                "299892736 0 0 0 0 0 0 0 0 0 0 0 0",
                "411959296 0 0 0 0 0 0 0 0 0 0 0 0",
                "546963456 0 0 0 0 0 0 0 0 0 0 0 0",
                "680919040 0 0 0 0 0 0 0 0 0 0 0 0",
                "767950848 0 0 0 0 0 0 0 0 0 0 0 0",
                "1016987648 0 0 0 0 0 0 0 0 0 0 0 0",
                "1295908864 0 0 0 0 0 0 0 0 0 0 0 0",
                "1554907136 0 0 0 0 0 0 0 0 0 0 0 0",
                "1803943936 0 0 0 0 0 0 0 0 0 0 0 0", null);
        zeroStats.parseStats(mockZeroReader);
        long[] expected = new long[] {0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L};
        LongSparseLongArray array = zeroStats.getBandwidthEntries();
        for (int i = 0; i < array.size(); i++) {
            assertEquals(expected[i], array.valueAt(i));
        }
        Mockito.verify(mockZeroReader, Mockito.times(13)).readLine();
    }
}