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