1 /*
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>
21
22 #include <pagemap/pagemap.h>
23
24 /* Information about a single mapping */
25 struct map_info {
26 pm_map_t *map;
27 pm_memusage_t usage;
28 /* page counts */
29 unsigned long shared_clean;
30 unsigned long shared_dirty;
31 unsigned long private_clean;
32 unsigned long private_dirty;
33 };
34
35 /* display the help screen */
36 static void usage(const char *cmd);
37
38 /* qsort compare function to compare maps by PSS */
39 int comp_pss(const void *a, const void *b);
40
main(int argc,char * argv[])41 int main(int argc, char *argv[]) {
42 pid_t pid;
43
44 /* libpagemap context */
45 pm_kernel_t *ker;
46 int pagesize; /* cached for speed */
47 pm_process_t *proc;
48
49 /* maps and such */
50 pm_map_t **maps; int num_maps;
51
52 struct map_info **mis;
53 struct map_info *mi;
54
55 /* pagemap information */
56 uint64_t *pagemap; int num_pages;
57 unsigned long address; uint64_t mapentry;
58 uint64_t count, flags;
59
60 /* totals */
61 unsigned long total_shared_clean, total_shared_dirty, total_private_clean, total_private_dirty;
62 pm_memusage_t total_usage;
63
64 /* command-line options */
65 int ws;
66 #define WS_OFF (0)
67 #define WS_ONLY (1)
68 #define WS_RESET (2)
69 int (*compfn)(const void *a, const void *b);
70 int hide_zeros;
71
72 /* temporary variables */
73 int i, j;
74 char *endptr;
75 int error;
76
77 if (argc < 2) {
78 usage(argv[0]);
79 exit(EXIT_FAILURE);
80 }
81
82 ws = WS_OFF;
83 compfn = NULL;
84 hide_zeros = 0;
85 for (i = 1; i < argc - 1; i++) {
86 if (!strcmp(argv[i], "-w")) { ws = WS_ONLY; continue; }
87 if (!strcmp(argv[i], "-W")) { ws = WS_RESET; continue; }
88 if (!strcmp(argv[i], "-m")) { compfn = NULL; continue; }
89 if (!strcmp(argv[i], "-p")) { compfn = &comp_pss; continue; }
90 if (!strcmp(argv[i], "-h")) { hide_zeros = 1; continue; }
91 fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]);
92 usage(argv[0]);
93 exit(EXIT_FAILURE);
94 }
95
96 pid = (pid_t)strtol(argv[argc - 1], &endptr, 10);
97 if (*endptr != '\0') {
98 fprintf(stderr, "Invalid PID \"%s\".\n", argv[argc - 1]);
99 exit(EXIT_FAILURE);
100 }
101
102 error = pm_kernel_create(&ker);
103 if (error) {
104 fprintf(stderr, "error creating kernel interface -- "
105 "does this kernel have pagemap?\n");
106 exit(EXIT_FAILURE);
107 }
108
109 pagesize = pm_kernel_pagesize(ker);
110
111 error = pm_process_create(ker, pid, &proc);
112 if (error) {
113 fprintf(stderr, "error creating process interface -- "
114 "does process %d really exist?\n", pid);
115 exit(EXIT_FAILURE);
116 }
117
118 if (ws == WS_RESET) {
119 error = pm_process_workingset(proc, NULL, 1);
120 if (error) {
121 fprintf(stderr, "error resetting working set for process.\n");
122 exit(EXIT_FAILURE);
123 }
124 exit(EXIT_SUCCESS);
125 }
126
127 /* get maps, and allocate our map_info array */
128 error = pm_process_maps(proc, &maps, &num_maps);
129 if (error) {
130 fprintf(stderr, "error listing maps.\n");
131 exit(EXIT_FAILURE);
132 }
133
134 mis = (struct map_info **)calloc(num_maps, sizeof(struct map_info *));
135 if (!mis) {
136 fprintf(stderr, "error allocating map_info array: %s\n", strerror(errno));
137 exit(EXIT_FAILURE);
138 }
139
140 /* print header */
141 if (ws == WS_ONLY) {
142 printf("%7s %7s %7s %7s %7s %7s %7s %s\n",
143 "WRss", "WPss", "WUss", "WShCl", "WShDi", "WPrCl", "WPrDi", "Name");
144 printf("%7s %7s %7s %7s %7s %7s %7s %s\n",
145 "-------", "-------", "-------", "-------", "-------", "-------", "-------", "");
146 } else {
147 printf("%7s %7s %7s %7s %7s %7s %7s %7s %s\n",
148 "Vss", "Rss", "Pss", "Uss", "ShCl", "ShDi", "PrCl", "PrDi", "Name");
149 printf("%7s %7s %7s %7s %7s %7s %7s %7s %s\n",
150 "-------", "-------", "-------", "-------", "-------", "-------", "-------", "-------", "");
151 }
152
153 /* zero things */
154 pm_memusage_zero(&total_usage);
155 total_shared_clean = total_shared_dirty = total_private_clean = total_private_dirty = 0;
156
157 for (i = 0; i < num_maps; i++) {
158 mi = (struct map_info *)calloc(1, sizeof(struct map_info));
159 if (!mi) {
160 fprintf(stderr, "error allocating map_info: %s\n", strerror(errno));
161 exit(EXIT_FAILURE);
162 }
163
164 mi->map = maps[i];
165
166 /* get, and sum, memory usage */
167
168 if (ws == WS_ONLY)
169 error = pm_map_workingset(mi->map, &mi->usage);
170 else
171 error = pm_map_usage(mi->map, &mi->usage);
172 if (error) {
173 fflush(stdout);
174 fprintf(stderr, "error getting usage for map.\n");
175 continue;
176 }
177
178 pm_memusage_add(&total_usage, &mi->usage);
179
180 /* get, and sum, individual page counts */
181
182 error = pm_map_pagemap(mi->map, &pagemap, &num_pages);
183 if (error) {
184 fflush(stdout);
185 fprintf(stderr, "error getting pagemap for map.\n");
186 continue;
187 }
188
189 mi->shared_clean = mi->shared_dirty = mi->private_clean = mi->private_dirty = 0;
190
191 for (j = 0; j < num_pages; j++) {
192 address = pm_map_start(mi->map) + j * ker->pagesize;
193 mapentry = pagemap[j];
194
195 if (PM_PAGEMAP_PRESENT(mapentry) && !PM_PAGEMAP_SWAPPED(mapentry)) {
196
197 error = pm_kernel_count(ker, PM_PAGEMAP_PFN(mapentry), &count);
198 if (error) {
199 fflush(stdout);
200 fprintf(stderr, "error getting count for frame.\n");
201 }
202
203 error = pm_kernel_flags(ker, PM_PAGEMAP_PFN(mapentry), &flags);
204 if (error) {
205 fflush(stdout);
206 fprintf(stderr, "error getting flags for frame.\n");
207 }
208
209 if ((ws != WS_ONLY) || (flags & PM_PAGE_REFERENCED)) {
210 if (count > 1) {
211 if (flags & PM_PAGE_DIRTY)
212 mi->shared_dirty++;
213 else
214 mi->shared_clean++;
215 } else {
216 if (flags & PM_PAGE_DIRTY)
217 mi->private_dirty++;
218 else
219 mi->private_clean++;
220 }
221 }
222 }
223 }
224
225 total_shared_clean += mi->shared_clean;
226 total_shared_dirty += mi->shared_dirty;
227 total_private_clean += mi->private_clean;
228 total_private_dirty += mi->private_dirty;
229
230 /* add to array */
231 mis[i] = mi;
232 }
233
234 /* sort the array, if requested (compfn == NULL for original order) */
235 if (compfn)
236 qsort(mis, num_maps, sizeof(mis[0]), compfn);
237
238 for (i = 0; i < num_maps; i++) {
239 mi = mis[i];
240
241 if (hide_zeros && !mi->usage.rss)
242 continue;
243
244 if (ws == WS_ONLY) {
245 printf("%6ldK %6ldK %6ldK %6ldK %6ldK %6ldK %6ldK %s\n",
246 (long)mi->usage.rss / 1024,
247 (long)mi->usage.pss / 1024,
248 (long)mi->usage.uss / 1024,
249 mi->shared_clean * pagesize / 1024,
250 mi->shared_dirty * pagesize / 1024,
251 mi->private_clean * pagesize / 1024,
252 mi->private_dirty * pagesize / 1024,
253 pm_map_name(mi->map)
254 );
255 } else {
256 printf("%6ldK %6ldK %6ldK %6ldK %6ldK %6ldK %6ldK %6ldK %s\n",
257 (long)mi->usage.vss / 1024,
258 (long)mi->usage.rss / 1024,
259 (long)mi->usage.pss / 1024,
260 (long)mi->usage.uss / 1024,
261 mi->shared_clean * pagesize / 1024,
262 mi->shared_dirty * pagesize / 1024,
263 mi->private_clean * pagesize / 1024,
264 mi->private_dirty * pagesize / 1024,
265 pm_map_name(mi->map)
266 );
267 }
268 }
269
270 /* print totals */
271 if (ws == WS_ONLY) {
272 printf("%7s %7s %7s %7s %7s %7s %7s %s\n",
273 "-------", "-------", "-------", "-------", "-------", "-------", "-------", "");
274 printf("%6ldK %6ldK %6ldK %6ldK %6ldK %6ldK %6ldK %s\n",
275 (long)total_usage.rss / 1024,
276 (long)total_usage.pss / 1024,
277 (long)total_usage.uss / 1024,
278 total_shared_clean * pagesize / 1024,
279 total_shared_dirty * pagesize / 1024,
280 total_private_clean * pagesize / 1024,
281 total_private_dirty * pagesize / 1024,
282 "TOTAL"
283 );
284 } else {
285 printf("%7s %7s %7s %7s %7s %7s %7s %7s %s\n",
286 "-------", "-------", "-------", "-------", "-------", "-------", "-------", "-------", "");
287 printf("%6ldK %6ldK %6ldK %6ldK %6ldK %6ldK %6ldK %6ldK %s\n",
288 (long)total_usage.vss / 1024,
289 (long)total_usage.rss / 1024,
290 (long)total_usage.pss / 1024,
291 (long)total_usage.uss / 1024,
292 total_shared_clean * pagesize / 1024,
293 total_shared_dirty * pagesize / 1024,
294 total_private_clean * pagesize / 1024,
295 total_private_dirty * pagesize / 1024,
296 "TOTAL"
297 );
298 }
299
300 return 0;
301 }
302
usage(const char * cmd)303 static void usage(const char *cmd) {
304 fprintf(stderr, "Usage: %s [ -w | -W ] [ -p | -m ] [ -h ] pid\n"
305 " -w Displays statistics for the working set only.\n"
306 " -W Resets the working set of the process.\n"
307 " -p Sort by PSS.\n"
308 " -m Sort by mapping order (as read from /proc).\n"
309 " -h Hide maps with no RSS.\n",
310 cmd);
311 }
312
comp_pss(const void * a,const void * b)313 int comp_pss(const void *a, const void *b) {
314 struct map_info *ma, *mb;
315
316 ma = *((struct map_info **)a);
317 mb = *((struct map_info **)b);
318
319 if (mb->usage.pss < ma->usage.pss) return -1;
320 if (mb->usage.pss > ma->usage.pss) return 1;
321 return 0;
322 }
323