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