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

Commit 196532a0 authored by Rafal Slawik's avatar Rafal Slawik
Browse files

Move RSS HWM stats reading code under stats

The RSS high-water mark stats are used only by telemetry and should not
be a part of am package.

Modifications:
- avoid null, use empty string instead
- use assertThat from Truth

Bug: 140986627
Test: atest MemoryStatUtilTest
Test: atest ProcfsMemoryUtilTest
Test: statsd_testdrive for RSS HWM atom
Change-Id: I60140c085f1a47d71505955d79f545f52a4c4423
parent e33dcad8
Loading
Loading
Loading
Loading
+0 −24
Original line number Diff line number Diff line
@@ -63,8 +63,6 @@ public final class MemoryStatUtil {
    private static final Pattern CACHE_IN_BYTES = Pattern.compile("total_cache (\\d+)");
    private static final Pattern SWAP_IN_BYTES = Pattern.compile("total_swap (\\d+)");

    private static final Pattern RSS_HIGH_WATERMARK_IN_KILOBYTES =
            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 =
@@ -112,15 +110,6 @@ public final class MemoryStatUtil {
        return parseMemoryStatFromProcfs(readFileContents(statPath), readFileContents(statusPath));
    }

    /**
     * Reads RSS high-water mark of a process from procfs. Returns value of the VmHWM field in
     * /proc/PID/status in kilobytes or 0 if not available.
     */
    public static int readRssHighWaterMarkFromProcfs(int pid) {
        final String statusPath = String.format(Locale.US, PROC_STATUS_FILE_FMT, pid);
        return parseVmHWMFromProcfs(readFileContents(statusPath));
    }

    /**
     * Reads cmdline of a process from procfs.
     *
@@ -203,19 +192,6 @@ public final class MemoryStatUtil {
        }
    }

    /**
     * Parses RSS high-water mark out from the contents of the /proc/pid/status file in procfs. The
     * returned value is in kilobytes.
     */
    @VisibleForTesting
    static int parseVmHWMFromProcfs(String procStatusContents) {
        if (procStatusContents == null || procStatusContents.isEmpty()) {
            return 0;
        }
        return (int) tryParseLong(RSS_HIGH_WATERMARK_IN_KILOBYTES, procStatusContents);
    }


    /**
     * Parses cmdline out of the contents of the /proc/pid/cmdline file in procfs.
     *
+75 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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 com.android.server.stats;

import android.os.FileUtils;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;

import java.io.File;
import java.io.IOException;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

final class ProcfsMemoryUtil {
    private static final String TAG = "ProcfsMemoryUtil";

    /** Path to procfs status file: /proc/pid/status. */
    private static final String STATUS_FILE_FMT = "/proc/%d/status";

    private static final Pattern RSS_HIGH_WATER_MARK_IN_KILOBYTES =
            Pattern.compile("VmHWM:\\s*(\\d+)\\s*kB");

    private ProcfsMemoryUtil() {}

    /**
     * Reads RSS high-water mark of a process from procfs. Returns value of the VmHWM field in
     * /proc/PID/status in kilobytes or 0 if not available.
     */
    static int readRssHighWaterMarkFromProcfs(int pid) {
        final String statusPath = String.format(Locale.US, STATUS_FILE_FMT, pid);
        return parseVmHWMFromStatus(readFile(statusPath));
    }

    /**
     * Parses RSS high-water mark out from the contents of the /proc/pid/status file in procfs. The
     * returned value is in kilobytes.
     */
    @VisibleForTesting
    static int parseVmHWMFromStatus(String contents) {
        if (contents.isEmpty()) {
            return 0;
        }
        final Matcher matcher = RSS_HIGH_WATER_MARK_IN_KILOBYTES.matcher(contents);
        try {
            return matcher.find() ? Integer.parseInt(matcher.group(1)) : 0;
        } catch (NumberFormatException e) {
            Slog.e(TAG, "Failed to parse value", e);
            return 0;
        }
    }

    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 "";
        }
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -27,9 +27,9 @@ 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.stats.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
import static com.android.server.stats.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
import static com.android.server.stats.ProcfsMemoryUtil.readRssHighWaterMarkFromProcfs;

