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

Commit 2f549398 authored by Rafal Slawik's avatar Rafal Slawik Committed by Android (Google) Code Review
Browse files

Merge "Add ProcessMemorySnapshot atom"

parents e394a73d 7efcefb5
Loading
Loading
Loading
Loading
+42 −1
Original line number Diff line number Diff line
@@ -340,7 +340,7 @@ message Atom {
    }

    // Pulled events will start at field 10000.
    // Next: 10064
    // Next: 10065
    oneof pulled {
        WifiBytesTransfer wifi_bytes_transfer = 10000;
        WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -406,6 +406,7 @@ message Atom {
        ProcessSystemIonHeapSize process_system_ion_heap_size = 10061;
        SurfaceflingerStatsGlobalInfo surfaceflinger_stats_global_info = 10062;
        SurfaceflingerStatsLayerInfo surfaceflinger_stats_layer_info = 10063;
        ProcessMemorySnapshot process_memory_snapshot = 10064;
    }

    // DO NOT USE field numbers above 100,000 in AOSP.
@@ -4107,6 +4108,46 @@ message ProcessMemoryHighWaterMark {
    optional int32 rss_high_water_mark_in_kilobytes = 4;
}

/*
 * Logs the memory stats for a process.
 *
 * Pulled from StatsCompanionService for all managed processes (from ActivityManagerService)
 * and for selected native processes.
 */
message ProcessMemorySnapshot {
    // The uid if available. -1 means not available.
    optional int32 uid = 1 [(is_uid) = true];

    // The process name.
    // Usually package name or process cmdline.
    // Provided by ActivityManagerService or read from /proc/PID/cmdline.
    optional string process_name = 2;

    // The pid of the process.
    // Allows to disambiguate instances of the process.
    optional int32 pid = 3;

    // The current OOM score adjustment value.
    // Read from ProcessRecord for managed processes.
    // Placeholder -1001 (OOM_SCORE_ADJ_MIN - 1, outside of allowed range) for native ones.
    optional int32 oom_score_adj = 4;

    // The current RSS of the process.
    // VmRSS from /proc/pid/status.
    optional int32 rss_in_kilobytes = 5;

    // The current anon RSS of the process.
    // RssAnon from /proc/pid/status.
    optional int32 anon_rss_in_kilobytes = 6;

    // The current swap size of the process.
    // VmSwap from /proc/pid/status.
    optional int32 swap_in_kilobytes = 7;

    // The sum of rss_in_kilobytes and swap_in_kilobytes.
    optional int32 anon_rss_and_swap_in_kilobytes = 8;
}

/*
 * Elapsed real time from SystemClock.
 */
+3 −0
Original line number Diff line number Diff line
@@ -153,6 +153,9 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
         {.additiveFields = {3},
          .puller =
                  new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
        // process_memory_snapshot
        {android::util::PROCESS_MEMORY_SNAPSHOT,
         {.puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_SNAPSHOT)}},
        // system_ion_heap_size
        {android::util::SYSTEM_ION_HEAP_SIZE,
         {.puller = new StatsCompanionServicePuller(android::util::SYSTEM_ION_HEAP_SIZE)}},
+45 −7
Original line number Diff line number Diff line
@@ -34,6 +34,12 @@ final class ProcfsMemoryUtil {

    private static final Pattern RSS_HIGH_WATER_MARK_IN_KILOBYTES =
            Pattern.compile("VmHWM:\\s*(\\d+)\\s*kB");
    private static final Pattern RSS_IN_KILOBYTES =
            Pattern.compile("VmRSS:\\s*(\\d+)\\s*kB");
    private static final Pattern ANON_RSS_IN_KILOBYTES =
            Pattern.compile("RssAnon:\\s*(\\d+)\\s*kB");
    private static final Pattern SWAP_IN_KILOBYTES =
            Pattern.compile("VmSwap:\\s*(\\d+)\\s*kB");

    private ProcfsMemoryUtil() {}

@@ -52,10 +58,41 @@ final class ProcfsMemoryUtil {
     */
    @VisibleForTesting
    static int parseVmHWMFromStatus(String contents) {
        return tryParseInt(contents, RSS_HIGH_WATER_MARK_IN_KILOBYTES);
    }

    /**
     * Reads memory stat of a process from procfs. Returns values of the VmRss, AnonRSS, VmSwap
     * fields in /proc/pid/status in kilobytes or 0 if not available.
     */
    static MemorySnapshot readMemorySnapshotFromProcfs(int pid) {
        final String statusPath = String.format(Locale.US, STATUS_FILE_FMT, pid);
        return parseMemorySnapshotFromStatus(readFile(statusPath));
    }

    @VisibleForTesting
    static MemorySnapshot parseMemorySnapshotFromStatus(String contents) {
        final MemorySnapshot snapshot = new MemorySnapshot();
        snapshot.rssInKilobytes = tryParseInt(contents, RSS_IN_KILOBYTES);
        snapshot.anonRssInKilobytes = tryParseInt(contents, ANON_RSS_IN_KILOBYTES);
        snapshot.swapInKilobytes = tryParseInt(contents, SWAP_IN_KILOBYTES);
        return snapshot;
    }

    private static String readFile(String path) {
        try {
            final File file = new File(path);
            return FileUtils.readTextFile(file, 0 /* max */, null /* ellipsis */);
        } catch (IOException e) {
            return "";
        }
    }

    private static int tryParseInt(String contents, Pattern pattern) {
        if (contents.isEmpty()) {
            return 0;
        }
        final Matcher matcher = RSS_HIGH_WATER_MARK_IN_KILOBYTES.matcher(contents);
        final Matcher matcher = pattern.matcher(contents);
        try {
            return matcher.find() ? Integer.parseInt(matcher.group(1)) : 0;
        } catch (NumberFormatException e) {
@@ -64,12 +101,13 @@ final class ProcfsMemoryUtil {
        }
    }

    private static String readFile(String path) {
        try {
            final File file = new File(path);
            return FileUtils.readTextFile(file, 0 /* max */, null /* ellipsis */);
        } catch (IOException e) {
            return "";
    static final class MemorySnapshot {
        public int rssInKilobytes;
        public int anonRssInKilobytes;
        public int swapInKilobytes;

        boolean isEmpty() {
            return (anonRssInKilobytes + swapInKilobytes) == 0;
        }
    }
}
+47 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromProcfs;
import static com.android.server.stats.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
import static com.android.server.stats.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
import static com.android.server.stats.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
import static com.android.server.stats.ProcfsMemoryUtil.readRssHighWaterMarkFromProcfs;

import android.annotation.NonNull;
@@ -141,6 +142,7 @@ import com.android.server.SystemServiceManager;
import com.android.server.am.MemoryStatUtil.MemoryStat;
import com.android.server.role.RoleManagerInternal;
import com.android.server.stats.IonMemoryUtil.IonAllocations;
import com.android.server.stats.ProcfsMemoryUtil.MemorySnapshot;
import com.android.server.storage.DiskStatsFileLogger;
import com.android.server.storage.DiskStatsLoggingService;

@@ -1270,6 +1272,47 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
        SystemProperties.set("sys.rss_hwm_reset.on", "1");
    }

    private void pullProcessMemorySnapshot(
            int tagId, long elapsedNanos, long wallClockNanos,
            List<StatsLogEventWrapper> pulledData) {
        List<ProcessMemoryState> managedProcessList =
                LocalServices.getService(
                        ActivityManagerInternal.class).getMemoryStateForProcesses();
        for (ProcessMemoryState managedProcess : managedProcessList) {
            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
            e.writeInt(managedProcess.uid);
            e.writeString(managedProcess.processName);
            e.writeInt(managedProcess.pid);
            e.writeInt(managedProcess.oomScore);
            final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid);
            if (snapshot.isEmpty()) {
                continue;
            }
            e.writeInt(snapshot.rssInKilobytes);
            e.writeInt(snapshot.anonRssInKilobytes);
            e.writeInt(snapshot.swapInKilobytes);
            e.writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes);
            pulledData.add(e);
        }
        int[] pids = getPidsForCommands(MEMORY_INTERESTING_NATIVE_PROCESSES);
        for (int pid : pids) {
            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
            e.writeInt(getUidForPid(pid));
            e.writeString(readCmdlineFromProcfs(pid));
            e.writeInt(pid);
            e.writeInt(-1001);  // Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1.
            final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid);
            if (snapshot.isEmpty()) {
                continue;
            }
            e.writeInt(snapshot.rssInKilobytes);
            e.writeInt(snapshot.anonRssInKilobytes);
            e.writeInt(snapshot.swapInKilobytes);
            e.writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes);
            pulledData.add(e);
        }
    }

    private void pullSystemIonHeapSize(
            int tagId, long elapsedNanos, long wallClockNanos,
            List<StatsLogEventWrapper> pulledData) {
@@ -2352,6 +2395,10 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
                pullProcessMemoryHighWaterMark(tagId, elapsedNanos, wallClockNanos, ret);
                break;
            }
            case StatsLog.PROCESS_MEMORY_SNAPSHOT: {
                pullProcessMemorySnapshot(tagId, elapsedNanos, wallClockNanos, ret);
                break;
            }
            case StatsLog.SYSTEM_ION_HEAP_SIZE: {
                pullSystemIonHeapSize(tagId, elapsedNanos, wallClockNanos, ret);
                break;
+31 −0
Original line number Diff line number Diff line
@@ -15,12 +15,15 @@
 */
package com.android.server.stats;

import static com.android.server.stats.ProcfsMemoryUtil.parseMemorySnapshotFromStatus;
import static com.android.server.stats.ProcfsMemoryUtil.parseVmHWMFromStatus;

import static com.google.common.truth.Truth.assertThat;

import androidx.test.filters.SmallTest;

import com.android.server.stats.ProcfsMemoryUtil.MemorySnapshot;

import org.junit.Test;

/**
@@ -90,4 +93,32 @@ public class ProcfsMemoryUtilTest {
    public void testParseVmHWMFromStatus_emptyContents() {
        assertThat(parseVmHWMFromStatus("")).isEqualTo(0);
    }

    @Test
    public void testParseMemorySnapshotFromStatus_parsesCorrectValue() {
        MemorySnapshot snapshot = parseMemorySnapshotFromStatus(STATUS_CONTENTS);
        assertThat(snapshot.rssInKilobytes).isEqualTo(126776);
        assertThat(snapshot.anonRssInKilobytes).isEqualTo(37860);
        assertThat(snapshot.swapInKilobytes).isEqualTo(22);
        assertThat(snapshot.isEmpty()).isFalse();
    }

    @Test
    public void testParseMemorySnapshotFromStatus_invalidValue() {
        MemorySnapshot snapshot =
                parseMemorySnapshotFromStatus("test\nVmRSS:\tx0x0x\nVmSwap:\t1 kB\ntest");
        assertThat(snapshot.rssInKilobytes).isEqualTo(0);
        assertThat(snapshot.anonRssInKilobytes).isEqualTo(0);
        assertThat(snapshot.swapInKilobytes).isEqualTo(1);
        assertThat(snapshot.isEmpty()).isFalse();
    }

    @Test
    public void testParseMemorySnapshotFromStatus_emptyContents() {
        MemorySnapshot snapshot = parseMemorySnapshotFromStatus("");
        assertThat(snapshot.rssInKilobytes).isEqualTo(0);
        assertThat(snapshot.anonRssInKilobytes).isEqualTo(0);
        assertThat(snapshot.swapInKilobytes).isEqualTo(0);
        assertThat(snapshot.isEmpty()).isTrue();
    }
}