blob: aad890b8bd7481916efd43de613024abef7523c3 [file] [log] [blame]
Ng Zhi Anbbefdec2018-01-30 17:12:39 -08001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.am;
18
19import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_METRICS;
20import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
21import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
22
23import android.annotation.Nullable;
24import android.os.FileUtils;
Rafal Slawik07b60cd2018-09-13 11:34:24 +010025import android.os.SystemProperties;
Ng Zhi Anbbefdec2018-01-30 17:12:39 -080026import android.util.Slog;
27
28import com.android.internal.annotations.VisibleForTesting;
29
30import java.io.File;
31import java.io.IOException;
Rajeev Kumar22d92b72018-02-07 18:38:36 -080032import java.util.Locale;
Ng Zhi Anbbefdec2018-01-30 17:12:39 -080033import java.util.regex.Matcher;
34import java.util.regex.Pattern;
35
36/**
37 * Static utility methods related to {@link MemoryStat}.
38 */
39final class MemoryStatUtil {
40 private static final String TAG = TAG_WITH_CLASS_NAME ? "MemoryStatUtil" : TAG_AM;
41
Rafal Slawik07b60cd2018-09-13 11:34:24 +010042 /** True if device has per-app memcg */
43 private static final Boolean DEVICE_HAS_PER_APP_MEMCG =
44 SystemProperties.getBoolean("ro.config.per_app_memcg", false);
45
Rajeev Kumarbfcd9202018-03-02 22:42:13 +000046 /** Path to check if device has memcg */
47 private static final String MEMCG_TEST_PATH = "/dev/memcg/apps/memory.stat";
Ng Zhi Anbbefdec2018-01-30 17:12:39 -080048 /** Path to memory stat file for logging app start memory state */
49 private static final String MEMORY_STAT_FILE_FMT = "/dev/memcg/apps/uid_%d/pid_%d/memory.stat";
Rajeev Kumarbfcd9202018-03-02 22:42:13 +000050 /** Path to procfs stat file for logging app start memory state */
51 private static final String PROC_STAT_FILE_FMT = "/proc/%d/stat";
Ng Zhi Anbbefdec2018-01-30 17:12:39 -080052
53 private static final Pattern PGFAULT = Pattern.compile("total_pgfault (\\d+)");
54 private static final Pattern PGMAJFAULT = Pattern.compile("total_pgmajfault (\\d+)");
55 private static final Pattern RSS_IN_BYTES = Pattern.compile("total_rss (\\d+)");
56 private static final Pattern CACHE_IN_BYTES = Pattern.compile("total_cache (\\d+)");
57 private static final Pattern SWAP_IN_BYTES = Pattern.compile("total_swap (\\d+)");
58
Rajeev Kumarbfcd9202018-03-02 22:42:13 +000059 private static final int PGFAULT_INDEX = 9;
60 private static final int PGMAJFAULT_INDEX = 11;
61 private static final int RSS_IN_BYTES_INDEX = 23;
62
Ng Zhi Anbbefdec2018-01-30 17:12:39 -080063 private MemoryStatUtil() {}
64
65 /**
Rajeev Kumarbfcd9202018-03-02 22:42:13 +000066 * Reads memory stat for a process.
67 *
Rafal Slawik07b60cd2018-09-13 11:34:24 +010068 * Reads from per-app memcg if available on device, else fallback to procfs.
Rajeev Kumarbfcd9202018-03-02 22:42:13 +000069 * Returns null if no stats can be read.
70 */
71 @Nullable
72 static MemoryStat readMemoryStatFromFilesystem(int uid, int pid) {
73 return hasMemcg() ? readMemoryStatFromMemcg(uid, pid) : readMemoryStatFromProcfs(pid);
74 }
75
76 /**
Ng Zhi Anbbefdec2018-01-30 17:12:39 -080077 * Reads memory.stat of a process from memcg.
Rajeev Kumarbfcd9202018-03-02 22:42:13 +000078 *
79 * Returns null if file is not found in memcg or if file has unrecognized contents.
Ng Zhi Anbbefdec2018-01-30 17:12:39 -080080 */
Rajeev Kumar22d92b72018-02-07 18:38:36 -080081 @Nullable
82 static MemoryStat readMemoryStatFromMemcg(int uid, int pid) {
Rajeev Kumarbfcd9202018-03-02 22:42:13 +000083 final String path = String.format(Locale.US, MEMORY_STAT_FILE_FMT, uid, pid);
84 return parseMemoryStatFromMemcg(readFileContents(path));
85 }
86
87 /**
88 * Reads memory stat of a process from procfs.
89 *
90 * Returns null if file is not found in procfs or if file has unrecognized contents.
91 */
92 @Nullable
93 static MemoryStat readMemoryStatFromProcfs(int pid) {
94 final String path = String.format(Locale.US, PROC_STAT_FILE_FMT, pid);
95 return parseMemoryStatFromProcfs(readFileContents(path));
96 }
97
98 private static String readFileContents(String path) {
99 final File file = new File(path);
100 if (!file.exists()) {
101 if (DEBUG_METRICS) Slog.i(TAG, path + " not found");
Ng Zhi Anbbefdec2018-01-30 17:12:39 -0800102 return null;
103 }
104
105 try {
Rajeev Kumarbfcd9202018-03-02 22:42:13 +0000106 return FileUtils.readTextFile(file, 0 /* max */, null /* ellipsis */);
Ng Zhi Anbbefdec2018-01-30 17:12:39 -0800107 } catch (IOException e) {
108 Slog.e(TAG, "Failed to read file:", e);
109 return null;
110 }
111 }
112
113 /**
114 * Parses relevant statistics out from the contents of a memory.stat file in memcg.
115 */
116 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
Rajeev Kumar22d92b72018-02-07 18:38:36 -0800117 @Nullable
Rajeev Kumarbfcd9202018-03-02 22:42:13 +0000118 static MemoryStat parseMemoryStatFromMemcg(String memoryStatContents) {
119 if (memoryStatContents == null || memoryStatContents.isEmpty()) {
120 return null;
Ng Zhi Anbbefdec2018-01-30 17:12:39 -0800121 }
122
Rajeev Kumarbfcd9202018-03-02 22:42:13 +0000123 final MemoryStat memoryStat = new MemoryStat();
Ng Zhi Anbbefdec2018-01-30 17:12:39 -0800124 Matcher m;
125 m = PGFAULT.matcher(memoryStatContents);
126 memoryStat.pgfault = m.find() ? Long.valueOf(m.group(1)) : 0;
127 m = PGMAJFAULT.matcher(memoryStatContents);
128 memoryStat.pgmajfault = m.find() ? Long.valueOf(m.group(1)) : 0;
129 m = RSS_IN_BYTES.matcher(memoryStatContents);
130 memoryStat.rssInBytes = m.find() ? Long.valueOf(m.group(1)) : 0;
131 m = CACHE_IN_BYTES.matcher(memoryStatContents);
132 memoryStat.cacheInBytes = m.find() ? Long.valueOf(m.group(1)) : 0;
133 m = SWAP_IN_BYTES.matcher(memoryStatContents);
134 memoryStat.swapInBytes = m.find() ? Long.valueOf(m.group(1)) : 0;
135 return memoryStat;
136 }
137
Rajeev Kumarbfcd9202018-03-02 22:42:13 +0000138 /**
139 * Parses relevant statistics out from the contents of a /proc/pid/stat file in procfs.
140 */
141 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
142 @Nullable
143 static MemoryStat parseMemoryStatFromProcfs(String procStatContents) {
144 if (procStatContents == null || procStatContents.isEmpty()) {
145 return null;
146 }
147
148 final String[] splits = procStatContents.split(" ");
149 if (splits.length < 24) {
150 return null;
151 }
152
153 final MemoryStat memoryStat = new MemoryStat();
154 memoryStat.pgfault = Long.valueOf(splits[PGFAULT_INDEX]);
155 memoryStat.pgmajfault = Long.valueOf(splits[PGMAJFAULT_INDEX]);
156 memoryStat.rssInBytes = Long.valueOf(splits[RSS_IN_BYTES_INDEX]);
157 return memoryStat;
158 }
159
160 /**
Rafal Slawik07b60cd2018-09-13 11:34:24 +0100161 * Returns whether per-app memcg is available on device.
Rajeev Kumarbfcd9202018-03-02 22:42:13 +0000162 */
163 static boolean hasMemcg() {
Rafal Slawik07b60cd2018-09-13 11:34:24 +0100164 return DEVICE_HAS_PER_APP_MEMCG;
Rajeev Kumarbfcd9202018-03-02 22:42:13 +0000165 }
166
Ng Zhi Anbbefdec2018-01-30 17:12:39 -0800167 static final class MemoryStat {
168 /** Number of page faults */
169 long pgfault;
170 /** Number of major page faults */
171 long pgmajfault;
172 /** Number of bytes of anonymous and swap cache memory */
173 long rssInBytes;
174 /** Number of bytes of page cache memory */
175 long cacheInBytes;
176 /** Number of bytes of swap usage */
177 long swapInBytes;
178 }
Ng Zhi An597871a2018-02-22 14:06:33 -0800179}