]> git.itanic.dy.fi Git - scan-pagemap/blob - analyze.c
pagemap.h: Implement kpageflag handling code
[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         long int pages_present;
62         long int pages_swapped;
63         long int pages_unique;
64 };
65
66 #define bintree_ops_to_af(bintree_ops)                          \
67         container_of((bintree_ops), struct analyze_frames, ops)
68
69 static void count_pages(struct bintree *b, struct bintree_ops *ops)
70 {
71         struct pageframe *pf = tree_to_pageframe(b);
72         struct analyze_frames *af = bintree_ops_to_af(ops);
73         struct maps_list *ml;
74
75         if (af->pid) {
76                 /* Find pages which reference at least once a pid */
77                 list_for_each_entry(ml, &pf->ml, list) {
78                         if (ml->map->pid == af->pid)
79                                 goto get_stats;
80                 }
81                 return;
82         } else if (af->pidlist && af->map) {
83                 /*
84                  * Find pages that reference at least once all of the
85                  * given pids and a given mapping
86                  */
87                 struct pidlist *pid;
88                 int matches = 0;
89
90                 /*
91                  * Check that we reference the given mapping at least
92                  * once
93                  */
94                 list_for_each_entry(ml, &pf->ml, list) {
95                         if (ml->map == af->map) {
96                                 matches++;
97                                 break;
98                         }
99                 }
100
101                 if (!matches)
102                         return;
103                 matches = 0;
104
105                 /*
106                  * Check that we reference all of the given pids
107                  * too. The order of the loops is important here. We
108                  * must scan through all the references and test for a
109                  * given pid. If we would iterate through the
110                  * references in the outer loop, we might get
111                  * duplicate matches for a pid since it is possible
112                  * that a page is mapped multiple times in a process's
113                  * addrses space.
114                  */
115                 list_for_each_entry(pid, af->pidlist, list) {
116                         list_for_each_entry(ml, &pf->ml, list) {
117                                 if (ml->map->pid == pid->pid) {
118                                         matches++;
119                                         break;
120                                 }
121                         }
122
123                         /*
124                          * If we have found as many matches as ther
125                          * are pids, we will count the stats
126                          */
127                         if (matches == af->pids)
128                                 goto get_stats;
129                 }
130                 return;
131         }
132
133 get_stats:
134         if (page_present(pf))
135                 af->pages_present++;
136         else if (page_swapped(pf))
137                 af->pages_swapped++;
138         if (pf->kpagecount == 1)
139                 af->pages_unique++;
140 }
141
142 /*
143  * print_page_stats - Prints system wide page stats
144  */
145 void print_page_stats(struct pageframe *pf)
146 {
147         struct analyze_frames af;
148         long count;
149         memset(&af, 0, sizeof(af));
150
151         af.ops.callback = count_pages;
152
153         count = bintree_walk(&pf->tree, &af.ops);
154         printf("      present pages: %6ld, %5lld %sB\n"
155                 "      swapped pages: %6ld, %5lld %sB\n"
156                 "       unique pages: %6ld, %5lld %sB\n"
157                 "        total pages: %6ld, %5lld %sB\n",
158                 af.pages_present,
159                 PAGE_TO_NICE(af.pages_present),
160                 PAGE_TO_NICE_UNIT(af.pages_present),
161                 af.pages_swapped,
162                 PAGE_TO_NICE(af.pages_swapped),
163                 PAGE_TO_NICE_UNIT(af.pages_swapped),
164                 af.pages_unique,
165                 PAGE_TO_NICE(af.pages_unique),
166                 PAGE_TO_NICE_UNIT(af.pages_unique),
167                 count,
168                 PAGE_TO_NICE(count),
169                 PAGE_TO_NICE_UNIT(count));
170 }
171
172 void print_pid_stats(struct pageframe *pf, struct process *process_list,
173                 struct parse_opts *opts)
174 {
175         struct analyze_frames af;
176         struct process *ps;
177         long int swapped, present, total;
178         long int biggest = 0, second_biggest;
179         int count, processes = 0;
180
181         /*
182          * walk through all processes, find the one with most present
183          * pages
184          */
185         list_for_each_entry(ps, &process_list->list, list) {
186                 memset(&af, 0, sizeof(af));
187                 af.ops.callback = count_pages;
188                 af.pid = ps->pid;
189
190                 bintree_walk(&pf->tree, &af.ops);
191                 ps->pages_present = af.pages_present;
192                 ps->pages_swapped = af.pages_swapped;
193                 biggest = MAX(biggest, ps->pages_present + ps->pages_swapped);
194         }
195
196         printf("  in ram  swapped    total   pid");
197         if (opts->with_threads)
198                 printf("   tid");
199         printf("   name\n");
200
201 restart:
202         second_biggest = 0;
203         count = 0;
204         list_for_each_entry(ps, &process_list->list, list) {
205
206                 present = ps->pages_present;
207                 swapped = ps->pages_swapped;
208                 total = present + swapped;
209
210                 second_biggest = (total < biggest) &&
211                         (second_biggest < total) ?
212                         total : second_biggest;
213
214                 if (total != biggest)
215                         continue;
216
217                 if (total == 0)
218                         continue;
219
220                 printf("%5lld %sB %5lld %sB %5lld %sB %5d ",
221                         PAGE_TO_NICE(present), PAGE_TO_NICE_UNIT(present),
222                         PAGE_TO_NICE(swapped), PAGE_TO_NICE_UNIT(swapped),
223                         PAGE_TO_NICE(total), PAGE_TO_NICE_UNIT(total),
224                         ps->pid);
225
226                 if (opts->with_threads)
227                         printf("%5d ", ps->tid);
228
229                 printf("%c %s\n",
230                         ps->is_initial_pid ? '*' : ' ',
231                         ps->name);
232
233                 count++;
234                 processes++;
235         }
236
237         if (count > 0) {
238                 biggest = second_biggest;
239                 goto restart;
240         }
241
242         printf("Total %d processes\n", processes);
243 }
244
245 static void _dump_process_maps(struct pageframe *pf, struct process *ps,
246                         struct parse_opts *opts)
247 {
248         struct maps *map;
249         long int swapped, present, total;
250         long int biggest = 0, second_biggest;
251         int count, processes = 0, pids = 0;
252
253         if (is_parse_option(opts, PARSE_SHARED_MAPPING)) {
254                 struct pidlist *pid;
255                 list_for_each_entry(pid, &opts->pidlist, list)
256                         pids++;
257         }
258
259         list_for_each_entry(map, &ps->maps->list, list) {
260                 struct analyze_frames af;
261
262                 if (is_parse_option(opts, PARSE_SHARED_MAPPING)) {
263                         memset(&af, 0, sizeof(af));
264                         af.ops.callback = count_pages;
265                         af.pidlist = &opts->pidlist;
266                         af.pids = pids;
267                         af.map = map;
268
269                         bintree_walk(&pf->tree, &af.ops);
270                         map->pages_present = af.pages_present;
271                         map->pages_swapped = af.pages_swapped;
272                 }
273
274                 biggest = MAX(biggest, map->pages_present + map->pages_swapped);
275         }
276
277         printf("process: [%d] %s\n", ps->pid, ps->name);
278         printf("    size   in ram  swapped    total name\n");
279 restart:
280         second_biggest = 0;
281         count = 0;
282         list_for_each_entry(map, &ps->maps->list, list) {
283
284                 present = map->pages_present;
285                 swapped = map->pages_swapped;
286                 total = present + swapped;
287
288                 second_biggest = (total < biggest) &&
289                         (second_biggest < total) ?
290                         total : second_biggest;
291
292                 if (total != biggest)
293                         continue;
294
295                 /*
296                  * Do not print zero sized mappings if
297                  * --shared-mappings is enabled
298                  */
299                 if (is_parse_option(opts, PARSE_SHARED_MAPPING) && total == 0)
300                         continue;
301
302                 printf("%5lld %sB %5lld %sB %5lld %sB %5lld %sB %s\n",
303                         NICE_DIV(map->size), NICE_UNIT(map->size),
304                         PAGE_TO_NICE(present), PAGE_TO_NICE_UNIT(present),
305                         PAGE_TO_NICE(swapped), PAGE_TO_NICE_UNIT(swapped),
306                         PAGE_TO_NICE(total), PAGE_TO_NICE_UNIT(total),
307                         map->name);
308
309                 count++;
310                 processes++;
311         }
312
313         if (count > 0 && biggest > 0) {
314                 biggest = second_biggest;
315                 goto restart;
316         }
317         printf("\n");
318 }
319
320 void dump_process_maps(struct pageframe *pf, struct process *process_list,
321                 struct parse_opts *opts)
322 {
323         struct process *ps;
324
325         list_for_each_entry(ps, &process_list->list, list) {
326                 _dump_process_maps(pf, ps, opts);
327         }
328 }