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