]> git.itanic.dy.fi Git - scan-pagemap/blob - analyze.c
b2ab22c8acc795d26d059b781c27ba3f05197edf
[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   97
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 %sB\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, unique, 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                 ps->pages_unique  = af.pages_unique;
216                 biggest = MAX(biggest, ps->pages_present + ps->pages_swapped);
217         }
218
219         printf("     RSS  swapped      USS    total   pid");
220         if (opts->with_threads)
221                 printf("   tid");
222         printf("   name\n");
223
224 restart:
225         second_biggest = 0;
226         count = 0;
227         list_for_each_entry(ps, &process_list->list, list) {
228
229                 present = ps->pages_present;
230                 swapped = ps->pages_swapped;
231                 unique = ps->pages_unique;
232                 total = present + swapped;
233
234                 second_biggest = (total < biggest) &&
235                         (second_biggest < total) ?
236                         total : second_biggest;
237
238                 if (total != biggest)
239                         continue;
240
241                 if (total == 0)
242                         continue;
243
244                 printf("%5lld %sB %5lld %sB %5lld %sB %5lld %sB %5d ",
245                         PAGE_TO_NICE(present), PAGE_TO_NICE_UNIT(present),
246                         PAGE_TO_NICE(swapped), PAGE_TO_NICE_UNIT(swapped),
247                         PAGE_TO_NICE(unique), PAGE_TO_NICE_UNIT(unique),
248                         PAGE_TO_NICE(total), PAGE_TO_NICE_UNIT(total),
249                         ps->pid);
250
251                 if (opts->with_threads)
252                         printf("%5d ", ps->tid);
253
254                 printf("%c %s\n",
255                         ps->is_initial_pid ? '*' : ' ',
256                         ps->name);
257
258                 count++;
259                 processes++;
260         }
261
262         if (count > 0) {
263                 biggest = second_biggest;
264                 goto restart;
265         }
266
267         printf("Total %d processes\n", processes);
268 }
269
270 static void _dump_process_maps(struct pageframe *pf, struct process *ps,
271                         struct parse_opts *opts)
272 {
273         struct maps *map;
274         long int swapped, present, total;
275         long int biggest = 0, second_biggest;
276         int count, processes = 0, pids = 0;
277
278         if (is_parse_option(opts, PARSE_SHARED_MAPPING)) {
279                 struct pidlist *pid;
280                 list_for_each_entry(pid, &opts->pidlist, list)
281                         pids++;
282         }
283
284         list_for_each_entry(map, &ps->maps->list, list) {
285                 struct analyze_frames af;
286
287                 if (is_parse_option(opts, PARSE_SHARED_MAPPING)) {
288                         memset(&af, 0, sizeof(af));
289                         af.ops.callback = count_pages;
290                         af.pidlist = &opts->pidlist;
291                         af.pids = pids;
292                         af.map = map;
293
294                         bintree_walk(&pf->tree, &af.ops);
295                         map->pages_present = af.pages_present;
296                         map->pages_swapped = af.pages_swapped;
297                 }
298
299                 biggest = MAX(biggest, map->pages_present + map->pages_swapped);
300         }
301
302         printf("process: [%d] %s\n", ps->pid, ps->name);
303         printf("    size      RSS  swapped    total name\n");
304 restart:
305         second_biggest = 0;
306         count = 0;
307         list_for_each_entry(map, &ps->maps->list, list) {
308
309                 present = map->pages_present;
310                 swapped = map->pages_swapped;
311                 total = present + swapped;
312
313                 second_biggest = (total < biggest) &&
314                         (second_biggest < total) ?
315                         total : second_biggest;
316
317                 if (total != biggest)
318                         continue;
319
320                 /*
321                  * Do not print zero sized mappings if
322                  * --shared-mappings is enabled
323                  */
324                 if (is_parse_option(opts, PARSE_SHARED_MAPPING) && total == 0)
325                         continue;
326
327                 printf("%5lld %sB %5lld %sB %5lld %sB %5lld %sB %s\n",
328                         NICE_DIV(map->size), NICE_UNIT(map->size),
329                         PAGE_TO_NICE(present), PAGE_TO_NICE_UNIT(present),
330                         PAGE_TO_NICE(swapped), PAGE_TO_NICE_UNIT(swapped),
331                         PAGE_TO_NICE(total), PAGE_TO_NICE_UNIT(total),
332                         map->name);
333
334                 count++;
335                 processes++;
336         }
337
338         if (count > 0 && biggest > 0) {
339                 biggest = second_biggest;
340                 goto restart;
341         }
342         printf("\n");
343 }
344
345 void dump_process_maps(struct pageframe *pf, struct process *process_list,
346                 struct parse_opts *opts)
347 {
348         struct process *ps;
349
350         list_for_each_entry(ps, &process_list->list, list) {
351                 _dump_process_maps(pf, ps, opts);
352         }
353 }