Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0862158f authored by Rafal Slawik's avatar Rafal Slawik
Browse files

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
parent 5269abcf
Loading
Loading
Loading
Loading
+28 −2
Original line number Diff line number Diff line
@@ -146,7 +146,7 @@ message Atom {
    }

    // 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 @@ message Atom {
        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.
@@ -2344,6 +2345,31 @@ message ProcessMemoryState {
    optional int64 rss_high_watermark_in_bytes = 9;
}

/*
 * 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.
 */
+6 −0
Original line number Diff line number Diff line
@@ -170,6 +170,12 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
          {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
+9 −0
Original line number Diff line number Diff line
@@ -166,9 +166,18 @@ public abstract class ActivityManagerInternal {

    /**
     * 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.
+27 −0
Original line number Diff line number Diff line
@@ -72,7 +72,9 @@ import static android.os.Process.THREAD_GROUP_TOP_APP;
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.POSTFIX_UID_OBSER
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;
@@ -20497,6 +20502,28 @@ public class ActivityManagerService extends IActivityManager.Stub
            return processMemoryStates;
        }
        @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) {
+25 −0
Original line number Diff line number Diff line
@@ -37,6 +37,17 @@ import java.util.regex.Pattern;
 * 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 @@ final class MemoryStatUtil {
    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 @@ final class MemoryStatUtil {
        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()) {
Loading