diff options
7 files changed, 91 insertions, 264 deletions
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index b71a86b87d49..6249de3d3bac 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -378,7 +378,6 @@ message Atom { PowerProfile power_profile = 10033; ProcStatsPkgProc proc_stats_pkg_proc = 10034; ProcessCpuTime process_cpu_time = 10035; - NativeProcessMemoryState native_process_memory_state = 10036; CpuTimePerThreadFreq cpu_time_per_thread_freq = 10037; OnDevicePowerMeasurement on_device_power_measurement = 10038; DeviceCalculatedPowerUse device_calculated_power_use = 10039; @@ -412,6 +411,8 @@ message Atom { // DO NOT USE field numbers above 100,000 in AOSP. // Field numbers 100,000 - 199,999 are reserved for non-AOSP (e.g. OEMs) to use. // Field numbers 200,000 and above are reserved for future use; do not use them at all. + + reserved 10036; } /** @@ -4019,8 +4020,8 @@ message ProcessMemoryState { optional int64 page_major_fault = 5; // RSS - // Value is read from /proc/PID/status. Or from memory.stat, field - // total_rss if per-app memory cgroups are enabled. + // Value is read from memory.stat, field total_rss if per-app memory + // cgroups are enabled. Otherwise, value from /proc/pid/stat. optional int64 rss_in_bytes = 6; // CACHE @@ -4030,56 +4031,17 @@ message ProcessMemoryState { // SWAP // Value is read from memory.stat, field total_swap if per-app memory - // cgroups are enabled. Otherwise, VmSwap from /proc/PID/status. + // cgroups are enabled. Otherwise, 0. optional int64 swap_in_bytes = 8; - // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0. + // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always -1. optional int64 rss_high_watermark_in_bytes = 9 [deprecated = true]; - // Elapsed real time when the process started. - // Value is read from /proc/PID/stat, field 22. 0 if read from per-app memory cgroups. - optional int64 start_time_nanos = 10; - - // Anonymous page size plus swap size. Values are read from /proc/PID/status. - optional int32 anon_rss_and_swap_in_kilobytes = 11; -} - -/* - * Logs the memory stats for a native process (from procfs). - * - * Pulled from StatsCompanionService for selected native processes. - */ -message NativeProcessMemoryState { - // The uid if available. -1 means not available. - optional int32 uid = 1 [(is_uid) = true]; - - // The process name. - // Value read from /proc/PID/cmdline. - optional string process_name = 2; - - // # of page-faults - optional int64 page_fault = 3; - - // # of major page-faults - optional int64 page_major_fault = 4; - - // RSS - // Value read from /proc/PID/status. - optional int64 rss_in_bytes = 5; - - // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0. - optional int64 rss_high_watermark_in_bytes = 6 [deprecated = true]; - - // Elapsed real time when the process started. - // Value is read from /proc/PID/stat, field 22. - optional int64 start_time_nanos = 7; - - // SWAP - // Value read from /proc/PID/status, field VmSwap. - optional int64 swap_in_bytes = 8; + // Deprecated: use ProcessMemorySnapshot atom instead. Always -1. + optional int64 start_time_nanos = 10 [deprecated = true]; - // Anonymous page size plus swap size. Values are read from /proc/PID/status. - optional int32 anon_rss_and_swap_in_kilobytes = 9; + // Deprecated: use ProcessMemorySnapshot atom instead. Always -1. + optional int32 anon_rss_and_swap_in_kilobytes = 11 [deprecated = true]; } /* diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 7a183a3b1b2e..43e33f59f612 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -145,16 +145,11 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_CYCLE_COUNT)}}, // process_memory_state {android::util::PROCESS_MEMORY_STATE, - {.additiveFields = {4, 5, 6, 7, 8, 9}, + {.additiveFields = {4, 5, 6, 7, 8}, .puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}}, - // native_process_memory_state - {android::util::NATIVE_PROCESS_MEMORY_STATE, - {.additiveFields = {3, 4, 5, 6, 8}, - .puller = new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}}, // process_memory_high_water_mark {android::util::PROCESS_MEMORY_HIGH_WATER_MARK, - {.additiveFields = {3}, - .puller = + {.puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}}, // process_memory_snapshot {android::util::PROCESS_MEMORY_SNAPSHOT, diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java index fd64df9e86e0..2081b177e2b2 100644 --- a/services/core/java/com/android/server/am/MemoryStatUtil.java +++ b/services/core/java/com/android/server/am/MemoryStatUtil.java @@ -39,8 +39,7 @@ import java.util.regex.Pattern; * Static utility methods related to {@link MemoryStat}. */ public final class MemoryStatUtil { - static final int BYTES_IN_KILOBYTE = 1024; - static final long JIFFY_NANOS = 1_000_000_000 / Os.sysconf(OsConstants._SC_CLK_TCK); + static final int PAGE_SIZE = (int) Os.sysconf(OsConstants._SC_PAGESIZE); private static final String TAG = TAG_WITH_CLASS_NAME ? "MemoryStatUtil" : TAG_AM; @@ -52,10 +51,6 @@ public final class MemoryStatUtil { private static final String MEMORY_STAT_FILE_FMT = "/dev/memcg/apps/uid_%d/pid_%d/memory.stat"; /** Path to procfs stat file for logging app start memory state */ 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+)"); @@ -63,16 +58,9 @@ 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 PROCFS_RSS_IN_KILOBYTES = - Pattern.compile("VmRSS:\\s*(\\d+)\\s*kB"); - private static final Pattern PROCFS_ANON_RSS_IN_KILOBYTES = - Pattern.compile("RssAnon:\\s*(\\d+)\\s*kB"); - private static final Pattern PROCFS_SWAP_IN_KILOBYTES = - Pattern.compile("VmSwap:\\s*(\\d+)\\s*kB"); - private static final int PGFAULT_INDEX = 9; private static final int PGMAJFAULT_INDEX = 11; - private static final int START_TIME_INDEX = 21; + private static final int RSS_IN_PAGES_INDEX = 23; private MemoryStatUtil() {} @@ -106,19 +94,7 @@ public final class MemoryStatUtil { @Nullable public static MemoryStat readMemoryStatFromProcfs(int pid) { final String statPath = String.format(Locale.US, PROC_STAT_FILE_FMT, pid); - final String statusPath = String.format(Locale.US, PROC_STATUS_FILE_FMT, pid); - return parseMemoryStatFromProcfs(readFileContents(statPath), readFileContents(statusPath)); - } - - /** - * 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. - */ - public static String readCmdlineFromProcfs(int pid) { - final String path = String.format(Locale.US, PROC_CMDLINE_FILE_FMT, pid); - return parseCmdlineFromProcfs(readFileContents(path)); + return parseMemoryStatFromProcfs(readFileContents(statPath)); } private static String readFileContents(String path) { @@ -160,31 +136,19 @@ public final class MemoryStatUtil { */ @VisibleForTesting @Nullable - static MemoryStat parseMemoryStatFromProcfs( - String procStatContents, String procStatusContents) { + static MemoryStat parseMemoryStatFromProcfs(String procStatContents) { if (procStatContents == null || procStatContents.isEmpty()) { return null; } - if (procStatusContents == null || procStatusContents.isEmpty()) { - return null; - } - final String[] splits = procStatContents.split(" "); if (splits.length < 24) { return null; } - try { final MemoryStat memoryStat = new MemoryStat(); memoryStat.pgfault = Long.parseLong(splits[PGFAULT_INDEX]); memoryStat.pgmajfault = Long.parseLong(splits[PGMAJFAULT_INDEX]); - memoryStat.rssInBytes = - tryParseLong(PROCFS_RSS_IN_KILOBYTES, procStatusContents) * BYTES_IN_KILOBYTE; - memoryStat.anonRssInBytes = - tryParseLong(PROCFS_ANON_RSS_IN_KILOBYTES, procStatusContents) * BYTES_IN_KILOBYTE; - memoryStat.swapInBytes = - tryParseLong(PROCFS_SWAP_IN_KILOBYTES, procStatusContents) * BYTES_IN_KILOBYTE; - memoryStat.startTimeNanos = Long.parseLong(splits[START_TIME_INDEX]) * JIFFY_NANOS; + memoryStat.rssInBytes = Long.parseLong(splits[RSS_IN_PAGES_INDEX]) * PAGE_SIZE; return memoryStat; } catch (NumberFormatException e) { Slog.e(TAG, "Failed to parse value", e); @@ -193,23 +157,6 @@ public final class MemoryStatUtil { } /** - * Parses cmdline out of the contents of the /proc/pid/cmdline file in procfs. - * - * Parsing is required to strip anything after first null byte. - */ - @VisibleForTesting - static String parseCmdlineFromProcfs(String cmdline) { - if (cmdline == null) { - return ""; - } - int firstNullByte = cmdline.indexOf("\0"); - if (firstNullByte == -1) { - return cmdline; - } - return cmdline.substring(0, firstNullByte); - } - - /** * Returns whether per-app memcg is available on device. */ static boolean hasMemcg() { @@ -237,13 +184,9 @@ public final class MemoryStatUtil { public long pgmajfault; /** For memcg stats, the anon rss + swap cache size. Otherwise total RSS. */ public long rssInBytes; - /** Number of bytes of the anonymous RSS. Only present for non-memcg stats. */ - public long anonRssInBytes; /** Number of bytes of page cache memory. Only present for memcg stats. */ public long cacheInBytes; /** Number of bytes of swap usage */ public long swapInBytes; - /** Device time when the processes started. */ - public long startTimeNanos; } } diff --git a/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java b/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java index 2b25b8921540..8431ae4aeb98 100644 --- a/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java +++ b/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java @@ -72,6 +72,30 @@ final class ProcfsMemoryUtil { return null; } + /** + * 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. + */ + public static String readCmdlineFromProcfs(int pid) { + return parseCmdline(readFile("/proc/" + pid + "/cmdline")); + } + + /** + * Parses cmdline out of the contents of the /proc/pid/cmdline file in procfs. + * + * Parsing is required to strip anything after the first null byte. + */ + @VisibleForTesting + static String parseCmdline(String contents) { + int firstNullByte = contents.indexOf("\0"); + if (firstNullByte == -1) { + return contents; + } + return contents.substring(0, firstNullByte); + } + private static String readFile(String path) { try { final File file = new File(path); diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 67830a930caf..1d3ac6a9f14f 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -24,11 +24,10 @@ import static android.os.storage.VolumeInfo.TYPE_PRIVATE; import static android.os.storage.VolumeInfo.TYPE_PUBLIC; 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.stats.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs; import static com.android.server.stats.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs; +import static com.android.server.stats.ProcfsMemoryUtil.readCmdlineFromProcfs; import static com.android.server.stats.ProcfsMemoryUtil.readMemorySnapshotFromProcfs; import android.annotation.NonNull; @@ -1193,48 +1192,13 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { e.writeLong(memoryStat.rssInBytes); e.writeLong(memoryStat.cacheInBytes); e.writeLong(memoryStat.swapInBytes); - e.writeLong(0); // unused - e.writeLong(memoryStat.startTimeNanos); - e.writeInt(anonAndSwapInKilobytes(memoryStat)); + e.writeLong(-1); // unused + e.writeLong(-1); // unused + e.writeInt(-1); // unsed pulledData.add(e); } } - private void pullNativeProcessMemoryState( - int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - int[] pids = getPidsForCommands(MEMORY_INTERESTING_NATIVE_PROCESSES); - for (int pid : pids) { - String processName = readCmdlineFromProcfs(pid); - MemoryStat memoryStat = readMemoryStatFromProcfs(pid); - if (memoryStat == null) { - continue; - } - int uid = getUidForPid(pid); - // Sometimes we get here a process that is not included in the whitelist. It comes - // from forking the zygote for an app. We can ignore that sample because this process - // is collected by ProcessMemoryState. - if (isAppUid(uid)) { - continue; - } - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(uid); - e.writeString(processName); - e.writeLong(memoryStat.pgfault); - e.writeLong(memoryStat.pgmajfault); - e.writeLong(memoryStat.rssInBytes); - e.writeLong(0); // unused - e.writeLong(memoryStat.startTimeNanos); - e.writeLong(memoryStat.swapInBytes); - e.writeInt(anonAndSwapInKilobytes(memoryStat)); - pulledData.add(e); - } - } - - private static int anonAndSwapInKilobytes(MemoryStat memoryStat) { - return (int) ((memoryStat.anonRssInBytes + memoryStat.swapInBytes) / 1024); - } - private void pullProcessMemoryHighWaterMark( int tagId, long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) { @@ -2405,10 +2369,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pullProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret); break; } - case StatsLog.NATIVE_PROCESS_MEMORY_STATE: { - pullNativeProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret); - break; - } case StatsLog.PROCESS_MEMORY_HIGH_WATER_MARK: { pullProcessMemoryHighWaterMark(tagId, elapsedNanos, wallClockNanos, ret); break; diff --git a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java index 9e3b54d1ca96..c3a1243f75af 100644 --- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java +++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java @@ -16,10 +16,8 @@ package com.android.server.am; -import static com.android.server.am.MemoryStatUtil.BYTES_IN_KILOBYTE; -import static com.android.server.am.MemoryStatUtil.JIFFY_NANOS; import static com.android.server.am.MemoryStatUtil.MemoryStat; -import static com.android.server.am.MemoryStatUtil.parseCmdlineFromProcfs; +import static com.android.server.am.MemoryStatUtil.PAGE_SIZE; import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromMemcg; import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromProcfs; @@ -30,7 +28,6 @@ import androidx.test.filters.SmallTest; import org.junit.Test; -import java.io.ByteArrayOutputStream; import java.util.Collections; /** @@ -99,7 +96,7 @@ public class MemoryStatUtilTest { "0", "2222", // this in start time (in ticks per second) "1257177088", - "3", + "3", // this is RSS in pages "4294967295", "2936971264", "2936991289", @@ -129,53 +126,6 @@ public class MemoryStatUtilTest { "3198889956", "0"); - private static final String PROC_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 testParseMemoryStatFromMemcg_parsesCorrectValues() { MemoryStat stat = parseMemoryStatFromMemcg(MEMORY_STAT_CONTENTS); @@ -197,71 +147,26 @@ public class MemoryStatUtilTest { @Test public void testParseMemoryStatFromProcfs_parsesCorrectValues() { - MemoryStat stat = parseMemoryStatFromProcfs(PROC_STAT_CONTENTS, PROC_STATUS_CONTENTS); + MemoryStat stat = parseMemoryStatFromProcfs(PROC_STAT_CONTENTS); assertEquals(1, stat.pgfault); assertEquals(2, stat.pgmajfault); - assertEquals(126776 * BYTES_IN_KILOBYTE, stat.rssInBytes); + assertEquals(3 * PAGE_SIZE, stat.rssInBytes); assertEquals(0, stat.cacheInBytes); - assertEquals(22 * BYTES_IN_KILOBYTE, stat.swapInBytes); - assertEquals(2222 * JIFFY_NANOS, stat.startTimeNanos); - assertEquals(37860 * BYTES_IN_KILOBYTE, stat.anonRssInBytes); + assertEquals(0, stat.swapInBytes); } @Test public void testParseMemoryStatFromProcfs_emptyContents() { - MemoryStat stat = parseMemoryStatFromProcfs("", PROC_STATUS_CONTENTS); - assertNull(stat); - - stat = parseMemoryStatFromProcfs(null, PROC_STATUS_CONTENTS); + MemoryStat stat = parseMemoryStatFromProcfs(""); assertNull(stat); - stat = parseMemoryStatFromProcfs(PROC_STAT_CONTENTS, ""); - assertNull(stat); - - stat = parseMemoryStatFromProcfs(PROC_STAT_CONTENTS, null); + stat = parseMemoryStatFromProcfs(null); assertNull(stat); } @Test public void testParseMemoryStatFromProcfs_invalidValue() { String contents = String.join(" ", Collections.nCopies(24, "memory")); - assertNull(parseMemoryStatFromProcfs(contents, PROC_STATUS_CONTENTS)); - } - - @Test - public void testParseCmdlineFromProcfs_invalidValue() { - byte[] nothing = new byte[] {0x00, 0x74, 0x65, 0x73, 0x74}; // \0test - - assertEquals("", parseCmdlineFromProcfs(bytesToString(nothing))); - } - - @Test - public void testParseCmdlineFromProcfs_correctValue_noNullBytes() { - assertEquals("com.google.app", parseCmdlineFromProcfs("com.google.app")); - } - - @Test - public void testParseCmdlineFromProcfs_correctValue_withNullBytes() { - byte[] trailing = new byte[] {0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00}; // test\0\0\0 - - assertEquals("test", parseCmdlineFromProcfs(bytesToString(trailing))); - - // test\0\0test - byte[] inside = new byte[] {0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74}; - - assertEquals("test", parseCmdlineFromProcfs(bytesToString(trailing))); - } - - @Test - public void testParseCmdlineFromProcfs_emptyContents() { - assertEquals("", parseCmdlineFromProcfs("")); - - assertEquals("", parseCmdlineFromProcfs(null)); - } - - private static String bytesToString(byte[] bytes) { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - output.write(bytes, 0, bytes.length); - return output.toString(); + assertNull(parseMemoryStatFromProcfs(contents)); } } diff --git a/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java b/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java index dd2ee5cce13b..d1ac19c540a4 100644 --- a/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java +++ b/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java @@ -15,6 +15,7 @@ */ package com.android.server.stats; +import static com.android.server.stats.ProcfsMemoryUtil.parseCmdline; import static com.android.server.stats.ProcfsMemoryUtil.parseMemorySnapshotFromStatus; import static com.google.common.truth.Truth.assertThat; @@ -25,6 +26,8 @@ import com.android.server.stats.ProcfsMemoryUtil.MemorySnapshot; import org.junit.Test; +import java.io.ByteArrayOutputStream; + /** * Build/Install/Run: * atest FrameworksServicesTests:ProcfsMemoryUtilTest @@ -100,4 +103,39 @@ public class ProcfsMemoryUtilTest { MemorySnapshot snapshot = parseMemorySnapshotFromStatus(""); assertThat(snapshot).isNull(); } + + @Test + public void testParseCmdline_invalidValue() { + byte[] nothing = new byte[] {0x00, 0x74, 0x65, 0x73, 0x74}; // \0test + + assertThat(parseCmdline(bytesToString(nothing))).isEmpty(); + } + + @Test + public void testParseCmdline_correctValue_noNullBytes() { + assertThat(parseCmdline("com.google.app")).isEqualTo("com.google.app"); + } + + @Test + public void testParseCmdline_correctValue_withNullBytes() { + byte[] trailing = new byte[] {0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00}; // test\0\0\0 + + assertThat(parseCmdline(bytesToString(trailing))).isEqualTo("test"); + + // test\0\0test + byte[] inside = new byte[] {0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74}; + + assertThat(parseCmdline(bytesToString(trailing))).isEqualTo("test"); + } + + @Test + public void testParseCmdline_emptyContents() { + assertThat(parseCmdline("")).isEmpty(); + } + + private static String bytesToString(byte[] bytes) { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + output.write(bytes, 0, bytes.length); + return output.toString(); + } } |