]> git.itanic.dy.fi Git - scan-pagemap/blob - analyze.c
547a3add74a7c64289006988bb6e51d30cc916bc
[scan-pagemap] / analyze.c
1 /*
2  * Copyright (C) 2010 Timo Kokkonen <kaapeli@itanic.dy.fi>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16  */
17
18 #include <string.h>
19 #include <stdio.h>
20
21 #include "analyze.h"
22 #include "utils.h"
23
24 struct kpageflag_str kpageflag_str[] = {
25         { .flag = LOCKED,               .str = "locked", },
26         { .flag = ERROR,                .str = "error", },
27         { .flag = REFERENCED,           .str = "referenced", },
28         { .flag = UPTODATE,             .str = "uptodate", },
29         { .flag = DIRTY,                .str = "dirty", },
30         { .flag = LRU,                  .str = "lru", },
31         { .flag = ACTIVE,               .str = "active", },
32         { .flag = SLAB,                 .str = "slab", },
33         { .flag = WRITEBACK,            .str = "writeback", },
34         { .flag = RECLAIM,              .str = "reclaim", },
35         { .flag = BUDDY,                .str = "buddy", },
36         { .flag = MMAP,                 .str = "mmap", },
37         { .flag = ANON,                 .str = "anon", },
38         { .flag = SWAPCACHE,            .str = "swapcache", },
39         { .flag = SWAPBACKED,           .str = "swapbacked", },
40         { .flag = COMPOUND_HEAD,        .str = "compound_head", },
41         { .flag = COMPOUND_TAIL,        .str = "compound_tail", },
42         { .flag = HUGE,                 .str = "huge", },
43         { .flag = UNEVICTABLE,          .str = "unevictable", },
44         { .flag = HWPOISON,             .str = "hwpoison", },
45         { .flag = NOPAGE,               .str = "nopage", },
46         { .flag = KSM,                  .str = "ksm", },
47 };
48
49 #define SI_k    1024ll
50 #define SI_M    (SI_k * SI_k)
51 #define SI_G    (SI_M * SI_k)
52
53 #define PRETTY_THRESH   97
54 #define NICE_DIV(a)                                                     \
55         ((a) < SI_k * 4 ? (a) :                         \
56                 (a < SI_M * PRETTY_THRESH ? ((a) / SI_k) :              \
57                         (a < SI_G * PRETTY_THRESH ? ((a) / SI_M) :      \
58                                 ((a) / SI_G))))
59
60 #define NICE_UNIT(a)                                                    \
61         ((a) < (SI_k * 4) ? " " :                               \
62                 ((a) < (SI_M * PRETTY_THRESH) ? "k" :                   \
63                         ((a) < (SI_G * PRETTY_THRESH) ? "M" : "G")))
64
65 #define PAGE_TO_NICE(a)         NICE_DIV((long long)a * PAGE_SIZE)
66 #define PAGE_TO_NICE_UNIT(a)    NICE_UNIT((long long)a * PAGE_SIZE)
67
68 struct analyze_frames {
69         int pid;
70
71         struct list_head *pidlist;
72         struct maps *map;
73         int pids;
74
75         /* General counters */
76         long int pages_present;
77         long int pages_swapped;
78         long int pages_unique;
79
80         /* kpageflag stats counters */
81         long int kpageflag[KPAGEFLAGS_NUM];
82 };
83
84 #define bintree_ops_to_af(bintree_ops)                          \
85         container_of((bintree_ops), struct analyze_frames, ops)
86
87 static void count_page(struct pageframe *pf, struct analyze_frames *af)
88 {
89         struct maps_list *ml;
90         int i;
91
92         if (af->pid) {
93                 /* Find pages which reference at least once a pid */
94                 list_for_each_entry(ml, &pf->ml, list) {
95                         if (ml->map->pid == af->pid)
96                                 goto get_stats;
97                 }
98                 return;
99         } else if (af->pidlist && af->map) {
100                 /*
101                  * Find pages that reference at least once all of the
102                  * given pids and a given mapping
103                  */
104                 struct pidlist *pid;
105                 int matches = 0;
106
107                 /*
108                  * Check that we reference the given mapping at least
109                  * once
110                  */
111                 list_for_each_entry(ml, &pf->ml, list) {
112                         if (ml->map == af->map) {
113                                 matches++;
114                                 break;
115                         }
116                 }
117
118                 if (!matches)
119                         return;
120                 matches = 0;
121
122                 /*
123                  * Check that we reference all of the given pids
124                  * too. The order of the loops is important here. We
125                  * must scan through all the references and test for a
126                  * given pid. If we would iterate through the
127                  * references in the outer loop, we might get
128                  * duplicate matches for a pid since it is possible
129                  * that a page is mapped multiple times in a process's
130                  * addrses space.
131                  */
132                 list_for_each_entry(pid, af->pidlist, list) {
133                         list_for_each_entry(ml, &pf->ml, list) {
134                                 if (ml->map->pid == pid->pid) {
135                                         matches++;
136                                         break;
137                                 }
138                         }
139
140                         /*
141                          * If we have found as many matches as ther
142                          * are pids, we will count the stats
143                          */
144                         if (matches == af->pids)
145                                 goto get_stats;
146                 }
147                 return;
148         }
149
150 get_stats:
151         if (page_present(pf))
152                 af->pages_present++;
153         else if (page_swapped(pf))
154                 af->pages_swapped++;
155         if (pf->kpagecount == 1)
156                 af->pages_unique++;
157
158         for (i = 0; i < KPAGEFLAGS_NUM; i++)
159                 if (kpageflag_is_set(pf, i))
160                         af->kpageflag[i]++;
161 }
162
163 static int count_pages(struct rb_root *root, struct analyze_frames *af)
164 {
165         struct pageframe *pf;
166         int pages = 0;
167
168         pf = rb_to_pageframe(rb_first(root));
169
170         while (pf) {
171                 pages++;
172                 count_page(pf, af);
173
174                 pf = rb_to_pageframe(rb_next(&pf->tree));
175         }
176
177         return pages;
178 }
179
180 /*
181  * print_page_stats - Prints system wide page stats
182  */
183 void print_page_stats(struct rb_root *root)
184 {
185         struct analyze_frames af;
186         long count;
187         int i;
188         memset(&af, 0, sizeof(af));
189
190         count = count_pages(root, &af);
191
192         printf("\n");
193
194         for (i = 0; i < KPAGEFLAGS_NUM; i++) {
195                 if (!af.kpageflag[i])
196                         continue;
197
198                 printf("%13s pages: %6ld, %5lld %sB\n",
199                         kpageflag_to_str(i),
200                         af.kpageflag[i],
201                         PAGE_TO_NICE(af.kpageflag[i]),
202                         PAGE_TO_NICE_UNIT(af.kpageflag[i]));
203         }
204
205         printf("      present pages: %6ld, %5lld %sB\n"
206                 "      swapped pages: %6ld, %5lld %sB\n"
207                 "       unique pages: %6ld, %5lld %sB\n"
208                 "        total pages: %6ld, %5lld %sB\n",
209                 af.pages_present,
210                 PAGE_TO_NICE(af.pages_present),
211                 PAGE_TO_NICE_UNIT(af.pages_present),
212                 af.pages_swapped,
213                 PAGE_TO_NICE(af.pages_swapped),
214                 PAGE_TO_NICE_UNIT(af.pages_swapped),
215                 af.pages_unique,
216                 PAGE_TO_NICE(af.pages_unique),
217                 PAGE_TO_NICE_UNIT(af.pages_unique),
218                 count,
219                 PAGE_TO_NICE(count),
220                 PAGE_TO_NICE_UNIT(count));
221 }
222
223 void print_pid_stats(struct rb_root *root, struct process *process_list,
224                 struct parse_opts *opts)
225 {
226         struct analyze_frames af;
227         struct process *ps;
228         long int swapped, present, unique, total;
229         long int biggest = 0, second_biggest;
230         int count, processes = 0;
231         int len = 0, i;
232
233         /*
234          * walk through all processes, find the one with most present
235          * pages
236          */
237         printf("\rAnalyzing pages for process: ");
238         list_for_each_entry(ps, &process_list->list, list) {
239                 for (i = 0; i < len; i++)
240                         putchar('\b');
241                 len =  printf("% 5d", ps->pid);
242                 fflush(stdout);
243
244                 memset(&af, 0, sizeof(af));
245                 af.pid = ps->pid;
246
247                 count_pages(root, &af);
248                 ps->pages_present = af.pages_present;
249                 ps->pages_swapped = af.pages_swapped;
250                 ps->pages_unique  = af.pages_unique;
251                 biggest = MAX(biggest, ps->pages_present + ps->pages_swapped);
252         }
253         for (i = 0; i < len; i++)
254                 putchar('\b');
255         printf("Done  \n\n");
256
257         printf("     RSS  swapped      USS    total   pid");
258         if (opts->with_threads)
259                 printf("   tid");
260         printf("   name\n");
261
262 restart:
263         second_biggest = 0;
264         count = 0;
265         list_for_each_entry(ps, &process_list->list, list) {
266
267                 present = ps->pages_present;
268                 swapped = ps->pages_swapped;
269                 unique = ps->pages_unique;
270                 total = present + swapped;
271
272                 second_biggest = (total < biggest) &&
273                         (second_biggest < total) ?
274                         total : second_biggest;
275
276                 if (total != biggest)
277                         continue;
278
279                 if (total == 0)
280                         continue;
281
282                 printf("%5lld %sB %5lld %sB %5lld %sB %5lld %sB %5d ",
283                         PAGE_TO_NICE(present), PAGE_TO_NICE_UNIT(present),
284                         PAGE_TO_NICE(swapped), PAGE_TO_NICE_UNIT(swapped),
285                         PAGE_TO_NICE(unique), PAGE_TO_NICE_UNIT(unique),
286                         PAGE_TO_NICE(total), PAGE_TO_NICE_UNIT(total),
287                         ps->pid);
288
289                 if (opts->with_threads)
290                         printf("%5d ", ps->tid);
291
292                 printf("%c %s\n",
293                         ps->is_initial_pid ? '*' : ' ',
294                         ps->name);
295
296                 count++;
297                 processes++;
298         }
299
300         if (count > 0) {
301                 biggest = second_biggest;
302                 goto restart;
303         }
304
305         printf("Total %d processes\n", processes);
306 }
307
308 static void _dump_process_maps(struct rb_root *root, struct process *ps,
309                         struct parse_opts *opts)
310 {
311         struct maps *map;
312         long int swapped, present, total;
313         long int biggest = 0, second_biggest;
314         int count, processes = 0, pids = 0;
315
316         if (is_parse_option(opts, PARSE_SHARED_MAPPING)) {
317                 struct pidlist *pid;
318                 list_for_each_entry(pid, &opts->pidlist, list)
319                         pids++;
320         }
321
322         list_for_each_entry(map, &ps->maps->list, list) {
323                 struct analyze_frames af;
324
325                 if (is_parse_option(opts, PARSE_SHARED_MAPPING)) {
326                         memset(&af, 0, sizeof(af));
327                         af.pidlist = &opts->pidlist;
328                         af.pids = pids;
329                         af.map = map;
330
331                         count_pages(root, &af);
332                         map->pages_present = af.pages_present;
333                         map->pages_swapped = af.pages_swapped;
334                 }
335
336                 biggest = MAX(biggest, map->pages_present + map->pages_swapped);
337         }
338
339         printf("process: [%d] %s\n", ps->pid, ps->name);
340         printf("    size      RSS  swapped    total name\n");
341 restart:
342         second_biggest = 0;
343         count = 0;
344         list_for_each_entry(map, &ps->maps->list, list) {
345
346                 present = map->pages_present;
347                 swapped = map->pages_swapped;
348                 total = present + swapped;
349
350                 second_biggest = (total < biggest) &&
351                         (second_biggest < total) ?
352                         total : second_biggest;
353
354                 if (total != biggest)
355                         continue;
356
357                 /*
358                  * Do not print zero sized mappings if
359                  * --shared-mappings is enabled
360                  */
361                 if (is_parse_option(opts, PARSE_SHARED_MAPPING) && total == 0)
362                         continue;
363
364                 printf("%5lld %sB %5lld %sB %5lld %sB %5lld %sB %s\n",
365                         NICE_DIV(map->size), NICE_UNIT(map->size),
366                         PAGE_TO_NICE(present), PAGE_TO_NICE_UNIT(present),
367                         PAGE_TO_NICE(swapped), PAGE_TO_NICE_UNIT(swapped),
368                         PAGE_TO_NICE(total), PAGE_TO_NICE_UNIT(total),
369                         map->name);
370
371                 count++;
372                 processes++;
373         }
374
375         if (count > 0 && biggest > 0) {
376                 biggest = second_biggest;
377                 goto restart;
378         }
379         printf("\n");
380 }
381
382 void dump_process_maps(struct rb_root *root, struct process *process_list,
383                 struct parse_opts *opts)
384 {
385         struct process *ps;
386
387         list_for_each_entry(ps, &process_list->list, list) {
388                 _dump_process_maps(root, ps, opts);
389         }
390 }