Add ProcessMemorySnapshot atom

Introduce new atom to replace existing ProcessMemoryState and
NativeProcessMemoryState ones for telemetry purposes.

This change is the first step to decouple memory stats from the
framework internals. Follow-up CLs will remove the telemetry parts
from the existing atoms.

Test: atest ProcfsMemoryUtilTest
Test: statsd_testdrive 10064
Change-Id: I7959c6e473201c10e7da17fa49d864bb03a2403c
This commit is contained in:
Rafal Slawik
2019-09-20 11:16:20 +01:00
parent 644c2bb97f
commit 7efcefb5d7
5 changed files with 171 additions and 11 deletions

View File

@@ -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.
*/

View File

@@ -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)}},

View File

@@ -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,16 +58,25 @@ final class ProcfsMemoryUtil {
*/
@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;
}
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) {
@@ -72,4 +87,27 @@ final class ProcfsMemoryUtil {
return "";
}
}
private static int tryParseInt(String contents, Pattern pattern) {
if (contents.isEmpty()) {
return 0;
}
final Matcher matcher = pattern.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;
}
}
static final class MemorySnapshot {
public int rssInKilobytes;
public int anonRssInKilobytes;
public int swapInKilobytes;
boolean isEmpty() {
return (anonRssInKilobytes + swapInKilobytes) == 0;
}
}
}

View File

@@ -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;

View File

@@ -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();
}
}