]> git.itanic.dy.fi Git - scan-pagemap/blob - parse.c
parser: Factor out reading cmdline to separate function
[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
8 #include "parse.h"
9 #include "pagemap.h"
10
11 static struct maps_list *alloc_maplist(void)
12 {
13         struct maps_list *map;
14
15         map = malloc(sizeof *map);
16         if (map == NULL)
17                 goto err;
18
19         memset(map, 0, sizeof(*map));
20         INIT_LIST_HEAD(&map->list);
21 err:
22         return map;
23 }
24
25 static struct maps *alloc_map(void)
26 {
27         struct maps *map;
28
29         map = malloc(sizeof *map);
30         if (map == NULL)
31                 goto err;
32
33         memset(map, 0, sizeof(*map));
34         INIT_LIST_HEAD(&map->list);
35 err:
36         return map;
37 }
38
39 static struct maps *parse_maps(FILE *file, int pid, int tid)
40 {
41         struct maps *the_map = NULL;
42         char line[1024];
43         int ret;
44
45         while(fgets(line, sizeof(line), file)) {
46                 struct maps *map = alloc_map();
47                 unsigned long start, end;
48                 char name[1024];
49
50                 if (map == NULL)
51                         return 0;
52
53                 if (the_map == NULL)
54                         the_map = map;
55
56                 ret = sscanf(line, "%lx-%lx %*s %*s %*s %*s %s",
57                              &start, &end, name);
58
59                 if (ret < 2) {
60                         printf("Error reading input: %s\n", line);
61                         break;
62                 }
63
64                 map->start = start;
65                 map->end = end;
66                 map->size = end - start;
67                 map->pid = pid;
68                 map->tid = tid;
69
70                 if (ret >= 3)
71                         strncpy(map->name, name, sizeof(map->name));
72
73                 list_add_tail(&map->list, &the_map->list);
74         }
75
76         return the_map;
77 }
78
79 static void clear_pageframe(struct pageframe *pf)
80 {
81         memset(pf, 0, sizeof(*pf));
82 }
83
84 static struct pageframe *alloc_pageframe(void)
85 {
86         struct pageframe *pageframe;
87
88         pageframe = malloc(sizeof *pageframe);
89         if (pageframe == NULL)
90                 goto err;
91
92         clear_pageframe(pageframe);
93 err:
94         return pageframe;
95 }
96
97 #define BITRANGE(first, last) (((2ll << (last - first)) - 1) << first)
98
99 static void pageframe_to_struct(unsigned long long p, struct pageframe *pf)
100 {
101         /* Refer Documentation/vm/pagemap.txt for the format */
102         pf->page_present = !!(BITRANGE(63, 63) & p);
103         pf->page_swapped = !!(BITRANGE(62, 62) & p);
104         pf->page_shift   =   (BITRANGE(55, 60) & p) >> 55;
105         pf->pfn          =   (BITRANGE(0, 54) & p);
106         pf->swap_type    =   (BITRANGE(0, 4) & p);
107         pf->swap_offset  =   (BITRANGE(5, 54) & p) >> 5;
108 #if 0
109         printf("pfn: %lx shift: %d present: %d swapped %d\n",
110                 pf->pfn, pf->page_shift, pf->page_present, pf->page_swapped);
111 #endif
112 }
113
114 static int compare_pageframe(struct bintree *at, struct bintree *bt)
115 {
116         struct pageframe *a, *b;
117         a = tree_to_pageframe(at);
118         b = tree_to_pageframe(bt);
119
120         return a->pfn - b->pfn;
121 }
122
123 struct bintree_ops pageframe_ops = {
124         .compare = compare_pageframe,
125 };
126
127 static int read_cmdline(int pid, int tid, char *cmdline, size_t len)
128 {
129         FILE *file;
130         char path[512];
131         int ret;
132
133         snprintf(path, sizeof(path), "/proc/%d/task/%d/cmdline", pid, tid);
134         file = fopen(path, "rb");
135
136         if (!file)
137                 return -1;
138
139         ret = fread(cmdline, 1, len, file);
140         if (ret > 0)
141                 cmdline[ret - 1] = 0;
142         fclose(file);
143
144         return ret > 0 ? 0 : -1;
145 }
146
147 static int check_parse_opts(struct parse_opts *opts, struct pageframe *pf,
148                 struct maps *map)
149 {
150         if (opts->parse_mask & PARSE_PID) {
151                 if (opts->pid == map->pid)
152                         return 1;
153         }
154
155         if (opts->parse_mask & PARSE_MAP_NAME) {
156                 if (!strcmp(opts->name, map->name))
157                         return 1;
158         }
159
160         return 0;
161 }
162
163 /* Read data from the /proc/pid/pagemap file */
164 static int parse_pageframe(FILE *file, struct pageframe *pf_tree,
165                         struct maps *maps, struct parse_opts *opts)
166 {
167         struct maps *map;
168         struct maps_list *tmp;
169         struct pageframe *match, *pageframe = NULL;
170         long start, len, i;
171         unsigned long long pf[10240];
172         int ret, error;
173
174         if (maps == NULL)
175                 return 0;
176
177         /* Go through the list of allocated memory areas */
178         list_for_each_entry(map, &maps->list, list) {
179                 start = map->start >> (PAGE_SHIFT - 3);
180                 len = map->size >> (PAGE_SHIFT - 3);
181
182                 ret = fseek(file, start, SEEK_SET);
183                 if (ret) {
184                         error = errno;
185                         fprintf(stderr, "Error seeking to %lx: %s\n", start,
186                                 strerror(error));
187                         continue;
188                 }
189
190                 for (i = 0; i < len; i++) {
191                         if (!ret) {
192                                 ret = fread(&pf, 1,
193                                         MIN(sizeof(pf), (len - i) * 8), file);
194                         }
195                         if (ret < 0) {
196                                 error = errno;
197                                 continue;
198                         }
199                         if (!pageframe)
200                                 pageframe = alloc_pageframe();
201                         ret -= sizeof(pf[0]);
202
203                         /* ignore unused pages */
204                         if (!pf[ret / sizeof(pf[0])])
205                                 continue;
206
207                         pageframe_to_struct(pf[ret / sizeof(pf[0])], pageframe);
208
209                         /* ignore unused pages */
210                         if (!(pageframe->page_swapped ||
211                                         pageframe->page_present))
212                                 continue;
213
214                         if (check_parse_opts(opts, pageframe, map)) {
215                                 match = tree_to_pageframe(
216                                         bintree_add(&pf_tree->tree,
217                                                 &pageframe->tree,
218                                                 &pageframe_ops));
219                         } else {
220                                 match = tree_to_pageframe(
221                                         bintree_find(&pf_tree->tree,
222                                                 &pageframe->tree,
223                                                 &pageframe_ops));
224                         }
225
226                         if (match == NULL)
227                                 continue;
228
229                         if (match == pageframe)
230                                 pageframe = NULL;
231
232                         match->refcount++;
233                         /*
234                          * Add a link from the physical page to this
235                          * process's page map
236                          */
237                         if (!match->ml) {
238                                 match->ml = alloc_maplist();
239                                 match->ml->map = map;
240                         } else {
241                                 tmp = alloc_maplist();
242                                 tmp->map = map;
243                                 list_add(&match->ml->list, &tmp->list);
244                         }
245
246                         if (match->page_present) {
247                                 map->pages_present++;
248                         } else if (match->page_swapped) {
249                                 map->pages_swapped++;
250                         }
251                 }
252         }
253
254         return 0;
255 }
256
257 void read_pageframe(int pid, int tid, struct pageframe *pageframe,
258                 struct process **process_list, struct parse_opts *opts)
259 {
260         struct maps *maps;
261         struct process *process;
262         FILE *file;
263         char path[512];
264
265         process = malloc(sizeof(*process));
266         memset(process, 0, sizeof(*process));
267         INIT_LIST_HEAD(&process->list);
268
269         if (*process_list == NULL)
270                 *process_list = process;
271
272         process->pid = pid;
273         process->tid = tid;
274
275         list_add_tail(&process->list, &(*process_list)->list);
276
277         snprintf(path, sizeof(path), "/proc/%d/task/%d/maps", pid, tid);
278         file = fopen(path, "rb");
279
280         if (!file)
281                 return;
282
283         maps = parse_maps(file, pid, tid);
284         fclose(file);
285         process->maps = maps;
286
287         snprintf(path, sizeof(path), "/proc/%d/task/%d/pagemap", pid, tid);
288         file = fopen(path, "rb");
289
290         if (!file)
291                 return;
292
293         parse_pageframe(file, pageframe, maps, opts);
294         fclose(file);
295
296         if (read_cmdline(pid, tid, process->name, sizeof(process->name)))
297                 return;
298
299         if (maps == NULL)
300                 return;
301
302         list_for_each_entry(maps, &process->maps->list, list) {
303                 process->pages_present += maps->pages_present;
304                 process->pages_swapped += maps->pages_swapped;
305         }
306
307         return;
308 }
309
310 static int parse_pid(DIR **dir)
311 {
312         struct dirent *dirent;
313         int error;
314
315 restart:
316         dirent = readdir(*dir);
317         if (!dirent) {
318                 if (errno == 0) {
319                         closedir(*dir);
320                         *dir = NULL;
321                         return 0;
322                 }
323                 error = errno;
324                 printf("Failed to read /proc directory: %s\n", strerror(error));
325                 return -1;
326         }
327
328         if (dirent->d_name[0] < '0' || dirent->d_name[0] > '9')
329                 goto restart;
330
331         return atoi(dirent->d_name);
332 }
333
334 static int opendir_check(DIR **dir, const char *path)
335 {
336         int error;
337
338         if (!*dir) {
339                 *dir = opendir("/proc");
340                 if (!dir) {
341                         error = errno;
342                         fprintf(stderr, "Failed to open /proc directory: %s\n",
343                                 strerror(error));
344                         return -1;
345                 }
346         }
347
348         return 0;
349 }
350
351 static int get_next_tid(int pid, DIR **dir)
352 {
353         if (*dir == NULL) {
354                 char path[64];
355
356                 snprintf(path, sizeof(path), "/proc/%d/task/", pid);
357                 if (opendir_check(dir, path))
358                         return -1;
359         }
360
361         return parse_pid(dir);
362 }
363
364 static int get_next_pid(DIR **dir)
365 {
366         if (opendir_check(dir, "/proc"))
367                 return -1;
368
369         return parse_pid(dir);
370 }
371
372 static void read_pageframe_with_threads(int pid,
373                                         struct pageframe *pageframe,
374                                         struct process **process_list,
375                                         struct parse_opts *opts)
376 {
377         DIR *dir = NULL;
378         int tid;
379
380         while (1) {
381                 if (opts->with_threads)
382                         tid = get_next_tid(pid, &dir);
383                 else
384                         tid = pid;
385
386                 if (tid <= 0)
387                         return;
388
389                 read_pageframe(pid, tid, pageframe, process_list, opts);
390
391                 if (!opts->with_threads)
392                         break;
393         }
394 }
395
396 void scan_all_pids(struct pageframe *pf, struct process **process_list,
397                 struct parse_opts *opts)
398 {
399         DIR *dir = NULL;
400         int pid;
401
402         if (opts->parse_mask & PARSE_PID)
403                 read_pageframe_with_threads(opts->pid, pf, process_list, opts);
404
405         while(1) {
406                 pid = get_next_pid(&dir);
407                 if (pid <= 0)
408                         break;
409                 read_pageframe_with_threads(pid, pf, process_list, opts);
410         }
411 }