import android.annotation.NonNull;
import android.annotation.Nullable;
+0 −13
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import static com.android.server.am.MemoryStatUtil.MemoryStat;
import static com.android.server.am.MemoryStatUtil.parseCmdlineFromProcfs;
import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromMemcg;
import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromProcfs;
import static com.android.server.am.MemoryStatUtil.parseVmHWMFromProcfs;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -229,18 +228,6 @@ public class MemoryStatUtilTest {
        assertNull(parseMemoryStatFromProcfs(contents, PROC_STATUS_CONTENTS));
    }

    @Test
    public void testParseVmHWMFromProcfs_parsesCorrectValue() {
        assertEquals(137668, parseVmHWMFromProcfs(PROC_STATUS_CONTENTS));
    }

    @Test
    public void testParseVmHWMFromProcfs_emptyContents() {
        assertEquals(0, parseVmHWMFromProcfs(""));

        assertEquals(0, parseVmHWMFromProcfs(null));
    }

    @Test
    public void testParseCmdlineFromProcfs_invalidValue() {
        byte[] nothing = new byte[] {0x00, 0x74, 0x65, 0x73, 0x74}; // \0test
+93 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 com.android.server.stats;

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

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

import androidx.test.filters.SmallTest;

import org.junit.Test;

/**
 * Build/Install/Run:
 *  atest FrameworksServicesTests:ProcfsMemoryUtilTest
 */
@SmallTest
public class ProcfsMemoryUtilTest {
    private static final String STATUS_CONTENTS = "Name:\tandroid.youtube\n"
            + "State:\tS (sleeping)\n"
            + "Tgid:\t12088\n"
            + "Pid:\t12088\n"
            + "PPid:\t723\n"
            + "TracerPid:\t0\n"
            + "Uid:\t10083\t10083\t10083\t10083\n"
            + "Gid:\t10083\t10083\t10083\t10083\n"
            + "Ngid:\t0\n"
            + "FDSize:\t128\n"
            + "Groups:\t3003 9997 20083 50083 \n"
            + "VmPeak:\t 4546844 kB\n"
            + "VmSize:\t 4542636 kB\n"
            + "VmLck:\t       0 kB\n"
            + "VmPin:\t       0 kB\n"
            + "VmHWM:\t  137668 kB\n" // RSS high-water mark
            + "VmRSS:\t  126776 kB\n" // RSS
            + "RssAnon:\t   37860 kB\n"
            + "RssFile:\t   88764 kB\n"
            + "RssShmem:\t     152 kB\n"
            + "VmData:\t 4125112 kB\n"
            + "VmStk:\t    8192 kB\n"
            + "VmExe:\t      24 kB\n"
            + "VmLib:\t  102432 kB\n"
            + "VmPTE:\t    1300 kB\n"
            + "VmPMD:\t      36 kB\n"
            + "VmSwap:\t      22 kB\n" // Swap
            + "Threads:\t95\n"
            + "SigQ:\t0/13641\n"
            + "SigPnd:\t0000000000000000\n"
            + "ShdPnd:\t0000000000000000\n"
            + "SigBlk:\t0000000000001204\n"
            + "SigIgn:\t0000000000000001\n"
            + "SigCgt:\t00000006400084f8\n"
            + "CapInh:\t0000000000000000\n"
            + "CapPrm:\t0000000000000000\n"
            + "CapEff:\t0000000000000000\n"
            + "CapBnd:\t0000000000000000\n"
            + "CapAmb:\t0000000000000000\n"
            + "Seccomp:\t2\n"
            + "Cpus_allowed:\tff\n"
            + "Cpus_allowed_list:\t0-7\n"
            + "Mems_allowed:\t1\n"
            + "Mems_allowed_list:\t0\n"
            + "voluntary_ctxt_switches:\t903\n"
            + "nonvoluntary_ctxt_switches:\t104\n";

    @Test
    public void testParseVmHWMFromStatus_parsesCorrectValue() {
        assertThat(parseVmHWMFromStatus(STATUS_CONTENTS)).isEqualTo(137668);
    }

    @Test
    public void testParseVmHWMFromStatus_invalidValue() {
        assertThat(parseVmHWMFromStatus("test\nVmHWM: x0x0x\ntest")).isEqualTo(0);
    }

    @Test
    public void testParseVmHWMFromStatus_emptyContents() {
        assertThat(parseVmHWMFromStatus("")).isEqualTo(0);
    }
}