Snapshot memory state for some native processes
Which processes to snapshot is controlled by a whitelist.
Benchmark for taking the snapshot:
https://docs.google.com/spreadsheets/d/1vG9ku8Uu8104CmKbO4cNeEKVeeByvHY--p0_dK1GAdA/edit?usp=sharing
(The difference between the first two sheets.)
~20ms constant cost plus ~4ms per process.
Bug: 115968899
Test: manually verified that statsd is included in the report
Change-Id: Iba680531c563ba28fae849e44044313866b2103f
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 4045996..10b7c0b 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -146,7 +146,7 @@
}
// Pulled events will start at field 10000.
- // Next: 10025
+ // Next: 10037
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000;
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -184,6 +184,7 @@
PowerProfile power_profile = 10033;
ProcStats proc_stats_pkg_proc = 10034;
ProcessCpuTime process_cpu_time = 10035;
+ NativeProcessMemoryState native_process_memory_state = 10036;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -2345,6 +2346,31 @@
}
/*
+ * Logs the memory stats for a native process (from procfs).
+ */
+message NativeProcessMemoryState {
+ // The uid if available. -1 means not available.
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // The process name.
+ optional string process_name = 2;
+
+ // # of page-faults
+ optional int64 pgfault = 3;
+
+ // # of major page-faults
+ optional int64 pgmajfault = 4;
+
+ // RSS
+ optional int64 rss_in_bytes = 5;
+
+ // RSS high watermark.
+ // Peak RSS usage of the process. Value is read from the VmHWM field in /proc/PID/status or
+ // from memory.max_usage_in_bytes under /dev/memcg if the device uses per-app memory cgroups.
+ optional int64 rss_high_watermark_in_bytes = 6;
+}
+
+/*
* Elapsed real time from SystemClock.
*/
message SystemElapsedRealtime {
@@ -3022,4 +3048,4 @@
optional int64 user_time_millis = 3;
// Process cpu time in system space, cumulative from boot/process start
optional int64 system_time_millis = 4;
-}
\ No newline at end of file
+}
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 4c74c17..8871d4d 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -170,6 +170,12 @@
{2, 3},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
+ // native_process_memory_state
+ {android::util::NATIVE_PROCESS_MEMORY_STATE,
+ {{3, 4, 5, 6},
+ {2},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}},
// temperature
{android::util::TEMPERATURE, {{}, {}, 1 * NS_PER_SEC, new ResourceThermalManagerPuller()}},
// binder_calls
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 296c063..a7836af 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -166,10 +166,19 @@
/**
* Returns a list that contains the memory stats for currently running processes.
+ *
+ * Only processes managed by ActivityManagerService are included.
*/
public abstract List<ProcessMemoryState> getMemoryStateForProcesses();
/**
+ * Returns a list that contains the memory stats for monitored native processes.
+ *
+ * The list of the monitored processes is defined in MemoryStatUtil class.
+ */
+ public abstract List<ProcessMemoryState> getMemoryStateForNativeProcesses();
+
+ /**
* Checks to see if the calling pid is allowed to handle the user. Returns adjusted user id as
* needed.
*/
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d5b618f..f9e6134 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -72,7 +72,9 @@
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
import static android.os.Process.getFreeMemory;
+import static android.os.Process.getPidsForCommands;
import static android.os.Process.getTotalMemory;
+import static android.os.Process.getUidForPid;
import static android.os.Process.isThreadInProcess;
import static android.os.Process.killProcess;
import static android.os.Process.killProcessQuiet;
@@ -138,8 +140,11 @@
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.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.MemoryStatUtil.MEMORY_STAT_INTERESTING_NATIVE_PROCESSES;
import static com.android.server.am.MemoryStatUtil.hasMemcg;
+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 android.Manifest;
import android.Manifest.permission;
@@ -20498,6 +20503,28 @@
}
@Override
+ public List<ProcessMemoryState> getMemoryStateForNativeProcesses() {
+ List<ProcessMemoryState> processMemoryStates = new ArrayList<>();
+ int[] pids = getPidsForCommands(MEMORY_STAT_INTERESTING_NATIVE_PROCESSES);
+ for (int i = 0; i < pids.length; i++) {
+ int pid = pids[i];
+ MemoryStat memoryStat = readMemoryStatFromProcfs(pid);
+ if (memoryStat == null) {
+ continue;
+ }
+ int uid = getUidForPid(pid);
+ String processName = readCmdlineFromProcfs(pid);
+ int oomScore = -1; // Unused, not included in the NativeProcessMemoryState atom.
+ ProcessMemoryState processMemoryState = new ProcessMemoryState(uid, processName,
+ oomScore, memoryStat.pgfault, memoryStat.pgmajfault,
+ memoryStat.rssInBytes, memoryStat.cacheInBytes, memoryStat.swapInBytes,
+ memoryStat.rssHighWatermarkInBytes);
+ processMemoryStates.add(processMemoryState);
+ }
+ return processMemoryStates;
+ }
+
+ @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 98f3f95..23ae77d9 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -37,6 +37,17 @@
* Static utility methods related to {@link MemoryStat}.
*/
final class MemoryStatUtil {
+ /**
+ * Which native processes to create {@link MemoryStat} for.
+ *
+ * <p>Processes are matched by their cmdline in procfs. Example: cat /proc/pid/cmdline returns
+ * /system/bin/statsd for the stats daemon.
+ */
+ static final String[] MEMORY_STAT_INTERESTING_NATIVE_PROCESSES = new String[]{
+ "/system/bin/statsd", // Stats daemon.
+ "/system/bin/surfaceflinger",
+ };
+
static final int BYTES_IN_KILOBYTE = 1024;
static final int PAGE_SIZE = 4096;
@@ -57,6 +68,8 @@
private static final String PROC_STAT_FILE_FMT = "/proc/%d/stat";
/** Path to procfs status file for logging app memory state */
private static final String PROC_STATUS_FILE_FMT = "/proc/%d/status";
+ /** Path to procfs cmdline file. Used with pid: /proc/pid/cmdline. */
+ private static final String PROC_CMDLINE_FILE_FMT = "/proc/%d/cmdline";
private static final Pattern PGFAULT = Pattern.compile("total_pgfault (\\d+)");
private static final Pattern PGMAJFAULT = Pattern.compile("total_pgmajfault (\\d+)");
@@ -119,6 +132,18 @@
return stat;
}
+ /**
+ * Reads cmdline of a process from procfs.
+ *
+ * Returns content of /proc/pid/cmdline (e.g. /system/bin/statsd) or an empty string
+ * if the file is not available.
+ */
+ static String readCmdlineFromProcfs(int pid) {
+ String path = String.format(Locale.US, PROC_CMDLINE_FILE_FMT, pid);
+ String cmdline = readFileContents(path);
+ return cmdline != null ? cmdline : "";
+ }
+
private static String readFileContents(String path) {
final File file = new File(path);
if (!file.exists()) {
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 14434cd..6db7e4f 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -1004,6 +1004,23 @@
}
}
+ private void pullNativeProcessMemoryState(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ List<ProcessMemoryState> processMemoryStates = LocalServices.getService(
+ ActivityManagerInternal.class).getMemoryStateForNativeProcesses();
+ for (ProcessMemoryState processMemoryState : processMemoryStates) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(processMemoryState.uid);
+ e.writeString(processMemoryState.processName);
+ e.writeLong(processMemoryState.pgfault);
+ e.writeLong(processMemoryState.pgmajfault);
+ e.writeLong(processMemoryState.rssInBytes);
+ e.writeLong(processMemoryState.rssHighWatermarkInBytes);
+ pulledData.add(e);
+ }
+ }
+
private void pullBinderCallsStats(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
@@ -1509,6 +1526,10 @@
pullProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+ case StatsLog.NATIVE_PROCESS_MEMORY_STATE: {
+ pullNativeProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
case StatsLog.BINDER_CALLS: {
pullBinderCallsStats(tagId, elapsedNanos, wallClockNanos, ret);
break;