2 * Copyright (C) 2010 Timo Kokkonen <kaapeli@itanic.dy.fi>
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.
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.
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.
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", },
51 #define SI_M (SI_k * SI_k)
52 #define SI_G (SI_M * SI_k)
54 #define PRETTY_THRESH 97
56 ((a) < SI_k * 4 ? (a) : \
57 (a < SI_M * PRETTY_THRESH ? ((a) / SI_k) : \
58 (a < SI_G * PRETTY_THRESH ? ((a) / SI_M) : \
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")))
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)
69 struct analyze_frames {
70 struct bintree_ops ops;
74 struct list_head *pidlist;
78 /* General counters */
79 long int pages_present;
80 long int pages_swapped;
81 long int pages_unique;
83 /* kpageflag stats counters */
84 long int kpageflag[KPAGEFLAGS_NUM];
87 #define bintree_ops_to_af(bintree_ops) \
88 container_of((bintree_ops), struct analyze_frames, ops)
90 static void count_pages(struct bintree *b, struct bintree_ops *ops)
92 struct pageframe *pf = tree_to_pageframe(b);
93 struct analyze_frames *af = bintree_ops_to_af(ops);
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)
104 } else if (af->pidlist && af->map) {
106 * Find pages that reference at least once all of the
107 * given pids and a given mapping
113 * Check that we reference the given mapping at least
116 list_for_each_entry(ml, &pf->ml, list) {
117 if (ml->map == af->map) {
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
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) {
146 * If we have found as many matches as ther
147 * are pids, we will count the stats
149 if (matches == af->pids)
156 if (page_present(pf))
158 else if (page_swapped(pf))
160 if (pf->kpagecount == 1)
163 for (i = 0; i < KPAGEFLAGS_NUM; i++)
164 if (kpageflag_is_set(pf, i))
169 * print_page_stats - Prints system wide page stats
171 void print_page_stats(struct pageframe *pf)
173 struct analyze_frames af;
176 memset(&af, 0, sizeof(af));
178 af.ops.callback = count_pages;
180 count = bintree_walk(&pf->tree, &af.ops);
184 for (i = 0; i < KPAGEFLAGS_NUM; i++) {
185 if (!af.kpageflag[i])
188 printf("%13s pages: %6ld, %5lld %sB\n",
191 PAGE_TO_NICE(af.kpageflag[i]),
192 PAGE_TO_NICE_UNIT(af.kpageflag[i]));
195 printf(" present pages: %6ld, %5lld %sB\n"
196 " swapped pages: %6ld, %5lld %sB\n"
197 " unique pages: %6ld, %5lld %sB\n"
198 " total pages: %6ld, %5lld %sB\n",
200 PAGE_TO_NICE(af.pages_present),
201 PAGE_TO_NICE_UNIT(af.pages_present),
203 PAGE_TO_NICE(af.pages_swapped),
204 PAGE_TO_NICE_UNIT(af.pages_swapped),
206 PAGE_TO_NICE(af.pages_unique),
207 PAGE_TO_NICE_UNIT(af.pages_unique),
210 PAGE_TO_NICE_UNIT(count));
213 void print_pid_stats(struct pageframe *pf, struct process *process_list,
214 struct parse_opts *opts)
216 struct analyze_frames af;
218 long int swapped, present, unique, total;
219 long int biggest = 0, second_biggest;
220 int count, processes = 0;
224 * walk through all processes, find the one with most present
227 printf("\rAnalyzing pages for process: ");
228 list_for_each_entry(ps, &process_list->list, list) {
229 for (i = 0; i < len; i++)
231 len = printf("% 5d", ps->pid);
234 memset(&af, 0, sizeof(af));
235 af.ops.callback = count_pages;
238 bintree_walk(&pf->tree, &af.ops);
239 ps->pages_present = af.pages_present;
240 ps->pages_swapped = af.pages_swapped;
241 ps->pages_unique = af.pages_unique;
242 biggest = MAX(biggest, ps->pages_present + ps->pages_swapped);
244 for (i = 0; i < len; i++)
248 printf(" RSS swapped USS total pid");
249 if (opts->with_threads)
256 list_for_each_entry(ps, &process_list->list, list) {
258 present = ps->pages_present;
259 swapped = ps->pages_swapped;
260 unique = ps->pages_unique;
261 total = present + swapped;
263 second_biggest = (total < biggest) &&
264 (second_biggest < total) ?
265 total : second_biggest;
267 if (total != biggest)
273 printf("%5lld %sB %5lld %sB %5lld %sB %5lld %sB %5d ",
274 PAGE_TO_NICE(present), PAGE_TO_NICE_UNIT(present),
275 PAGE_TO_NICE(swapped), PAGE_TO_NICE_UNIT(swapped),
276 PAGE_TO_NICE(unique), PAGE_TO_NICE_UNIT(unique),
277 PAGE_TO_NICE(total), PAGE_TO_NICE_UNIT(total),
280 if (opts->with_threads)
281 printf("%5d ", ps->tid);
284 ps->is_initial_pid ? '*' : ' ',
292 biggest = second_biggest;
296 printf("Total %d processes\n", processes);
299 static void _dump_process_maps(struct pageframe *pf, struct process *ps,
300 struct parse_opts *opts)
303 long int swapped, present, total;
304 long int biggest = 0, second_biggest;
305 int count, processes = 0, pids = 0;
307 if (is_parse_option(opts, PARSE_SHARED_MAPPING)) {
309 list_for_each_entry(pid, &opts->pidlist, list)
313 list_for_each_entry(map, &ps->maps->list, list) {
314 struct analyze_frames af;
316 if (is_parse_option(opts, PARSE_SHARED_MAPPING)) {
317 memset(&af, 0, sizeof(af));
318 af.ops.callback = count_pages;
319 af.pidlist = &opts->pidlist;
323 bintree_walk(&pf->tree, &af.ops);
324 map->pages_present = af.pages_present;
325 map->pages_swapped = af.pages_swapped;
328 biggest = MAX(biggest, map->pages_present + map->pages_swapped);
331 printf("process: [%d] %s\n", ps->pid, ps->name);
332 printf(" size RSS swapped total name\n");
336 list_for_each_entry(map, &ps->maps->list, list) {
338 present = map->pages_present;
339 swapped = map->pages_swapped;
340 total = present + swapped;
342 second_biggest = (total < biggest) &&
343 (second_biggest < total) ?
344 total : second_biggest;
346 if (total != biggest)
350 * Do not print zero sized mappings if
351 * --shared-mappings is enabled
353 if (is_parse_option(opts, PARSE_SHARED_MAPPING) && total == 0)
356 printf("%5lld %sB %5lld %sB %5lld %sB %5lld %sB %s\n",
357 NICE_DIV(map->size), NICE_UNIT(map->size),
358 PAGE_TO_NICE(present), PAGE_TO_NICE_UNIT(present),
359 PAGE_TO_NICE(swapped), PAGE_TO_NICE_UNIT(swapped),
360 PAGE_TO_NICE(total), PAGE_TO_NICE_UNIT(total),
367 if (count > 0 && biggest > 0) {
368 biggest = second_biggest;
374 void dump_process_maps(struct pageframe *pf, struct process *process_list,
375 struct parse_opts *opts)
379 list_for_each_entry(ps, &process_list->list, list) {
380 _dump_process_maps(pf, ps, opts);