]> git.itanic.dy.fi Git - scan-pagemap/blob - analyze.c
Show full process argument list instead only executable name
[scan-pagemap] / analyze.c
1 /*
2  * Copyright (C) 2010 Timo Kokkonen <timo.t.kokkonen@iki.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         { .flag = THP,                  .str = "thp", },
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   100
55 #define NICE_DIV(a)                                                     \
56         ((a) < SI_k * 4 ? (a) :                         \
57                 (a < SI_k * PRETTY_THRESH * 1000 ? ((a) / SI_k) :       \
58                         (a < SI_M * PRETTY_THRESH * 1000 ? ((a) / SI_M) : \
59                                 ((a) / SI_G))))
60
61 #define NICE_UNIT(a)                                                    \
62         ((a) < (SI_k * 4) ? " " :                               \
63                 ((a) < (SI_k * PRETTY_THRESH * 1000) ? "k" :            \
64                         ((a) < (SI_M * PRETTY_THRESH * 1000) ? "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         int pid;
71
72         struct list_head *pidlist;
73         struct maps *map;
74         int pids;
75
76         /* General counters */
77         long int pages_present;
78         long int pages_swapped;
79         long int pages_unique;
80
81         /* kpageflag stats counters */
82         long int kpageflag[KPAGEFLAGS_NUM];
83 };
84
85 #define bintree_ops_to_af(bintree_ops)                          \
86         container_of((bintree_ops), struct analyze_frames, ops)
87
88 static void count_page(struct pageframe *pf, struct analyze_frames *af)
89 {
90         struct maps_list *ml;
91         int i;
92
93         if (af->pid) {
94                 /* Find pages which reference at least once a pid */
95                 list_for_each_entry(ml, &pf->ml, list) {
96                         if (ml->map->pid == af->pid)
97                                 goto get_stats;
98                 }
99                 return;
100         } else if (af->pidlist && af->map) {
101                 /*
102                  * Find pages that reference at least once all of the
103                  * given pids and a given mapping
104                  */
105                 struct pidlist *pid;
106                 int matches = 0;
107
108                 /*
109                  * Check that we reference the given mapping at least
110                  * once
111                  */
112                 list_for_each_entry(ml, &pf->ml, list) {
113                         if (ml->map == af->map) {
114                                 matches++;
115                                 break;
116                         }
117                 }
118
119                 if (!matches)
120                         return;
121                 matches = 0;
122
123                 /*
124                  * Check that we reference all of the given pids
125                  * too. The order of the loops is important here. We
126                  * must scan through all the references and test for a
127                  * given pid. If we would iterate through the
128                  * references in the outer loop, we might get
129                  * duplicate matches for a pid since it is possible
130                  * that a page is mapped multiple times in a process's
131                  * addrses space.
132                  */
133                 list_for_each_entry(pid, af->pidlist, list) {
134                         list_for_each_entry(ml, &pf->ml, list) {
135                                 if (ml->map->pid == pid->pid) {
136                                         matches++;
137                                         break;
138                                 }
139                         }
140
141                         /*
142                          * If we have found as many matches as ther
143                          * are pids, we will count the stats
144                          */
145                         if (matches == af->pids)
146                                 goto get_stats;
147                 }
148                 return;
149         }
150
151 get_stats:
152         if (page_present(pf))
153                 af->pages_present++;
154         else if (page_swapped(pf))
155                 af->pages_swapped++;
156         if (pf->kpagecount == 1)
157                 af->pages_unique++;
158
159         for (i = 0; i < KPAGEFLAGS_NUM; i++)
160                 if (kpageflag_is_set(pf, i))
161                         af->kpageflag[i]++;
162 }
163
164 static int count_pages(struct rb_root *root, struct analyze_frames *af)
165 {
166         struct pageframe *pf;
167         int pages = 0;
168
169         pf = rb_to_pageframe(rb_first(root));
170
171         while (pf) {
172                 pages++;
173                 count_page(pf, af);
174
175                 pf = rb_to_pageframe(rb_next(&pf->tree));
176         }
177
178         return pages;
179 }
180
181 /*
182  * print_page_stats - Prints system wide page stats
183  */
184 void print_page_stats(struct rb_root *root)
185 {
186         struct analyze_frames af;
187         long count;
188         int i;
189         memset(&af, 0, sizeof(af));
190
191         count = count_pages(root, &af);
192
193         printf("\n");
194
195         for (i = 0; i < KPAGEFLAGS_NUM; i++) {
196                 if (!af.kpageflag[i])
197                         continue;
198
199                 printf("%13s pages: %7ld, %5lld %sB\n",
200                         kpageflag_to_str(i),
201                         af.kpageflag[i],
202                         PAGE_TO_NICE(af.kpageflag[i]),
203                         PAGE_TO_NICE_UNIT(af.kpageflag[i]));
204         }
205
206         printf("      present pages: %7ld, %5lld %sB\n"
207                 "      swapped pages: %7ld, %5lld %sB\n"
208                 "       unique pages: %7ld, %5lld %sB\n"
209                 "        total pages: %7ld, %5lld %sB\n",
210                 af.pages_present,
211                 PAGE_TO_NICE(af.pages_present),
212                 PAGE_TO_NICE_UNIT(af.pages_present),
213                 af.pages_swapped,
214                 PAGE_TO_NICE(af.pages_swapped),
215                 PAGE_TO_NICE_UNIT(af.pages_swapped),
216                 af.pages_unique,
217                 PAGE_TO_NICE(af.pages_unique),
218                 PAGE_TO_NICE_UNIT(af.pages_unique),
219                 count,
220                 PAGE_TO_NICE(count),
221                 PAGE_TO_NICE_UNIT(count));
222 }
223
224 void print_pid_stats(struct rb_root *root, struct process *process_list,
225                 struct parse_opts *opts)
226 {
227         struct analyze_frames af;
228         struct process *ps;
229         struct pageframe *pf;
230         long int swapped, present, unique, total;
231         long int biggest = 0, smallest = 0, second_smallest;
232         int count, processes = 0;
233
234         /*
235          * walk through all processes, find the one with most present
236          * pages
237          */
238         pf = rb_to_pageframe(rb_first(root));
239         while(pf) {
240                 list_for_each_entry(ps, &process_list->list, list) {
241                         memset(&af, 0, sizeof(af));
242                         af.pid = ps->pid;
243
244                         count_page(pf, &af);
245                         ps->pages_present += af.pages_present;
246                         ps->pages_swapped += af.pages_swapped;
247                         ps->pages_unique  += af.pages_unique;
248                         biggest = MAX(biggest, ps->pages_present + ps->pages_swapped);
249                 }
250                 pf = rb_to_pageframe(rb_next(&pf->tree));
251         }
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_smallest = biggest;
260         count = 0;
261         list_for_each_entry(ps, &process_list->list, list) {
262                 present = ps->pages_present;
263                 swapped = ps->pages_swapped;
264                 unique = ps->pages_unique;
265                 total = present + swapped;
266
267                 if (total > smallest)
268                         second_smallest = MIN(second_smallest, total);
269
270                 if (total != smallest)
271                         continue;
272
273                 if (total == 0)
274                         continue;
275
276                 printf("%5lld %sB %5lld %sB %5lld %sB %5lld %sB %5d ",
277                         PAGE_TO_NICE(present), PAGE_TO_NICE_UNIT(present),
278                         PAGE_TO_NICE(swapped), PAGE_TO_NICE_UNIT(swapped),
279                         PAGE_TO_NICE(unique), PAGE_TO_NICE_UNIT(unique),
280                         PAGE_TO_NICE(total), PAGE_TO_NICE_UNIT(total),
281                         ps->pid);
282
283                 if (opts->with_threads)
284                         printf("%5d ", ps->tid);
285
286                 printf("%c %s\n",
287                         ps->is_initial_pid ? '*' : ' ',
288                         ps->name);
289
290                 count++;
291                 processes++;
292         }
293
294         if (smallest != second_smallest) {
295                 smallest = second_smallest;
296                 goto restart;
297         }
298
299         printf("     RSS  swapped      USS    total   pid");
300         if (opts->with_threads)
301                 printf("   tid");
302         printf("   name\n");
303         printf("Total %d processes\n", processes);
304 }
305
306 static void _dump_process_maps(struct rb_root *root, struct process *ps,
307                         struct parse_opts *opts)
308 {
309         struct maps *map;
310         long int swapped, present, total;
311         long int biggest = 0, smallest = 0, second_smallest;
312         int count, processes = 0, pids = 0;
313
314         if (is_parse_option(opts, PARSE_SHARED_MAPPING)) {
315                 struct pidlist *pid;
316                 list_for_each_entry(pid, &opts->pidlist, list)
317                         pids++;
318         }
319
320         list_for_each_entry(map, &ps->maps->list, list) {
321                 struct analyze_frames af;
322
323                 if (is_parse_option(opts, PARSE_SHARED_MAPPING)) {
324                         memset(&af, 0, sizeof(af));
325                         af.pidlist = &opts->pidlist;
326                         af.pids = pids;
327                         af.map = map;
328
329                         count_pages(root, &af);
330                         map->pages_present = af.pages_present;
331                         map->pages_swapped = af.pages_swapped;
332                 }
333
334                 biggest = MAX(biggest, map->pages_present + map->pages_swapped);
335         }
336
337         printf("process: [%d] %s\n", ps->pid, ps->name);
338         printf("    size      RSS  swapped    total name\n");
339 restart:
340         second_smallest = biggest;
341         count = 0;
342         list_for_each_entry(map, &ps->maps->list, list) {
343                 present = map->pages_present;
344                 swapped = map->pages_swapped;
345                 total = present + swapped;
346
347                 if (total > smallest)
348                         second_smallest = MIN(second_smallest, total);
349
350                 if (total != smallest)
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 (smallest != second_smallest) {
372                 smallest = second_smallest;
373                 goto restart;
374         }
375         printf("    size      RSS  swapped    total name\n");
376         printf("\n");
377 }
378
379 void dump_process_maps(struct rb_root *root, struct process *process_list,
380                 struct parse_opts *opts)
381 {
382         struct process *ps;
383
384         list_for_each_entry(ps, &process_list->list, list) {
385                 _dump_process_maps(root, ps, opts);
386         }
387 }