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