]> git.itanic.dy.fi Git - scan-pagemap/blob - analyze.c
.gitignore: Add some more entries
[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 int count_pages(struct rb_root *root, struct analyze_frames *af)
88 {
89         struct pageframe *pf;
90         struct maps_list *ml;
91         int i, pages = 0;
92
93         pf = rb_to_pageframe(rb_first(root));
94
95         while (pf) {
96                 pages++;
97
98                 if (af->pid) {
99                         /* Find pages which reference at least once a pid */
100                         list_for_each_entry(ml, &pf->ml, list) {
101                                 if (ml->map->pid == af->pid)
102                                         goto get_stats;
103                         }
104                         goto next;
105                 } else if (af->pidlist && af->map) {
106                         /*
107                          * Find pages that reference at least once all
108                          * of the given pids and a given mapping
109                          */
110                         struct pidlist *pid;
111                         int matches = 0;
112
113                         /*
114                          * Check that we reference the given mapping
115                          * at least once
116                          */
117                         list_for_each_entry(ml, &pf->ml, list) {
118                                 if (ml->map == af->map) {
119                                         matches++;
120                                         break;
121                                 }
122                         }
123
124                         if (!matches)
125                                 goto next;
126                         matches = 0;
127
128                         /*
129                          * Check that we reference all of the given
130                          * pids too. The order of the loops is
131                          * important here. We must scan through all
132                          * the references and test for a given pid. If
133                          * we would iterate through the references in
134                          * the outer loop, we might get duplicate
135                          * matches for a pid since it is possible that
136                          * a page is mapped multiple times in a
137                          * process's addrses space.
138                          */
139                         list_for_each_entry(pid, af->pidlist, list) {
140                                 list_for_each_entry(ml, &pf->ml, list) {
141                                         if (ml->map->pid == pid->pid) {
142                                                 matches++;
143                                                 break;
144                                         }
145                                 }
146
147                                 /*
148                                  * If we have found as many matches as ther
149                                  * are pids, we will count the stats
150                                  */
151                                 if (matches == af->pids)
152                                         goto get_stats;
153                         }
154                         goto next;
155                 }
156
157 get_stats:
158                 if (page_present(pf))
159                         af->pages_present++;
160                 else if (page_swapped(pf))
161                         af->pages_swapped++;
162                 if (pf->kpagecount == 1)
163                         af->pages_unique++;
164
165                 for (i = 0; i < KPAGEFLAGS_NUM; i++)
166                         if (kpageflag_is_set(pf, i))
167                                 af->kpageflag[i]++;
168
169 next:
170                 pf = rb_to_pageframe(rb_next(&pf->tree));
171         }
172
173         return pages;
174 }
175
176 /*
177  * print_page_stats - Prints system wide page stats
178  */
179 void print_page_stats(struct rb_root *root)
180 {
181         struct analyze_frames af;
182         long count;
183         int i;
184         memset(&af, 0, sizeof(af));
185
186         count = count_pages(root, &af);
187
188         printf("\n");
189
190         for (i = 0; i < KPAGEFLAGS_NUM; i++) {
191                 if (!af.kpageflag[i])
192                         continue;
193
194                 printf("%13s pages: %6ld, %5lld %sB\n",
195                         kpageflag_to_str(i),
196                         af.kpageflag[i],
197                         PAGE_TO_NICE(af.kpageflag[i]),
198                         PAGE_TO_NICE_UNIT(af.kpageflag[i]));
199         }
200
201         printf("      present pages: %6ld, %5lld %sB\n"
202                 "      swapped pages: %6ld, %5lld %sB\n"
203                 "       unique pages: %6ld, %5lld %sB\n"
204                 "        total pages: %6ld, %5lld %sB\n",
205                 af.pages_present,
206                 PAGE_TO_NICE(af.pages_present),
207                 PAGE_TO_NICE_UNIT(af.pages_present),
208                 af.pages_swapped,
209                 PAGE_TO_NICE(af.pages_swapped),
210                 PAGE_TO_NICE_UNIT(af.pages_swapped),
211                 af.pages_unique,
212                 PAGE_TO_NICE(af.pages_unique),
213                 PAGE_TO_NICE_UNIT(af.pages_unique),
214                 count,
215                 PAGE_TO_NICE(count),
216                 PAGE_TO_NICE_UNIT(count));
217 }
218
219 void print_pid_stats(struct rb_root *root, struct process *process_list,
220                 struct parse_opts *opts)
221 {
222         struct analyze_frames af;
223         struct process *ps;
224         long int swapped, present, unique, total;
225         long int biggest = 0, second_biggest;
226         int count, processes = 0;
227         int len = 0, i;
228
229         /*
230          * walk through all processes, find the one with most present
231          * pages
232          */
233         printf("\rAnalyzing pages for process: ");
234         list_for_each_entry(ps, &process_list->list, list) {
235                 for (i = 0; i < len; i++)
236                         putchar('\b');
237                 len =  printf("% 5d", ps->pid);
238                 fflush(stdout);
239
240                 memset(&af, 0, sizeof(af));
241                 af.pid = ps->pid;
242
243                 count_pages(root, &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         for (i = 0; i < len; i++)
250                 putchar('\b');
251         printf("Done  \n\n");
252
253         printf("     RSS  swapped      USS    total   pid");
254         if (opts->with_threads)
255                 printf("   tid");
256         printf("   name\n");
257
258 restart:
259         second_biggest = 0;
260         count = 0;
261         list_for_each_entry(ps, &process_list->list, list) {
262
263                 present = ps->pages_present;
264                 swapped = ps->pages_swapped;
265                 unique = ps->pages_unique;
266                 total = present + swapped;
267
268                 second_biggest = (total < biggest) &&
269                         (second_biggest < total) ?
270                         total : second_biggest;
271
272                 if (total != biggest)
273                         continue;
274
275                 if (total == 0)
276                         continue;
277
278                 printf("%5lld %sB %5lld %sB %5lld %sB %5lld %sB %5d ",
279                         PAGE_TO_NICE(present), PAGE_TO_NICE_UNIT(present),
280                         PAGE_TO_NICE(swapped), PAGE_TO_NICE_UNIT(swapped),
281                         PAGE_TO_NICE(unique), PAGE_TO_NICE_UNIT(unique),
282                         PAGE_TO_NICE(total), PAGE_TO_NICE_UNIT(total),
283                         ps->pid);
284
285                 if (opts->with_threads)
286                         printf("%5d ", ps->tid);
287
288                 printf("%c %s\n",
289                         ps->is_initial_pid ? '*' : ' ',
290                         ps->name);
291
292                 count++;
293                 processes++;
294         }
295
296         if (count > 0) {
297                 biggest = second_biggest;
298                 goto restart;
299         }
300
301         printf("Total %d processes\n", processes);
302 }
303
304 static void _dump_process_maps(struct rb_root *root, struct process *ps,
305                         struct parse_opts *opts)
306 {
307         struct maps *map;
308         long int swapped, present, total;
309         long int biggest = 0, second_biggest;
310         int count, processes = 0, pids = 0;
311
312         if (is_parse_option(opts, PARSE_SHARED_MAPPING)) {
313                 struct pidlist *pid;
314                 list_for_each_entry(pid, &opts->pidlist, list)
315                         pids++;
316         }
317
318         list_for_each_entry(map, &ps->maps->list, list) {
319                 struct analyze_frames af;
320
321                 if (is_parse_option(opts, PARSE_SHARED_MAPPING)) {
322                         memset(&af, 0, sizeof(af));
323                         af.pidlist = &opts->pidlist;
324                         af.pids = pids;
325                         af.map = map;
326
327                         count_pages(root, &af);
328                         map->pages_present = af.pages_present;
329                         map->pages_swapped = af.pages_swapped;
330                 }
331
332                 biggest = MAX(biggest, map->pages_present + map->pages_swapped);
333         }
334
335         printf("process: [%d] %s\n", ps->pid, ps->name);
336         printf("    size      RSS  swapped    total name\n");
337 restart:
338         second_biggest = 0;
339         count = 0;
340         list_for_each_entry(map, &ps->maps->list, list) {
341
342                 present = map->pages_present;
343                 swapped = map->pages_swapped;
344                 total = present + swapped;
345
346                 second_biggest = (total < biggest) &&
347                         (second_biggest < total) ?
348                         total : second_biggest;
349
350                 if (total != biggest)
351                         continue;
352
353                 /*
354                  * Do not print zero sized mappings if
355                  * --shared-mappings is enabled
356                  */
357                 if (is_parse_option(opts, PARSE_SHARED_MAPPING) && total == 0)
358                         continue;
359
360                 printf("%5lld %sB %5lld %sB %5lld %sB %5lld %sB %s\n",
361                         NICE_DIV(map->size), NICE_UNIT(map->size),
362                         PAGE_TO_NICE(present), PAGE_TO_NICE_UNIT(present),
363                         PAGE_TO_NICE(swapped), PAGE_TO_NICE_UNIT(swapped),
364                         PAGE_TO_NICE(total), PAGE_TO_NICE_UNIT(total),
365                         map->name);
366
367                 count++;
368                 processes++;
369         }
370
371         if (count > 0 && biggest > 0) {
372                 biggest = second_biggest;
373                 goto restart;
374         }
375         printf("\n");
376 }
377
378 void dump_process_maps(struct rb_root *root, struct process *process_list,
379                 struct parse_opts *opts)
380 {
381         struct process *ps;
382
383         list_for_each_entry(ps, &process_list->list, list) {
384                 _dump_process_maps(root, ps, opts);
385         }
386 }