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