blob: 76686408a30697b202b434e541ed95d96d9bf550 [file] [log] [blame]
Sandeep Patil2aeaaeb2018-11-23 00:13:16 -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
17#include <dirent.h>
18#include <errno.h>
19#include <error.h>
20#include <inttypes.h>
21#include <linux/kernel-page-flags.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <sys/mman.h>
25#include <sys/types.h>
26#include <unistd.h>
27
28#include <algorithm>
29#include <memory>
30#include <vector>
31
32#include <android-base/file.h>
33#include <android-base/parseint.h>
34#include <android-base/stringprintf.h>
35#include <android-base/strings.h>
36
37#include <meminfo/procmeminfo.h>
38
39using ::android::meminfo::MemUsage;
40using ::android::meminfo::ProcMemInfo;
41using ::android::meminfo::Vma;
42
43[[noreturn]] static void usage(const char* myname, int exit_status) {
44 fprintf(stderr,
45 "Usage: %s [ -P | -L ] [ -v | -r | -p | -u | -s | -h ]\n"
46 "\n"
47 "Sort options:\n"
48 " -v Sort processes by VSS.\n"
49 " -r Sort processes by RSS.\n"
50 " -p Sort processes by PSS.\n"
51 " -u Sort processes by USS.\n"
52 " -s Sort processes by swap.\n"
53 " (Default sort order is PSS.)\n"
54 " -a Show all mappings, including stack, heap and anon.\n"
55 " -P /path Limit libraries displayed to those in path.\n"
56 " -R Reverse sort order (default is descending).\n"
57 " -m [r][w][x] Only list pages that exactly match permissions\n"
58 " -c Only show cached (storage backed) pages\n"
59 " -C Only show non-cached (ram/swap backed) pages\n"
60 " -k Only show pages collapsed by KSM\n"
61 " -h Display this help screen.\n",
62 myname);
63 exit(exit_status);
64}
65
66static void add_mem_usage(MemUsage* to, const MemUsage& from) {
67 to->vss += from.vss;
68 to->rss += from.rss;
69 to->pss += from.pss;
70 to->uss += from.uss;
71
72 to->swap += from.swap;
73
74 to->private_clean += from.private_clean;
75 to->private_dirty += from.private_dirty;
76
77 to->shared_clean += from.shared_clean;
78 to->shared_dirty += from.shared_dirty;
79}
80
81struct ProcessRecord {
82 public:
83 ProcessRecord(pid_t pid) : pid_(-1), cmdline_("") {
84 std::string fname = ::android::base::StringPrintf("/proc/%d/cmdline", pid);
85 std::string cmdline;
86 if (!::android::base::ReadFileToString(fname, &cmdline)) {
87 fprintf(stderr, "Failed to read cmdline from: %s\n", fname.c_str());
88 return;
89 }
90 // We deliberately don't read the proc/<pid>cmdline file directly into 'cmdline_'
91 // because of some processes showing up cmdlines that end with "0x00 0x0A 0x00"
92 // e.g. xtra-daemon, lowi-server
93 // The .c_str() assignment below then takes care of trimming the cmdline at the first
94 // 0x00. This is how original procrank worked (luckily)
95 cmdline_ = cmdline.c_str();
96 pid_ = pid;
97 usage_.clear();
98 }
99
100 ~ProcessRecord() = default;
101
102 bool valid() const { return pid_ != -1; }
103
104 // Getters
105 pid_t pid() const { return pid_; }
106 const std::string& cmdline() const { return cmdline_; }
107 const MemUsage& usage() const { return usage_; }
108
109 // Add to the usage
110 void AddUsage(const MemUsage& mem_usage) { add_mem_usage(&usage_, mem_usage); }
111
112 private:
113 pid_t pid_;
114 std::string cmdline_;
115 MemUsage usage_;
116};
117
118struct LibRecord {
119 public:
120 LibRecord(const std::string& name) : name_(name) {}
121 ~LibRecord() = default;
122
123 const std::string& name() const { return name_; }
124 const MemUsage& usage() const { return usage_; }
125 const std::vector<ProcessRecord>& processes() const { return procs_; }
126 uint64_t pss() const { return usage_.pss; }
127 void AddUsage(const ProcessRecord& proc, const MemUsage& mem_usage) {
128 auto process = std::find_if(procs_.begin(), procs_.end(),
129 [&](auto p) -> bool { return p.pid() == proc.pid(); });
130 if (process == procs_.end()) {
131 process = procs_.emplace(procs_.end(), proc.pid());
132 }
133 process->AddUsage(mem_usage);
134 add_mem_usage(&usage_, mem_usage);
135 }
136
137 void Sort(std::function<bool(const ProcessRecord&, const ProcessRecord&)>& sorter) {
138 std::sort(procs_.begin(), procs_.end(), sorter);
139 }
140
141 private:
142 std::string name_;
143 MemUsage usage_;
144 std::vector<ProcessRecord> procs_;
145};
146
147// List of every library / map
148static std::vector<LibRecord> g_libs;
149
150// List of library/map names that we don't want to show by default
151static const std::vector<std::string> g_blacklisted_libs = {"[heap]", "[stack]"};
152
153// Global flags affected by command line
154static uint64_t g_pgflags = 0;
155static uint64_t g_pgflags_mask = 0;
156static uint16_t g_mapflags_mask = 0;
157static bool g_all_libs = false;
158static bool g_has_swap = false;
159static bool g_reverse_sort = false;
160static std::string g_prefix_filter = "";
161
162static bool read_all_pids(std::function<bool(pid_t pid)> for_each_pid) {
163 std::unique_ptr<DIR, int (*)(DIR*)> procdir(opendir("/proc"), closedir);
164 if (!procdir) return false;
165
166 struct dirent* dir;
167 pid_t pid;
168 while ((dir = readdir(procdir.get()))) {
169 if (!::android::base::ParseInt(dir->d_name, &pid)) continue;
170 if (!for_each_pid(pid)) return false;
171 }
172
173 return true;
174}
175
176static bool scan_libs_per_process(pid_t pid) {
177 ProcMemInfo pmem(pid, false, g_pgflags, g_pgflags_mask);
178 const std::vector<Vma> maps = pmem.Maps();
179 if (maps.size() == 0) {
180 // nothing to do here, continue
181 return true;
182 }
183
184 ProcessRecord proc(pid);
185 if (!proc.valid()) {
186 fprintf(stderr, "Failed to create process record for process: %d\n", pid);
187 return false;
188 }
189
190 for (auto& map : maps) {
191 // skip library / map if prefix for the path doesn't match
192 if (!g_prefix_filter.empty() && !::android::base::StartsWith(map.name, g_prefix_filter)) {
193 continue;
194 }
195 // Skip maps based on map permissions
196 if (g_mapflags_mask &&
197 ((map.flags & (PROT_READ | PROT_WRITE | PROT_EXEC)) != g_mapflags_mask)) {
198 continue;
199 }
200
201 // skip blacklisted library / map names
202 if (!g_all_libs && (std::find(g_blacklisted_libs.begin(), g_blacklisted_libs.end(),
203 map.name) != g_blacklisted_libs.end())) {
204 continue;
205 }
206
207 auto lib = std::find_if(g_libs.begin(), g_libs.end(),
208 [&](auto l) -> bool { return map.name == l.name(); });
209 if (lib == g_libs.end()) {
210 lib = g_libs.emplace(g_libs.end(), map.name);
211 }
212
213 lib->AddUsage(proc, map.usage);
214 if (!g_has_swap && map.usage.swap) {
215 g_has_swap = true;
216 }
217 }
218
219 return true;
220}
221
222static uint16_t parse_mapflags(const char* mapflags) {
223 uint16_t ret = 0;
224 for (const char* p = mapflags; *p; p++) {
225 switch (*p) {
226 case 'r':
227 ret |= PROT_READ;
228 break;
229 case 'w':
230 ret |= PROT_WRITE;
231 break;
232 case 'x':
233 ret |= PROT_EXEC;
234 break;
235 default:
236 error(EXIT_FAILURE, 0, "Invalid permissions string: %s, %s", mapflags, p);
237 }
238 }
239
240 return ret;
241}
242
243int main(int argc, char* argv[]) {
244 int opt;
245
246 auto pss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
247 return g_reverse_sort ? a.usage().pss < b.usage().pss : a.usage().pss > b.usage().pss;
248 };
249
250 auto uss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
251 return g_reverse_sort ? a.usage().uss < b.usage().uss : a.usage().uss > b.usage().uss;
252 };
253
254 auto vss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
255 return g_reverse_sort ? a.usage().vss < b.usage().vss : a.usage().vss > b.usage().vss;
256 };
257
258 auto rss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
259 return g_reverse_sort ? a.usage().rss < b.usage().rss : a.usage().rss > b.usage().rss;
260 };
261
262 auto swap_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
263 return g_reverse_sort ? a.usage().swap < b.usage().swap : a.usage().swap > b.usage().swap;
264 };
265
266 std::function<bool(const ProcessRecord&, const ProcessRecord&)> sort_func = pss_sort;
267
268 while ((opt = getopt(argc, argv, "acChkm:pP:uvrsR")) != -1) {
269 switch (opt) {
270 case 'a':
271 g_all_libs = true;
272 break;
273 case 'c':
274 g_pgflags = 0;
275 g_pgflags_mask = (1 << KPF_SWAPBACKED);
276 break;
277 case 'C':
278 g_pgflags = g_pgflags_mask = (1 << KPF_SWAPBACKED);
279 break;
280 case 'h':
281 usage(argv[0], EXIT_SUCCESS);
282 case 'k':
283 g_pgflags = g_pgflags_mask = (1 << KPF_KSM);
284 break;
285 case 'm':
286 g_mapflags_mask = parse_mapflags(optarg);
287 break;
288 case 'p':
289 sort_func = pss_sort;
290 break;
291 case 'P':
292 g_prefix_filter = optarg;
293 break;
294 case 'u':
295 sort_func = uss_sort;
296 break;
297 case 'v':
298 sort_func = vss_sort;
299 break;
300 case 'r':
301 sort_func = rss_sort;
302 break;
303 case 's':
304 sort_func = swap_sort;
305 break;
306 case 'R':
307 g_reverse_sort = true;
308 break;
309 default:
310 usage(argv[0], EXIT_FAILURE);
311 }
312 }
313
314 if (!read_all_pids(scan_libs_per_process)) {
315 error(EXIT_FAILURE, 0, "Failed to read all pids from the system");
316 }
317
318 printf(" %6s %7s %6s %6s %6s ", "RSStot", "VSS", "RSS", "PSS", "USS");
319 if (g_has_swap) {
320 printf(" %6s ", "Swap");
321 }
322 printf("Name/PID\n");
323
324 // sort the libraries by their pss
325 std::sort(g_libs.begin(), g_libs.end(),
326 [](const LibRecord& l1, const LibRecord& l2) { return l1.pss() > l2.pss(); });
327
328 for (auto& lib : g_libs) {
329 printf("%6" PRIu64 "K %7s %6s %6s %6s ", lib.pss() / 1024, "", "", "", "");
330 if (g_has_swap) {
331 printf(" %6s ", "");
332 }
333 printf("%s\n", lib.name().c_str());
334
335 // sort all mappings first
336 lib.Sort(sort_func);
337
338 for (auto& p : lib.processes()) {
339 const MemUsage& usage = p.usage();
340 printf(" %6s %7" PRIu64 "K %6" PRIu64 "K %6" PRIu64 "K %6" PRIu64 "K ", "",
341 usage.vss / 1024, usage.rss / 1024, usage.pss / 1024, usage.uss / 1024);
342 if (g_has_swap) {
343 printf("%6" PRIu64 "K ", usage.swap / 1024);
344 }
345 printf(" %s [%d]\n", p.cmdline().c_str(), p.pid());
346 }
347 }
348
349 return 0;
350}