diff options
3 files changed, 247 insertions, 1 deletions
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index b174e3325168..be9bcfae2e61 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -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); @@ -9093,6 +9118,33 @@ public class BatteryStatsImpl extends BatteryStats { 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 * wakelocks. If the screen is on, we just assign the actual cpu time an app used. @@ -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); diff --git a/core/java/com/android/internal/os/KernelMemoryBandwidthStats.java b/core/java/com/android/internal/os/KernelMemoryBandwidthStats.java new file mode 100644 index 000000000000..b5915aaa6f2e --- /dev/null +++ b/core/java/com/android/internal/os/KernelMemoryBandwidthStats.java @@ -0,0 +1,72 @@ +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; + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java b/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java new file mode 100644 index 000000000000..32317eed203e --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java @@ -0,0 +1,80 @@ +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(); + } +} |