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