]> git.itanic.dy.fi Git - scan-pagemap/blob - analyze.c
Add a copyright notes on each source file
[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         for (i = 0; i < KPAGEFLAGS_NUM; i++) {
183                 if (!af.kpageflag[i])
184                         continue;
185
186                 printf("%13s pages: %6ld, %5lld %sB\n",
187                         kpageflag_to_str(i),
188                         af.kpageflag[i],
189                         PAGE_TO_NICE(af.kpageflag[i]),
190                         PAGE_TO_NICE_UNIT(af.kpageflag[i]));
191         }
192
193         printf("      present pages: %6ld, %5lld %sB\n"
194                 "      swapped pages: %6ld, %5lld %sB\n"
195                 "       unique pages: %6ld, %5lld %sB\n"
196                 "        total pages: %6ld, %5lld %sB\n",
197                 af.pages_present,
198                 PAGE_TO_NICE(af.pages_present),
199                 PAGE_TO_NICE_UNIT(af.pages_present),
200                 af.pages_swapped,
201                 PAGE_TO_NICE(af.pages_swapped),
202                 PAGE_TO_NICE_UNIT(af.pages_swapped),
203                 af.pages_unique,
204                 PAGE_TO_NICE(af.pages_unique),
205                 PAGE_TO_NICE_UNIT(af.pages_unique),
206                 count,
207                 PAGE_TO_NICE(count),
208                 PAGE_TO_NICE_UNIT(count));
209 }
210
211 void print_pid_stats(struct pageframe *pf, struct process *process_list,
212                 struct parse_opts *opts)
213 {
214         struct analyze_frames af;
215         struct process *ps;
216         long int swapped, present, unique, total;
217         long int biggest = 0, second_biggest;
218         int count, processes = 0;
219
220         /*
221          * walk through all processes, find the one with most present
222          * pages
223          */
224         list_for_each_entry(ps, &process_list->list, list) {
225                 memset(&af, 0, sizeof(af));
226                 af.ops.callback = count_pages;
227                 af.pid = ps->pid;
228
229                 bintree_walk(&pf->tree, &af.ops);
230                 ps->pages_present = af.pages_present;
231                 ps->pages_swapped = af.pages_swapped;
232                 ps->pages_unique  = af.pages_unique;
233                 biggest = MAX(biggest, ps->pages_present + ps->pages_swapped);
234         }
235
236         printf("     RSS  swapped      USS    total   pid");
237         if (opts->with_threads)
238                 printf("   tid");
239         printf("   name\n");
240
241 restart:
242         second_biggest = 0;
243         count = 0;
244         list_for_each_entry(ps, &process_list->list, list) {
245
246                 present = ps->pages_present;
247                 swapped = ps->pages_swapped;
248                 unique = ps->pages_unique;
249                 total = present + swapped;
250
251                 second_biggest = (total < biggest) &&
252                         (second_biggest < total) ?
253                         total : second_biggest;
254
255                 if (total != biggest)
256                         continue;
257
258                 if (total == 0)
259                         continue;
260
261                 printf("%5lld %sB %5lld %sB %5lld %sB %5lld %sB %5d ",
262                         PAGE_TO_NICE(present), PAGE_TO_NICE_UNIT(present),
263                         PAGE_TO_NICE(swapped), PAGE_TO_NICE_UNIT(swapped),
264                         PAGE_TO_NICE(unique), PAGE_TO_NICE_UNIT(unique),
265                         PAGE_TO_NICE(total), PAGE_TO_NICE_UNIT(total),
266                         ps->pid);
267
268                 if (opts->with_threads)
269                         printf("%5d ", ps->tid);
270
271                 printf("%c %s\n",
272                         ps->is_initial_pid ? '*' : ' ',
273                         ps->name);
274
275                 count++;
276                 processes++;
277         }
278
279         if (count > 0) {
280                 biggest = second_biggest;
281                 goto restart;
282         }
283
284         printf("Total %d processes\n", processes);
285 }
286
287 static void _dump_process_maps(struct pageframe *pf, struct process *ps,
288                         struct parse_opts *opts)
289 {
290         struct maps *map;
291         long int swapped, present, total;
292         long int biggest = 0, second_biggest;
293         int count, processes = 0, pids = 0;
294
295         if (is_parse_option(opts, PARSE_SHARED_MAPPING)) {
296                 struct pidlist *pid;
297                 list_for_each_entry(pid, &opts->pidlist, list)
298                         pids++;
299         }
300
301         list_for_each_entry(map, &ps->maps->list, list) {
302                 struct analyze_frames af;
303
304                 if (is_parse_option(opts, PARSE_SHARED_MAPPING)) {
305                         memset(&af, 0, sizeof(af));
306                         af.ops.callback = count_pages;
307                         af.pidlist = &opts->pidlist;
308                         af.pids = pids;
309                         af.map = map;
310
311                         bintree_walk(&pf->tree, &af.ops);
312                         map->pages_present = af.pages_present;
313                         map->pages_swapped = af.pages_swapped;
314                 }
315
316                 biggest = MAX(biggest, map->pages_present + map->pages_swapped);
317         }
318
319         printf("process: [%d] %s\n", ps->pid, ps->name);
320         printf("    size      RSS  swapped    total name\n");
321 restart:
322         second_biggest = 0;
323         count = 0;
324         list_for_each_entry(map, &ps->maps->list, list) {
325
326                 present = map->pages_present;
327                 swapped = map->pages_swapped;
328                 total = present + swapped;
329
330                 second_biggest = (total < biggest) &&
331                         (second_biggest < total) ?
332                         total : second_biggest;
333
334                 if (total != biggest)
335                         continue;
336
337                 /*
338                  * Do not print zero sized mappings if
339                  * --shared-mappings is enabled
340                  */
341                 if (is_parse_option(opts, PARSE_SHARED_MAPPING) && total == 0)
342                         continue;
343
344                 printf("%5lld %sB %5lld %sB %5lld %sB %5lld %sB %s\n",
345                         NICE_DIV(map->size), NICE_UNIT(map->size),
346                         PAGE_TO_NICE(present), PAGE_TO_NICE_UNIT(present),
347                         PAGE_TO_NICE(swapped), PAGE_TO_NICE_UNIT(swapped),
348                         PAGE_TO_NICE(total), PAGE_TO_NICE_UNIT(total),
349                         map->name);
350
351                 count++;
352                 processes++;
353         }
354
355         if (count > 0 && biggest > 0) {
356                 biggest = second_biggest;
357                 goto restart;
358         }
359         printf("\n");
360 }
361
362 void dump_process_maps(struct pageframe *pf, struct process *process_list,
363                 struct parse_opts *opts)
364 {
365         struct process *ps;
366
367         list_for_each_entry(ps, &process_list->list, list) {
368                 _dump_process_maps(pf, ps, opts);
369         }
370 }