]> git.itanic.dy.fi Git - scan-pagemap/blob - parse.c
be38f2fd067fffc523a34fc8ca19c18640553563
[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 pid_is_match(int pidn, struct list_head *pidlist)
109 {
110         struct pidlist *pid;
111
112         list_for_each_entry(pid, pidlist, list) {
113                 if (pid->pid == pidn)
114                         return 1;
115         }
116
117         return 0;
118 }
119
120 static int should_scan_process(struct parse_opts *opts, struct process *process)
121 {
122         int match = 0;
123
124         if (is_parse_option(opts, PARSE_PID) &&
125                 is_parse_option(opts, PARSE_MAP_NAME)) {
126                 if (pid_is_match(process->pid, &opts->pidlist))
127                         match = 1;
128         } else {
129                 if (is_parse_option(opts, PARSE_PID)) {
130                         if (pid_is_match(process->pid, &opts->pidlist))
131                                 match = 1;
132                 }
133
134                 if (is_parse_option(opts, PARSE_MAP_NAME))
135                         match = 1;
136         }
137
138         if (is_parse_option(opts, PARSE_NOADD_TREE))
139                 match = !match;
140
141         return match;
142 }
143
144 static int should_scan_mapping(struct parse_opts *opts, struct maps *map)
145 {
146         int match = 0;
147
148         if (is_parse_option(opts, PARSE_MAP_NAME)) {
149                 if (strstr(map->name, opts->name))
150                         match = 1;
151
152                 if (is_parse_option(opts, PARSE_NOADD_TREE))
153                         match = !match;
154         } else
155                 match = 1;
156
157         return match;
158 }
159
160 static int should_add_to_tree(struct parse_opts *opts, struct pageframe *pf,
161                 struct maps *map)
162 {
163         if (is_parse_option(opts, PARSE_NOADD_TREE))
164                 return 0;
165
166         return 1;
167 }
168
169 /* Read data from the /proc/pid/pagemap file */
170 static int parse_pageframe(FILE *file, struct pageframe *pf_tree,
171                         struct maps *maps, struct parse_opts *opts)
172 {
173         struct maps *map;
174         struct maps_list *tmp;
175         struct pageframe *match, *pageframe = NULL;
176         long start, len, i;
177         unsigned long long pf[10240];
178         int ret, error;
179
180         if (maps == NULL)
181                 return 0;
182
183         /* Go through the list of allocated memory areas */
184         list_for_each_entry(map, &maps->list, list) {
185                 start = map->start >> (PAGE_SHIFT - 3);
186                 len = map->size >> (PAGE_SHIFT);
187
188                 if (!should_scan_mapping(opts, map))
189                         continue;
190
191                 ret = fseek(file, start, SEEK_SET);
192                 if (ret) {
193                         error = errno;
194                         fprintf(stderr, "Error seeking to %lx: %s\n", start,
195                                 strerror(error));
196                         continue;
197                 }
198
199                 for (i = 0; i < len; i++) {
200                         if (!ret) {
201                                 ret = fread(&pf, 1,
202                                         MIN(sizeof(pf), (len - i) * 8), file);
203                         }
204                         if (ret < 0) {
205                                 error = errno;
206                                 continue;
207                         }
208                         if (!pageframe)
209                                 pageframe = alloc_pageframe();
210                         ret -= sizeof(pf[0]);
211
212                         /* ignore unused pages */
213                         if (!pf[ret / sizeof(pf[0])])
214                                 continue;
215
216                         pageframe->pf = (pf[ret / sizeof(pf[0])]);
217
218                         /* ignore unused pages */
219                         if (!(page_swapped(pageframe) ||
220                                         page_present(pageframe)))
221                                 continue;
222
223                         if (should_add_to_tree(opts, pageframe, map)) {
224                                 match = tree_to_pageframe(
225                                         bintree_add(&pf_tree->tree,
226                                                 &pageframe->tree,
227                                                 &pageframe_ops));
228                         } else {
229                                 match = tree_to_pageframe(
230                                         bintree_find(&pf_tree->tree,
231                                                 &pageframe->tree,
232                                                 &pageframe_ops));
233                         }
234
235                         if (match == NULL)
236                                 continue;
237
238                         if (match == pageframe)
239                                 pageframe = NULL;
240
241                         match->refcount++;
242                         /*
243                          * Add a link from the physical page to this
244                          * process's page map
245                          */
246                         tmp = alloc_maplist();
247                         tmp->map = map;
248                         list_add(&tmp->list, &match->ml);
249
250                         if (page_present(match))
251                                 map->pages_present++;
252                         else if (page_swapped(match))
253                                 map->pages_swapped++;
254                 }
255         }
256
257         return 0;
258 }
259
260 static int read_pageframe(int pid, int tid, struct pageframe *pageframe,
261                         struct process *process_list, struct parse_opts *opts)
262 {
263         struct maps *maps;
264         struct process *process;
265         struct pidlist *pidl, *n;
266         FILE *file;
267         char path[512];
268
269         process = malloc(sizeof(*process));
270         memset(process, 0, sizeof(*process));
271         INIT_LIST_HEAD(&process->list);
272
273         process->pid = pid;
274         process->tid = tid;
275
276         if (!should_scan_process(opts, process))
277                 goto free;
278
279         snprintf(path, sizeof(path), "/proc/%d/task/%d/maps", pid, tid);
280         file = fopen(path, "rb");
281
282         if (!file)
283                 goto free;
284
285         maps = parse_maps(file, pid, tid);
286         fclose(file);
287         process->maps = maps;
288
289         snprintf(path, sizeof(path), "/proc/%d/task/%d/pagemap", pid, tid);
290         file = fopen(path, "rb");
291
292         if (!file)
293                 goto free;
294
295         parse_pageframe(file, pageframe, maps, opts);
296         fclose(file);
297
298         if (read_cmdline(pid, tid, process->name, sizeof(process->name)))
299                 goto free;
300
301         if (maps != NULL) {
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
308         if (!is_parse_option(opts, PARSE_NOADD_TREE))
309                 process->is_initial_pid = 1;
310
311         list_add_tail(&process->list, &process_list->list);
312
313         return 1;
314 free:
315         free(process);
316
317         /*
318          * Remove the pid from the list. It is no longer an
319          * interesting pid, since we can't access its data
320          */
321         list_for_each_entry_safe(pidl, n, &opts->pidlist, list) {
322                 if (pidl->pid == pid) {
323                         list_del(&pidl->list);
324                         free(pidl);
325                         break;
326                 }
327         }
328
329         return 0;
330 }
331
332 static int read_pageframe_with_threads(int pid,
333                                 struct pageframe *pageframe,
334                                 struct process *process_list,
335                                 struct parse_opts *opts)
336 {
337         DIR *dir = NULL;
338         int tid;
339         int count = 0;
340
341         while (1) {
342                 if (opts->with_threads)
343                         tid = get_next_tid(pid, &dir);
344                 else
345                         tid = pid;
346
347                 if (tid <= 0)
348                         return count;
349
350                 count += read_pageframe(pid, tid, pageframe, process_list,
351                                         opts);
352
353                 if (!opts->with_threads)
354                         break;
355         }
356
357         return count;
358 }
359
360 int scan_all_pids(struct pageframe *pf, struct process *process_list,
361                 struct parse_opts *opts)
362 {
363         struct pidlist *pidlist, *n;
364         DIR *dir = NULL;
365         int pid;
366         int count = 0;
367
368         if (is_parse_option(opts, PARSE_PID)) {
369                 list_for_each_entry_safe(pidlist, n, &opts->pidlist, list) {
370                         count += read_pageframe_with_threads(pidlist->pid, pf,
371                                                         process_list, opts);
372                 }
373         }
374
375         if ((count == 0) && !(is_parse_option(opts, PARSE_MAP_NAME))) {
376                 printf("Failed to find any matching processes "
377                         "with given arguments\n");
378                 return -1;
379         }
380
381         if (is_parse_option(opts, PARSE_DUMP))
382                 return 0;
383
384         if (is_parse_option(opts, PARSE_MAP_NAME) &&
385                 !is_parse_option(opts, PARSE_PID)) {
386                 while (1) {
387                         pid = get_next_pid(&dir);
388                         if (pid <= 0)
389                                 break;
390                         read_pageframe_with_threads(pid, pf, process_list,
391                                                 opts);
392                 }
393         }
394         /* Do not add new pages in the tree after the initial scan */
395         opts->parse_mask |= PARSE_NOADD_TREE;
396
397         while (1) {
398                 pid = get_next_pid(&dir);
399                 if (pid <= 0)
400                         break;
401                 read_pageframe_with_threads(pid, pf, process_list, opts);
402         }
403
404         return 0;
405 }
406
407 struct kpageflag_data {
408         struct bintree_ops ops;
409
410         int kpageflags_fd;
411         int kpagecount_fd;
412 };
413
414 #define bintree_ops_to_kpfd(bintree_ops)                        \
415         container_of((bintree_ops), struct kpageflag_data, ops)
416
417 static void _update_kpageflags(struct bintree *bt, struct bintree_ops *ops)
418 {
419         struct pageframe *pf = tree_to_pageframe(bt);
420         struct kpageflag_data *kpfd = bintree_ops_to_kpfd(ops);
421         int ret, error;
422         long int pfnn = pfn(pf) * sizeof(pf->kpageflags);
423
424         ret = lseek(kpfd->kpageflags_fd, pfnn, SEEK_SET);
425         if (ret < 0) {
426                 error = errno;
427                 fprintf(stderr, "Error seeking to %lx: %s\n",
428                         pfnn, strerror(error));
429                 return;
430         }
431
432         ret = read(kpfd->kpageflags_fd, &pf->kpageflags,
433                 sizeof(pf->kpageflags));
434         if (ret < 0) {
435                 error = errno;
436                 fprintf(stderr, "Error reading from %llx: %s\n",
437                         pf->pf * sizeof(pf->kpageflags),
438                         strerror(error));
439                 return;
440         }
441
442         ret = lseek(kpfd->kpagecount_fd, pfnn, SEEK_SET);
443         if (ret < 0) {
444                 error = errno;
445                 fprintf(stderr, "Error seeking to %lx: %s\n",
446                         pfnn, strerror(error));
447                 return;
448         }
449
450         ret = read(kpfd->kpagecount_fd, &pf->kpagecount,
451                 sizeof(pf->kpagecount));
452         if (ret < 0) {
453                 error = errno;
454                 fprintf(stderr, "Error reading from %llx: %s\n",
455                         pf->pf * sizeof(pf->kpagecount),
456                         strerror(error));
457                 return;
458         }
459 }
460
461 int update_kpageflags(struct pageframe *pf)
462 {
463         struct kpageflag_data kpfd = {
464                 .ops = {
465                         .callback = _update_kpageflags,
466                 },
467                 .kpageflags_fd = open("/proc/kpageflags", O_RDONLY),
468                 .kpagecount_fd = open("/proc/kpagecount", O_RDONLY),
469         };
470
471         if (kpfd.kpageflags_fd == -1 || kpfd.kpagecount_fd == -1)
472                 return -1;
473
474         bintree_walk(&pf->tree, &kpfd.ops);
475
476         close(kpfd.kpageflags_fd);
477         close(kpfd.kpagecount_fd);
478
479         return 0;
480 }