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;