summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java96
-rw-r--r--core/java/com/android/internal/os/KernelMemoryBandwidthStats.java72
-rw-r--r--core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java80
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();
+ }
+}