]> git.itanic.dy.fi Git - scan-pagemap/blob - analyze.c
print_pid_stats: Revert order of pageframe tree and process list are traversed
[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         struct pageframe *pf;
229         long int swapped, present, unique, total;
230         long int biggest = 0, second_biggest;
231         int count, processes = 0;
232
233         /*
234          * walk through all processes, find the one with most present
235          * pages
236          */
237         pf = rb_to_pageframe(rb_first(root));
238         while(pf) {
239                 list_for_each_entry(ps, &process_list->list, list) {
240                         memset(&af, 0, sizeof(af));
241                         af.pid = ps->pid;
242
243                         count_page(pf, &af);
244                         ps->pages_present += af.pages_present;
245                         ps->pages_swapped += af.pages_swapped;
246                         ps->pages_unique  += af.pages_unique;
247                         biggest = MAX(biggest, ps->pages_present + ps->pages_swapped);
248                 }
249                 pf = rb_to_pageframe(rb_next(&pf->tree));
250         }
251
252         printf("     RSS  swapped      USS    total   pid");
253         if (opts->with_threads)
254                 printf("   tid");
255         printf("   name\n");
256
257 restart:
258         second_biggest = 0;
259         count = 0;
260         list_for_each_entry(ps, &process_list->list, list) {
261
262                 present = ps->pages_present;
263                 swapped = ps->pages_swapped;
264                 unique = ps->pages_unique;
265                 total = present + swapped;
266
267                 second_biggest = (total < biggest) &&
268                         (second_biggest < total) ?
269                         total : second_biggest;
270
271                 if (total != biggest)
272                         continue;
273
274                 if (total == 0)
275                         continue;
276
277                 printf("%5lld %sB %5lld %sB %5lld %sB %5lld %sB %5d ",
278                         PAGE_TO_NICE(present), PAGE_TO_NICE_UNIT(present),
279                         PAGE_TO_NICE(swapped), PAGE_TO_NICE_UNIT(swapped),
280                         PAGE_TO_NICE(unique), PAGE_TO_NICE_UNIT(unique),
281                         PAGE_TO_NICE(total), PAGE_TO_NICE_UNIT(total),
282                         ps->pid);
283
284                 if (opts->with_threads)
285                         printf("%5d ", ps->tid);
286
287                 printf("%c %s\n",
288                         ps->is_initial_pid ? '*' : ' ',
289                         ps->name);
290
291                 count++;
292                 processes++;
293         }
294
295         if (count > 0) {
296                 biggest = second_biggest;
297                 goto restart;
298         }
299
300         printf("Total %d processes\n", processes);
301 }
302
303 static void _dump_process_maps(struct rb_root *root, struct process *ps,
304                         struct parse_opts *opts)
305 {
306         struct maps *map;
307         long int swapped, present, total;
308         long int biggest = 0, second_biggest;
309         int count, processes = 0, pids = 0;
310
311         if (is_parse_option(opts, PARSE_SHARED_MAPPING)) {
312                 struct pidlist *pid;
313                 list_for_each_entry(pid, &opts->pidlist, list)
314                         pids++;
315         }
316
317         list_for_each_entry(map, &ps->maps->list, list) {
318                 struct analyze_frames af;
319
320                 if (is_parse_option(opts, PARSE_SHARED_MAPPING)) {
321                         memset(&af, 0, sizeof(af));
322                         af.pidlist = &opts->pidlist;
323                         af.pids = pids;
324                         af.map = map;
325
326                         count_pages(root, &af);
327                         map->pages_present = af.pages_present;
328                         map->pages_swapped = af.pages_swapped;
329                 }
330
331                 biggest = MAX(biggest, map->pages_present + map->pages_swapped);
332         }
333
334         printf("process: [%d] %s\n", ps->pid, ps->name);
335         printf("    size      RSS  swapped    total name\n");
336 restart:
337         second_biggest = 0;
338         count = 0;
339         list_for_each_entry(map, &ps->maps->list, list) {
340
341                 present = map->pages_present;
342                 swapped = map->pages_swapped;
343                 total = present + swapped;
344
345                 second_biggest = (total < biggest) &&
346                         (second_biggest < total) ?
347                         total : second_biggest;
348
349                 if (total != biggest)
350                         continue;
351
352                 /*
353                  * Do not print zero sized mappings if
354                  * --shared-mappings is enabled
355                  */
356                 if (is_parse_option(opts, PARSE_SHARED_MAPPING) && total == 0)
357                         continue;
358
359                 printf("%5lld %sB %5lld %sB %5lld %sB %5lld %sB %s\n",
360                         NICE_DIV(map->size), NICE_UNIT(map->size),
361                         PAGE_TO_NICE(present), PAGE_TO_NICE_UNIT(present),
362                         PAGE_TO_NICE(swapped), PAGE_TO_NICE_UNIT(swapped),
363                         PAGE_TO_NICE(total), PAGE_TO_NICE_UNIT(total),
364                         map->name);
365
366                 count++;
367                 processes++;
368         }
369
370         if (count > 0 && biggest > 0) {
371                 biggest = second_biggest;
372                 goto restart;
373         }
374         printf("\n");
375 }
376
377 void dump_process_maps(struct rb_root *root, struct process *process_list,
378                 struct parse_opts *opts)
379 {
380         struct process *ps;
381
382         list_for_each_entry(ps, &process_list->list, list) {
383                 _dump_process_maps(root, ps, opts);
384         }
385 }