]> git.itanic.dy.fi Git - scan-pagemap/blob - parse.c
Analyzer: Print detailed kpageflag statistics
[scan-pagemap] / parse.c
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <unistd.h>
9
10 #include "parse.h"
11 #include "pagemap.h"
12 #include "pidlib.h"
13
14 static struct maps_list *alloc_maplist(void)
15 {
16         struct maps_list *map;
17
18         map = malloc(sizeof *map);
19         if (map == NULL)
20                 goto err;
21
22         memset(map, 0, sizeof(*map));
23         INIT_LIST_HEAD(&map->list);
24 err:
25         return map;
26 }
27
28 static struct maps *alloc_map(void)
29 {
30         struct maps *map;
31
32         map = malloc(sizeof *map);
33         if (map == NULL)
34                 goto err;
35
36         memset(map, 0, sizeof(*map));
37         INIT_LIST_HEAD(&map->list);
38 err:
39         return map;
40 }
41
42 static struct maps *parse_maps(FILE *file, int pid, int tid)
43 {
44         struct maps *the_map = NULL;
45         char line[1024];
46         int ret;
47
48         while (fgets(line, sizeof(line), file)) {
49                 struct maps *map = alloc_map();
50                 unsigned long start, end;
51                 char name[1024];
52
53                 if (map == NULL)
54                         return 0;
55
56                 if (the_map == NULL)
57                         the_map = map;
58
59                 ret = sscanf(line, "%lx-%lx %*s %*s %*s %*s %s",
60                              &start, &end, name);
61
62                 if (ret < 2) {
63                         printf("Error reading input: %s\n", line);
64                         break;
65                 }
66
67                 map->start = start;
68                 map->end = end;
69                 map->size = end - start;
70                 map->pid = pid;
71                 map->tid = tid;
72
73                 if (ret >= 3)
74                         strncpy(map->name, name, sizeof(map->name));
75
76                 list_add_tail(&map->list, &the_map->list);
77         }
78
79         return the_map;
80 }
81
82 static struct pageframe *alloc_pageframe(void)
83 {
84         struct pageframe *pageframe;
85
86         pageframe = malloc(sizeof *pageframe);
87         if (pageframe == NULL)
88                 goto err;
89
90         clear_pageframe(pageframe);
91 err:
92         return pageframe;
93 }
94
95 static int compare_pageframe(struct bintree *at, struct bintree *bt)
96 {
97         struct pageframe *a, *b;
98         a = tree_to_pageframe(at);
99         b = tree_to_pageframe(bt);
100
101         return a->pf - b->pf;
102 }
103
104 struct bintree_ops pageframe_ops = {
105         .compare = compare_pageframe,
106 };
107
108 static int should_scan_process(struct parse_opts *opts, struct process *process)
109 {
110         struct pidlist *pid;
111         int match = 0;
112
113         if (is_parse_option(opts, PARSE_PID)) {
114                 list_for_each_entry(pid, &opts->pidlist, list) {
115                         if (pid->pid == process->pid) {
116                                 match = 1;
117                                 break;
118                         }
119                 }
120         }
121
122         if (is_parse_option(opts, PARSE_MAP_NAME))
123                 match = 1;
124
125         if (is_parse_option(opts, PARSE_NOADD_TREE))
126                 match = !match;
127
128         return match;
129 }
130
131 static int should_scan_mapping(struct parse_opts *opts, struct maps *map)
132 {
133         int match = 0;
134
135         if (is_parse_option(opts, PARSE_MAP_NAME)) {
136                 if (strstr(map->name, opts->name))
137                         match = 1;
138
139                 if (is_parse_option(opts, PARSE_NOADD_TREE))
140                         match = !match;
141         } else
142                 match = 1;
143
144         return match;
145 }
146
147 static int should_add_to_tree(struct parse_opts *opts, struct pageframe *pf,
148                 struct maps *map)
149 {
150         if (is_parse_option(opts, PARSE_NOADD_TREE))
151                 return 0;
152
153         return 1;
154 }
155
156 /* Read data from the /proc/pid/pagemap file */
157 static int parse_pageframe(FILE *file, struct pageframe *pf_tree,
158                         struct maps *maps, struct parse_opts *opts)
159 {
160         struct maps *map;
161         struct maps_list *tmp;
162         struct pageframe *match, *pageframe = NULL;
163         long start, len, i;
164         unsigned long long pf[10240];
165         int ret, error;
166
167         if (maps == NULL)
168                 return 0;
169
170         /* Go through the list of allocated memory areas */
171         list_for_each_entry(map, &maps->list, list) {
172                 start = map->start >> (PAGE_SHIFT - 3);
173                 len = map->size >> (PAGE_SHIFT);
174
175                 if (!should_scan_mapping(opts, map))
176                         continue;
177
178                 ret = fseek(file, start, SEEK_SET);
179                 if (ret) {
180                         error = errno;
181                         fprintf(stderr, "Error seeking to %lx: %s\n", start,
182                                 strerror(error));
183                         continue;
184                 }
185
186                 for (i = 0; i < len; i++) {
187                         if (!ret) {
188                                 ret = fread(&pf, 1,
189                                         MIN(sizeof(pf), (len - i) * 8), file);
190                         }
191                         if (ret < 0) {
192                                 error = errno;
193                                 continue;
194                         }
195                         if (!pageframe)
196                                 pageframe = alloc_pageframe();
197                         ret -= sizeof(pf[0]);
198
199                         /* ignore unused pages */
200                         if (!pf[ret / sizeof(pf[0])])
201                                 continue;
202
203                         pageframe->pf = (pf[ret / sizeof(pf[0])]);
204
205                         /* ignore unused pages */
206                         if (!(page_swapped(pageframe) ||
207                                         page_present(pageframe)))
208                                 continue;
209
210                         if (should_add_to_tree(opts, pageframe, map)) {
211                                 match = tree_to_pageframe(
212                                         bintree_add(&pf_tree->tree,
213                                                 &pageframe->tree,
214                                                 &pageframe_ops));
215                         } else {
216                                 match = tree_to_pageframe(
217                                         bintree_find(&pf_tree->tree,
218                                                 &pageframe->tree,
219                                                 &pageframe_ops));
220                         }
221
222                         if (match == NULL)
223                                 continue;
224
225                         if (match == pageframe)
226                                 pageframe = NULL;
227
228                         match->refcount++;
229                         /*
230                          * Add a link from the physical page to this
231                          * process's page map
232                          */
233                         tmp = alloc_maplist();
234                         tmp->map = map;
235                         list_add(&tmp->list, &match->ml);
236
237                         if (page_present(match))
238                                 map->pages_present++;
239                         else if (page_swapped(match))
240                                 map->pages_swapped++;
241                 }
242         }
243
244         return 0;
245 }
246
247 static int read_pageframe(int pid, int tid, struct pageframe *pageframe,
248                         struct process *process_list, struct parse_opts *opts)
249 {
250         struct maps *maps;
251         struct process *process;
252         struct pidlist *pidl, *n;
253         FILE *file;
254         char path[512];
255
256         process = malloc(sizeof(*process));
257         memset(process, 0, sizeof(*process));
258         INIT_LIST_HEAD(&process->list);
259
260         process->pid = pid;
261         process->tid = tid;
262
263         if (!should_scan_process(opts, process))
264                 goto free;
265
266         snprintf(path, sizeof(path), "/proc/%d/task/%d/maps", pid, tid);
267         file = fopen(path, "rb");
268
269         if (!file)
270                 goto free;
271
272         maps = parse_maps(file, pid, tid);
273         fclose(file);
274         process->maps = maps;
275
276         snprintf(path, sizeof(path), "/proc/%d/task/%d/pagemap", pid, tid);
277         file = fopen(path, "rb");
278
279         if (!file)
280                 goto free;
281
282         parse_pageframe(file, pageframe, maps, opts);
283         fclose(file);
284
285         if (read_cmdline(pid, tid, process->name, sizeof(process->name)))
286                 goto free;
287
288         if (maps != NULL) {
289                 list_for_each_entry(maps, &process->maps->list, list) {
290                         process->pages_present += maps->pages_present;
291                         process->pages_swapped += maps->pages_swapped;
292                 }
293         }
294
295         if (!is_parse_option(opts, PARSE_NOADD_TREE))
296                 process->is_initial_pid = 1;
297
298         list_add_tail(&process->list, &process_list->list);
299
300         return 1;
301 free:
302         free(process);
303
304         /*
305          * Remove the pid from the list. It is no longer an
306          * interesting pid, since we can't access its data
307          */
308         list_for_each_entry_safe(pidl, n, &opts->pidlist, list) {
309                 if (pidl->pid == pid) {
310                         list_del(&pidl->list);
311                         free(pidl);
312                         break;
313                 }
314         }
315
316         return 0;
317 }
318
319 static int read_pageframe_with_threads(int pid,
320                                 struct pageframe *pageframe,
321                                 struct process *process_list,
322                                 struct parse_opts *opts)
323 {
324         DIR *dir = NULL;
325         int tid;
326         int count = 0;
327
328         while (1) {
329                 if (opts->with_threads)
330                         tid = get_next_tid(pid, &dir);
331                 else
332                         tid = pid;
333
334                 if (tid <= 0)
335                         return count;
336
337                 count += read_pageframe(pid, tid, pageframe, process_list,
338                                         opts);
339
340                 if (!opts->with_threads)
341                         break;
342         }
343
344         return count;
345 }
346
347 int scan_all_pids(struct pageframe *pf, struct process *process_list,
348                 struct parse_opts *opts)
349 {
350         struct pidlist *pidlist, *n;
351         DIR *dir = NULL;
352         int pid;
353         int count = 0;
354
355         if (is_parse_option(opts, PARSE_PID)) {
356                 list_for_each_entry_safe(pidlist, n, &opts->pidlist, list) {
357                         count += read_pageframe_with_threads(pidlist->pid, pf,
358                                                         process_list, opts);
359                 }
360         }
361
362         if ((count == 0) && !(is_parse_option(opts, PARSE_MAP_NAME))) {
363                 printf("Failed to find any matching processes "
364                         "with given arguments\n");
365                 return -1;
366         }
367
368         if (is_parse_option(opts, PARSE_DUMP))
369                 return 0;
370
371         if (is_parse_option(opts, PARSE_MAP_NAME)) {
372                 while (1) {
373                         pid = get_next_pid(&dir);
374                         if (pid <= 0)
375                                 break;
376                         read_pageframe_with_threads(pid, pf, process_list,
377                                                 opts);
378                 }
379         }
380         /* Do not add new pages in the tree after the initial scan */
381         opts->parse_mask |= PARSE_NOADD_TREE;
382
383         while (1) {
384                 pid = get_next_pid(&dir);
385                 if (pid <= 0)
386                         break;
387                 read_pageframe_with_threads(pid, pf, process_list, opts);
388         }
389
390         return 0;
391 }
392
393 struct kpageflag_data {
394         struct bintree_ops ops;
395
396         int kpageflags_fd;
397         int kpagecount_fd;
398 };
399
400 #define bintree_ops_to_kpfd(bintree_ops)                        \
401         container_of((bintree_ops), struct kpageflag_data, ops)
402
403 static void _update_kpageflags(struct bintree *bt, struct bintree_ops *ops)
404 {
405         struct pageframe *pf = tree_to_pageframe(bt);
406         struct kpageflag_data *kpfd = bintree_ops_to_kpfd(ops);
407         int ret, error;
408         long int pfnn = pfn(pf) * sizeof(pf->kpageflags);
409
410         ret = lseek(kpfd->kpageflags_fd, pfnn, SEEK_SET);
411         if (ret < 0) {
412                 error = errno;
413                 fprintf(stderr, "Error seeking to %lx: %s\n",
414                         pfnn, strerror(error));
415                 return;
416         }
417
418         ret = read(kpfd->kpageflags_fd, &pf->kpageflags,
419                 sizeof(pf->kpageflags));
420         if (ret < 0) {
421                 error = errno;
422                 fprintf(stderr, "Error reading from %llx: %s\n",
423                         pf->pf * sizeof(pf->kpageflags),
424                         strerror(error));
425                 return;
426         }
427
428         ret = lseek(kpfd->kpagecount_fd, pfnn, SEEK_SET);
429         if (ret < 0) {
430                 error = errno;
431                 fprintf(stderr, "Error seeking to %lx: %s\n",
432                         pfnn, strerror(error));
433                 return;
434         }
435
436         ret = read(kpfd->kpagecount_fd, &pf->kpagecount,
437                 sizeof(pf->kpagecount));
438         if (ret < 0) {
439                 error = errno;
440                 fprintf(stderr, "Error reading from %llx: %s\n",
441                         pf->pf * sizeof(pf->kpagecount),
442                         strerror(error));
443                 return;
444         }
445 }
446
447 int update_kpageflags(struct pageframe *pf)
448 {
449         struct kpageflag_data kpfd = {
450                 .ops = {
451                         .callback = _update_kpageflags,
452                 },
453                 .kpageflags_fd = open("/proc/kpageflags", O_RDONLY),
454                 .kpagecount_fd = open("/proc/kpagecount", O_RDONLY),
455         };
456
457         if (kpfd.kpageflags_fd == -1 || kpfd.kpagecount_fd == -1)
458                 return -1;
459
460         bintree_walk(&pf->tree, &kpfd.ops);
461
462         close(kpfd.kpageflags_fd);
463         close(kpfd.kpagecount_fd);
464
465         return 0;
466 }