blob: e8eb4eda834f08580b96bd7f8d7a63d5fc56b813 [file] [log] [blame]
The Android Open Source Projecte16cb842009-03-03 19:32:58 -08001/*
2 * Copyright (C) 2008 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 <errno.h>
18#include <stdint.h>
19#include <stdio.h>
20#include <stdlib.h>
Elliott Hughes8678c6f2015-01-29 21:26:35 -080021#include <string.h>
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080022
23#include <pagemap/pagemap.h>
24
25/* Information about a single mapping */
26struct map_info {
27 pm_map_t *map;
28 pm_memusage_t usage;
29 /* page counts */
30 unsigned long shared_clean;
31 unsigned long shared_dirty;
32 unsigned long private_clean;
33 unsigned long private_dirty;
34};
35
36/* display the help screen */
37static void usage(const char *cmd);
38
39/* qsort compare function to compare maps by PSS */
40int comp_pss(const void *a, const void *b);
41
Sandeep Patil533cee52018-08-16 13:00:01 -070042/* qsort compare function to compare maps by USS */
43int comp_uss(const void* a, const void* b);
44
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080045int main(int argc, char *argv[]) {
46 pid_t pid;
47
48 /* libpagemap context */
49 pm_kernel_t *ker;
50 int pagesize; /* cached for speed */
51 pm_process_t *proc;
52
53 /* maps and such */
Ashok Bhat10e29de2013-01-10 16:24:59 +000054 pm_map_t **maps; size_t num_maps;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080055
Chih-Hung Hsieha57463f2016-03-04 10:14:32 -080056 struct map_info **mis = NULL;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080057 struct map_info *mi;
58
59 /* pagemap information */
Ashok Bhat10e29de2013-01-10 16:24:59 +000060 uint64_t *pagemap; size_t num_pages;
Chih-Hung Hsieha57463f2016-03-04 10:14:32 -080061 uint64_t mapentry;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080062 uint64_t count, flags;
63
64 /* totals */
65 unsigned long total_shared_clean, total_shared_dirty, total_private_clean, total_private_dirty;
66 pm_memusage_t total_usage;
67
68 /* command-line options */
69 int ws;
70#define WS_OFF (0)
71#define WS_ONLY (1)
72#define WS_RESET (2)
73 int (*compfn)(const void *a, const void *b);
74 int hide_zeros;
75
Sandeep Patilac5018d2018-08-15 13:24:09 -070076 /* Use kernel's idle page tracking interface for working set determination */
77 int use_pageidle;
78
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080079 /* temporary variables */
Ashok Bhat10e29de2013-01-10 16:24:59 +000080 size_t i, j;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080081 char *endptr;
82 int error;
83
84 if (argc < 2) {
85 usage(argv[0]);
86 exit(EXIT_FAILURE);
87 }
88
89 ws = WS_OFF;
90 compfn = NULL;
91 hide_zeros = 0;
Sandeep Patilac5018d2018-08-15 13:24:09 -070092 use_pageidle = 0;
Ashok Bhat10e29de2013-01-10 16:24:59 +000093 for (i = 1; i < (size_t)(argc - 1); i++) {
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080094 if (!strcmp(argv[i], "-w")) { ws = WS_ONLY; continue; }
95 if (!strcmp(argv[i], "-W")) { ws = WS_RESET; continue; }
Sandeep Patilac5018d2018-08-15 13:24:09 -070096 if (!strcmp(argv[i], "-i")) {
97 use_pageidle = 1;
98 continue;
99 }
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800100 if (!strcmp(argv[i], "-m")) { compfn = NULL; continue; }
101 if (!strcmp(argv[i], "-p")) { compfn = &comp_pss; continue; }
Sandeep Patil533cee52018-08-16 13:00:01 -0700102 if (!strcmp(argv[i], "-u")) {
103 compfn = &comp_uss;
104 continue;
105 }
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800106 if (!strcmp(argv[i], "-h")) { hide_zeros = 1; continue; }
107 fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]);
108 usage(argv[0]);
109 exit(EXIT_FAILURE);
110 }
111
112 pid = (pid_t)strtol(argv[argc - 1], &endptr, 10);
113 if (*endptr != '\0') {
114 fprintf(stderr, "Invalid PID \"%s\".\n", argv[argc - 1]);
115 exit(EXIT_FAILURE);
116 }
117
118 error = pm_kernel_create(&ker);
119 if (error) {
120 fprintf(stderr, "error creating kernel interface -- "
121 "does this kernel have pagemap?\n");
122 exit(EXIT_FAILURE);
123 }
124
Sandeep Patilac5018d2018-08-15 13:24:09 -0700125 if (ws != WS_OFF && use_pageidle) {
126 error = pm_kernel_init_page_idle(ker);
127 if (error) {
128 fprintf(stderr,
129 "error initalizing idle page tracking -- "
130 "enable CONFIG_IDLE_PAGE_TRACKING in kernel.\n");
131 exit(EXIT_FAILURE);
132 }
133 }
134
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800135 pagesize = pm_kernel_pagesize(ker);
136
137 error = pm_process_create(ker, pid, &proc);
138 if (error) {
139 fprintf(stderr, "error creating process interface -- "
140 "does process %d really exist?\n", pid);
141 exit(EXIT_FAILURE);
142 }
143
Sandeep Patil0f595cb2018-08-16 11:55:56 -0700144 if (ws != WS_OFF) {
145 /*
146 * The idle page tracking interface will update the PageIdle flags
147 * upon writing. So, even if we are called only to read the *current*
148 * working set, we need to reset the bitmap to make sure we get
149 * the updated page idle flags. This is not true with the 'clear_refs'
150 * implementation.
151 */
152 if (ws == WS_RESET || use_pageidle) {
153 error = pm_process_workingset(proc, NULL, 1);
154 if (error) {
155 fprintf(stderr, "error resetting working set for process.\n");
156 exit(EXIT_FAILURE);
157 }
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800158 }
Sandeep Patil0f595cb2018-08-16 11:55:56 -0700159 if (ws == WS_RESET) exit(EXIT_SUCCESS);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800160 }
161
162 /* get maps, and allocate our map_info array */
163 error = pm_process_maps(proc, &maps, &num_maps);
164 if (error) {
165 fprintf(stderr, "error listing maps.\n");
166 exit(EXIT_FAILURE);
167 }
168
169 mis = (struct map_info **)calloc(num_maps, sizeof(struct map_info *));
170 if (!mis) {
171 fprintf(stderr, "error allocating map_info array: %s\n", strerror(errno));
172 exit(EXIT_FAILURE);
173 }
174
175 /* print header */
176 if (ws == WS_ONLY) {
177 printf("%7s %7s %7s %7s %7s %7s %7s %s\n",
178 "WRss", "WPss", "WUss", "WShCl", "WShDi", "WPrCl", "WPrDi", "Name");
179 printf("%7s %7s %7s %7s %7s %7s %7s %s\n",
180 "-------", "-------", "-------", "-------", "-------", "-------", "-------", "");
181 } else {
182 printf("%7s %7s %7s %7s %7s %7s %7s %7s %s\n",
183 "Vss", "Rss", "Pss", "Uss", "ShCl", "ShDi", "PrCl", "PrDi", "Name");
184 printf("%7s %7s %7s %7s %7s %7s %7s %7s %s\n",
185 "-------", "-------", "-------", "-------", "-------", "-------", "-------", "-------", "");
186 }
187
188 /* zero things */
189 pm_memusage_zero(&total_usage);
190 total_shared_clean = total_shared_dirty = total_private_clean = total_private_dirty = 0;
191
192 for (i = 0; i < num_maps; i++) {
193 mi = (struct map_info *)calloc(1, sizeof(struct map_info));
194 if (!mi) {
195 fprintf(stderr, "error allocating map_info: %s\n", strerror(errno));
196 exit(EXIT_FAILURE);
197 }
198
199 mi->map = maps[i];
200
201 /* get, and sum, memory usage */
202
203 if (ws == WS_ONLY)
204 error = pm_map_workingset(mi->map, &mi->usage);
205 else
206 error = pm_map_usage(mi->map, &mi->usage);
207 if (error) {
208 fflush(stdout);
209 fprintf(stderr, "error getting usage for map.\n");
210 continue;
211 }
212
213 pm_memusage_add(&total_usage, &mi->usage);
214
215 /* get, and sum, individual page counts */
216
217 error = pm_map_pagemap(mi->map, &pagemap, &num_pages);
218 if (error) {
219 fflush(stdout);
220 fprintf(stderr, "error getting pagemap for map.\n");
221 continue;
222 }
223
224 mi->shared_clean = mi->shared_dirty = mi->private_clean = mi->private_dirty = 0;
225
226 for (j = 0; j < num_pages; j++) {
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800227 mapentry = pagemap[j];
228
229 if (PM_PAGEMAP_PRESENT(mapentry) && !PM_PAGEMAP_SWAPPED(mapentry)) {
230
231 error = pm_kernel_count(ker, PM_PAGEMAP_PFN(mapentry), &count);
232 if (error) {
233 fflush(stdout);
234 fprintf(stderr, "error getting count for frame.\n");
235 }
236
237 error = pm_kernel_flags(ker, PM_PAGEMAP_PFN(mapentry), &flags);
238 if (error) {
239 fflush(stdout);
240 fprintf(stderr, "error getting flags for frame.\n");
241 }
242
Sandeep Patilac5018d2018-08-15 13:24:09 -0700243 if ((ws != WS_ONLY) ||
244 pm_kernel_page_is_accessed(ker, PM_PAGEMAP_PFN(mapentry), &flags)) {
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800245 if (count > 1) {
Elliott Hughes509d9bb2018-04-12 17:04:38 -0700246 if (flags & (1 << KPF_DIRTY)) {
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800247 mi->shared_dirty++;
Elliott Hughes509d9bb2018-04-12 17:04:38 -0700248 } else {
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800249 mi->shared_clean++;
Elliott Hughes509d9bb2018-04-12 17:04:38 -0700250 }
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800251 } else {
Elliott Hughes509d9bb2018-04-12 17:04:38 -0700252 if (flags & (1 << KPF_DIRTY)) {
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800253 mi->private_dirty++;
Elliott Hughes509d9bb2018-04-12 17:04:38 -0700254 } else {
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800255 mi->private_clean++;
Elliott Hughes509d9bb2018-04-12 17:04:38 -0700256 }
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800257 }
258 }
259 }
260 }
261
262 total_shared_clean += mi->shared_clean;
263 total_shared_dirty += mi->shared_dirty;
264 total_private_clean += mi->private_clean;
265 total_private_dirty += mi->private_dirty;
266
267 /* add to array */
268 mis[i] = mi;
269 }
270
271 /* sort the array, if requested (compfn == NULL for original order) */
272 if (compfn)
273 qsort(mis, num_maps, sizeof(mis[0]), compfn);
274
275 for (i = 0; i < num_maps; i++) {
276 mi = mis[i];
277
Hong-Mei Lic4c464f2013-04-01 11:28:47 +0800278 if ((!mi) || (hide_zeros && !mi->usage.rss))
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800279 continue;
280
281 if (ws == WS_ONLY) {
282 printf("%6ldK %6ldK %6ldK %6ldK %6ldK %6ldK %6ldK %s\n",
283 (long)mi->usage.rss / 1024,
284 (long)mi->usage.pss / 1024,
285 (long)mi->usage.uss / 1024,
286 mi->shared_clean * pagesize / 1024,
287 mi->shared_dirty * pagesize / 1024,
288 mi->private_clean * pagesize / 1024,
289 mi->private_dirty * pagesize / 1024,
290 pm_map_name(mi->map)
291 );
292 } else {
293 printf("%6ldK %6ldK %6ldK %6ldK %6ldK %6ldK %6ldK %6ldK %s\n",
294 (long)mi->usage.vss / 1024,
295 (long)mi->usage.rss / 1024,
296 (long)mi->usage.pss / 1024,
297 (long)mi->usage.uss / 1024,
298 mi->shared_clean * pagesize / 1024,
299 mi->shared_dirty * pagesize / 1024,
300 mi->private_clean * pagesize / 1024,
301 mi->private_dirty * pagesize / 1024,
302 pm_map_name(mi->map)
303 );
304 }
305 }
306
307 /* print totals */
308 if (ws == WS_ONLY) {
309 printf("%7s %7s %7s %7s %7s %7s %7s %s\n",
310 "-------", "-------", "-------", "-------", "-------", "-------", "-------", "");
311 printf("%6ldK %6ldK %6ldK %6ldK %6ldK %6ldK %6ldK %s\n",
312 (long)total_usage.rss / 1024,
313 (long)total_usage.pss / 1024,
314 (long)total_usage.uss / 1024,
315 total_shared_clean * pagesize / 1024,
316 total_shared_dirty * pagesize / 1024,
317 total_private_clean * pagesize / 1024,
318 total_private_dirty * pagesize / 1024,
319 "TOTAL"
320 );
321 } else {
322 printf("%7s %7s %7s %7s %7s %7s %7s %7s %s\n",
323 "-------", "-------", "-------", "-------", "-------", "-------", "-------", "-------", "");
324 printf("%6ldK %6ldK %6ldK %6ldK %6ldK %6ldK %6ldK %6ldK %s\n",
325 (long)total_usage.vss / 1024,
326 (long)total_usage.rss / 1024,
327 (long)total_usage.pss / 1024,
328 (long)total_usage.uss / 1024,
329 total_shared_clean * pagesize / 1024,
330 total_shared_dirty * pagesize / 1024,
331 total_private_clean * pagesize / 1024,
332 total_private_dirty * pagesize / 1024,
333 "TOTAL"
334 );
335 }
336
Chih-Hung Hsieha57463f2016-03-04 10:14:32 -0800337 free(mis);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800338 return 0;
339}
340
Sandeep Patil533cee52018-08-16 13:00:01 -0700341static void usage(const char* cmd) {
Sandeep Patilac5018d2018-08-15 13:24:09 -0700342 fprintf(stderr,
343 "Usage: %s [-i] [ -w | -W ] [ -p | -m ] [ -h ] pid\n"
344 " -i Uses idle page tracking for working set statistics.\n"
345 " -w Displays statistics for the working set only.\n"
346 " -W Resets the working set of the process.\n"
347 " -p Sort by PSS.\n"
Sandeep Patil533cee52018-08-16 13:00:01 -0700348 " -u Sort by USS.\n"
Sandeep Patilac5018d2018-08-15 13:24:09 -0700349 " -m Sort by mapping order (as read from /proc).\n"
350 " -h Hide maps with no RSS.\n",
351 cmd);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800352}
353
354int comp_pss(const void *a, const void *b) {
355 struct map_info *ma, *mb;
356
357 ma = *((struct map_info **)a);
358 mb = *((struct map_info **)b);
359
360 if (mb->usage.pss < ma->usage.pss) return -1;
361 if (mb->usage.pss > ma->usage.pss) return 1;
362 return 0;
363}
Sandeep Patil533cee52018-08-16 13:00:01 -0700364
365int comp_uss(const void* a, const void* b) {
366 struct map_info *ma, *mb;
367
368 ma = *((struct map_info**)a);
369 mb = *((struct map_info**)b);
370
371 if (mb->usage.uss < ma->usage.uss) return -1;
372 if (mb->usage.uss > ma->usage.uss) return 1;
373 return 0;
374}