Add anon RSS + swap metric
We are planning to use this metric to detect leaks.
This CL also decouples the actual memory sampling from AM. This means:
- Less time locking the pid list (we used to lock and then read proc)
- Less serialization / deserialization for the parcel
- Simpler to evolve (e.g. removed the HWM-specific method in AM)
Change-Id: I87a7243156dd8c88cfa85038e7e6cf4963e271e1
Test: manual, MemoryStatUtilTest, UidAtomTests
Bug: b/135418017
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index d457ff9..f47767e 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -3948,7 +3948,7 @@
optional int64 page_major_fault = 5;
// RSS
- // Value is read from /proc/PID/stat, field 24. Or from memory.stat, field
+ // Value is read from /proc/PID/status. Or from memory.stat, field
// total_rss if per-app memory cgroups are enabled.
optional int64 rss_in_bytes = 6;
@@ -3968,6 +3968,9 @@
// Elapsed real time when the process started.
// Value is read from /proc/PID/stat, field 22. 0 if read from per-app memory cgroups.
optional int64 start_time_nanos = 10;
+
+ // Anonymous page size plus swap size. Values are read from /proc/PID/status.
+ optional int32 anon_rss_and_swap_in_kilobytes = 11;
}
/*
@@ -3990,7 +3993,7 @@
optional int64 page_major_fault = 4;
// RSS
- // Value read from /proc/PID/stat, field 24.
+ // Value read from /proc/PID/status.
optional int64 rss_in_bytes = 5;
// Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0.
@@ -4003,6 +4006,9 @@
// SWAP
// Value read from /proc/PID/status, field VmSwap.
optional int64 swap_in_bytes = 8;
+
+ // Anonymous page size plus swap size. Values are read from /proc/PID/status.
+ optional int32 anon_rss_and_swap_in_kilobytes = 9;
}
/*
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 0b25b2e..8508c2c 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -168,20 +168,13 @@
public abstract boolean isUidActive(int uid);
/**
- * Returns a list that contains the memory stats for currently running processes.
+ * Returns a list of running processes along with corresponding uids, pids and their oom score.
*
* Only processes managed by ActivityManagerService are included.
*/
public abstract List<ProcessMemoryState> getMemoryStateForProcesses();
/**
- * Returns a list that contains the memory high-water mark for currently running processes.
- *
- * Only processes managed by ActivityManagerService are included.
- */
- public abstract List<ProcessMemoryHighWaterMark> getMemoryHighWaterMarkForProcesses();
-
- /**
* Checks to see if the calling pid is allowed to handle the user. Returns adjusted user id as
* needed.
*/
diff --git a/core/java/android/app/ProcessMemoryHighWaterMark.java b/core/java/android/app/ProcessMemoryHighWaterMark.java
deleted file mode 100644
index d1cae94..0000000
--- a/core/java/android/app/ProcessMemoryHighWaterMark.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * The memory high-water mark value for a process.
- * {@hide}
- */
-public final class ProcessMemoryHighWaterMark implements Parcelable {
- public final int uid;
- public final String processName;
- public final long rssHighWaterMarkInBytes;
-
- public ProcessMemoryHighWaterMark(int uid, String processName, long rssHighWaterMarkInBytes) {
- this.uid = uid;
- this.processName = processName;
- this.rssHighWaterMarkInBytes = rssHighWaterMarkInBytes;
- }
-
- private ProcessMemoryHighWaterMark(Parcel in) {
- uid = in.readInt();
- processName = in.readString();
- rssHighWaterMarkInBytes = in.readLong();
- }
-
- public static final @android.annotation.NonNull Creator<ProcessMemoryHighWaterMark> CREATOR =
- new Creator<ProcessMemoryHighWaterMark>() {
- @Override
- public ProcessMemoryHighWaterMark createFromParcel(Parcel in) {
- return new ProcessMemoryHighWaterMark(in);
- }
-
- @Override
- public ProcessMemoryHighWaterMark[] newArray(int size) {
- return new ProcessMemoryHighWaterMark[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeInt(uid);
- parcel.writeString(processName);
- parcel.writeLong(rssHighWaterMarkInBytes);
- }
-}
diff --git a/core/java/android/app/ProcessMemoryState.java b/core/java/android/app/ProcessMemoryState.java
index e28d79c..24914a6 100644
--- a/core/java/android/app/ProcessMemoryState.java
+++ b/core/java/android/app/ProcessMemoryState.java
@@ -20,44 +20,27 @@
import android.os.Parcelable;
/**
- * The memory stats for a process.
+ * State (oom score) for processes known to activity manager.
* {@hide}
*/
public final class ProcessMemoryState implements Parcelable {
public final int uid;
+ public final int pid;
public final String processName;
public final int oomScore;
- public final long pgfault;
- public final long pgmajfault;
- public final long rssInBytes;
- public final long cacheInBytes;
- public final long swapInBytes;
- public final long startTimeNanos;
- public ProcessMemoryState(int uid, String processName, int oomScore, long pgfault,
- long pgmajfault, long rssInBytes, long cacheInBytes,
- long swapInBytes, long startTimeNanos) {
+ public ProcessMemoryState(int uid, int pid, String processName, int oomScore) {
this.uid = uid;
+ this.pid = pid;
this.processName = processName;
this.oomScore = oomScore;
- this.pgfault = pgfault;
- this.pgmajfault = pgmajfault;
- this.rssInBytes = rssInBytes;
- this.cacheInBytes = cacheInBytes;
- this.swapInBytes = swapInBytes;
- this.startTimeNanos = startTimeNanos;
}
private ProcessMemoryState(Parcel in) {
uid = in.readInt();
+ pid = in.readInt();
processName = in.readString();
oomScore = in.readInt();
- pgfault = in.readLong();
- pgmajfault = in.readLong();
- rssInBytes = in.readLong();
- cacheInBytes = in.readLong();
- swapInBytes = in.readLong();
- startTimeNanos = in.readLong();
}
public static final @android.annotation.NonNull Creator<ProcessMemoryState> CREATOR = new Creator<ProcessMemoryState>() {
@@ -80,13 +63,8 @@
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(uid);
+ parcel.writeInt(pid);
parcel.writeString(processName);
parcel.writeInt(oomScore);
- parcel.writeLong(pgfault);
- parcel.writeLong(pgmajfault);
- parcel.writeLong(rssInBytes);
- parcel.writeLong(cacheInBytes);
- parcel.writeLong(swapInBytes);
- parcel.writeLong(startTimeNanos);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 627ca91..b342d37 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -120,8 +120,6 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.MemoryStatUtil.hasMemcg;
-import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
-import static com.android.server.am.MemoryStatUtil.readRssHighWaterMarkFromProcfs;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
@@ -178,7 +176,6 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.ProcessMemoryHighWaterMark;
import android.app.ProcessMemoryState;
import android.app.ProfilerInfo;
import android.app.WaitResult;
@@ -349,7 +346,6 @@
import com.android.server.ThreadPriorityBooster;
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
-import com.android.server.am.MemoryStatUtil.MemoryStat;
import com.android.server.appop.AppOpsService;
import com.android.server.contentcapture.ContentCaptureManagerInternal;
import com.android.server.firewall.IntentFirewall;
@@ -17861,43 +17857,14 @@
synchronized (mPidsSelfLocked) {
for (int i = 0, size = mPidsSelfLocked.size(); i < size; i++) {
final ProcessRecord r = mPidsSelfLocked.valueAt(i);
- final int pid = r.pid;
- final int uid = r.uid;
- final MemoryStat memoryStat = readMemoryStatFromFilesystem(uid, pid);
- if (memoryStat == null) {
- continue;
- }
- ProcessMemoryState processMemoryState =
- new ProcessMemoryState(uid,
- r.processName,
- r.curAdj,
- memoryStat.pgfault,
- memoryStat.pgmajfault,
- memoryStat.rssInBytes,
- memoryStat.cacheInBytes,
- memoryStat.swapInBytes,
- memoryStat.startTimeNanos);
- processMemoryStates.add(processMemoryState);
+ processMemoryStates.add(
+ new ProcessMemoryState(r.uid, r.pid, r.processName, r.curAdj));
}
}
return processMemoryStates;
}
@Override
- public List<ProcessMemoryHighWaterMark> getMemoryHighWaterMarkForProcesses() {
- List<ProcessMemoryHighWaterMark> results = new ArrayList<>();
- synchronized (mPidsSelfLocked) {
- for (int i = 0, size = mPidsSelfLocked.size(); i < size; i++) {
- final ProcessRecord r = mPidsSelfLocked.valueAt(i);
- final long rssHighWaterMarkInBytes = readRssHighWaterMarkFromProcfs(r.pid);
- results.add(new ProcessMemoryHighWaterMark(r.uid, r.processName,
- rssHighWaterMarkInBytes));
- }
- }
- return results;
- }
-
- @Override
public int handleIncomingUser(int callingPid, int callingUid, int userId,
boolean allowAll, int allowMode, String name, String callerPackage) {
return mUserController.handleIncomingUser(callingPid, callingUid, userId, allowAll,
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index 78d2634..58d9965 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -69,6 +69,8 @@
Pattern.compile("VmHWM:\\s*(\\d+)\\s*kB");
private static final Pattern PROCFS_RSS_IN_KILOBYTES =
Pattern.compile("VmRSS:\\s*(\\d+)\\s*kB");
+ private static final Pattern PROCFS_ANON_RSS_IN_KILOBYTES =
+ Pattern.compile("RssAnon:\\s*(\\d+)\\s*kB");
private static final Pattern PROCFS_SWAP_IN_KILOBYTES =
Pattern.compile("VmSwap:\\s*(\\d+)\\s*kB");
@@ -204,6 +206,8 @@
memoryStat.pgmajfault = Long.parseLong(splits[PGMAJFAULT_INDEX]);
memoryStat.rssInBytes =
tryParseLong(PROCFS_RSS_IN_KILOBYTES, procStatusContents) * BYTES_IN_KILOBYTE;
+ memoryStat.anonRssInBytes =
+ tryParseLong(PROCFS_ANON_RSS_IN_KILOBYTES, procStatusContents) * BYTES_IN_KILOBYTE;
memoryStat.swapInBytes =
tryParseLong(PROCFS_SWAP_IN_KILOBYTES, procStatusContents) * BYTES_IN_KILOBYTE;
memoryStat.startTimeNanos = Long.parseLong(splits[START_TIME_INDEX]) * JIFFY_NANOS;
@@ -284,9 +288,11 @@
public long pgfault;
/** Number of major page faults */
public long pgmajfault;
- /** Number of bytes of anonymous and swap cache memory */
+ /** For memcg stats, the anon rss + swap cache size. Otherwise total RSS. */
public long rssInBytes;
- /** Number of bytes of page cache memory */
+ /** Number of bytes of the anonymous RSS. Only present for non-memcg stats. */
+ public long anonRssInBytes;
+ /** Number of bytes of page cache memory. Only present for memcg stats. */
public long cacheInBytes;
/** Number of bytes of swap usage */
public long swapInBytes;
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 63439d5..f0b2eca 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -25,6 +25,7 @@
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.server.am.MemoryStatUtil.readCmdlineFromProcfs;
+import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromProcfs;
import static com.android.server.am.MemoryStatUtil.readRssHighWaterMarkFromProcfs;
import static com.android.server.am.MemoryStatUtil.readSystemIonHeapSizeFromDebugfs;
@@ -39,7 +40,6 @@
import android.app.AppOpsManager.HistoricalOpsRequest;
import android.app.AppOpsManager.HistoricalPackageOps;
import android.app.AppOpsManager.HistoricalUidOps;
-import android.app.ProcessMemoryHighWaterMark;
import android.app.ProcessMemoryState;
import android.app.StatsManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
@@ -1170,17 +1170,23 @@
LocalServices.getService(
ActivityManagerInternal.class).getMemoryStateForProcesses();
for (ProcessMemoryState processMemoryState : processMemoryStates) {
+ final MemoryStat memoryStat = readMemoryStatFromFilesystem(processMemoryState.uid,
+ processMemoryState.pid);
+ if (memoryStat == null) {
+ continue;
+ }
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(processMemoryState.uid);
e.writeString(processMemoryState.processName);
e.writeInt(processMemoryState.oomScore);
- e.writeLong(processMemoryState.pgfault);
- e.writeLong(processMemoryState.pgmajfault);
- e.writeLong(processMemoryState.rssInBytes);
- e.writeLong(processMemoryState.cacheInBytes);
- e.writeLong(processMemoryState.swapInBytes);
+ e.writeLong(memoryStat.pgfault);
+ e.writeLong(memoryStat.pgmajfault);
+ e.writeLong(memoryStat.rssInBytes);
+ e.writeLong(memoryStat.cacheInBytes);
+ e.writeLong(memoryStat.swapInBytes);
e.writeLong(0); // unused
- e.writeLong(processMemoryState.startTimeNanos);
+ e.writeLong(memoryStat.startTimeNanos);
+ e.writeInt(anonAndSwapInKilobytes(memoryStat));
pulledData.add(e);
}
}
@@ -1213,20 +1219,31 @@
e.writeLong(0); // unused
e.writeLong(memoryStat.startTimeNanos);
e.writeLong(memoryStat.swapInBytes);
+ e.writeInt(anonAndSwapInKilobytes(memoryStat));
pulledData.add(e);
}
}
+ private static int anonAndSwapInKilobytes(MemoryStat memoryStat) {
+ return (int) ((memoryStat.anonRssInBytes + memoryStat.swapInBytes) / 1024);
+ }
+
private void pullProcessMemoryHighWaterMark(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
- List<ProcessMemoryHighWaterMark> results = LocalServices.getService(
- ActivityManagerInternal.class).getMemoryHighWaterMarkForProcesses();
- for (ProcessMemoryHighWaterMark processMemoryHighWaterMark : results) {
+ List<ProcessMemoryState> managedProcessList =
+ LocalServices.getService(
+ ActivityManagerInternal.class).getMemoryStateForProcesses();
+ for (ProcessMemoryState managedProcess : managedProcessList) {
+ final long rssHighWaterMarkInBytes =
+ readRssHighWaterMarkFromProcfs(managedProcess.pid);
+ if (rssHighWaterMarkInBytes == 0) {
+ continue;
+ }
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(processMemoryHighWaterMark.uid);
- e.writeString(processMemoryHighWaterMark.processName);
- e.writeLong(processMemoryHighWaterMark.rssHighWaterMarkInBytes);
+ e.writeInt(managedProcess.uid);
+ e.writeString(managedProcess.processName);
+ e.writeLong(rssHighWaterMarkInBytes);
pulledData.add(e);
}
int[] pids = getPidsForCommands(MEMORY_INTERESTING_NATIVE_PROCESSES);
diff --git a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
index 174571d..213c939 100644
--- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
@@ -233,6 +233,7 @@
assertEquals(0, stat.cacheInBytes);
assertEquals(22 * BYTES_IN_KILOBYTE, stat.swapInBytes);
assertEquals(2222 * JIFFY_NANOS, stat.startTimeNanos);
+ assertEquals(37860 * BYTES_IN_KILOBYTE, stat.anonRssInBytes);
}
@Test