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