]> git.itanic.dy.fi Git - scan-pagemap/blob - parse.c
371abdb71cbf83fcb553c8f53b911c4aa70acc01
[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 <fcntl.h>
23 #include <unistd.h>
24
25 #include "parse.h"
26 #include "pagemap.h"
27 #include "pidlib.h"
28
29 static struct maps_list *alloc_maplist(void)
30 {
31         struct maps_list *map;
32
33         map = calloc(sizeof *map, 1);
34         if (map == NULL)
35                 goto err;
36
37         INIT_LIST_HEAD(&map->list);
38 err:
39         return map;
40 }
41
42 static struct maps *alloc_map(void)
43 {
44         struct maps *map;
45
46         map = calloc(sizeof(*map), 1);
47         if (map == NULL)
48                 goto err;
49
50         INIT_LIST_HEAD(&map->list);
51 err:
52         return map;
53 }
54
55 static struct maps *parse_maps(FILE *file, int pid, int tid)
56 {
57         struct maps *the_map = NULL;
58         char line[1024];
59         int ret;
60
61         while (fgets(line, sizeof(line), file)) {
62                 struct maps *map = alloc_map();
63                 unsigned long start, end;
64                 char name[1024];
65
66                 if (map == NULL)
67                         return 0;
68
69                 if (the_map == NULL)
70                         the_map = map;
71
72                 ret = sscanf(line, "%lx-%lx %*s %*s %*s %*s %s",
73                              &start, &end, name);
74
75                 if (ret < 2) {
76                         printf("Error reading input: %s\n", line);
77                         break;
78                 }
79
80                 map->start = start;
81                 map->end = end;
82                 map->size = end - start;
83                 map->pid = pid;
84                 map->tid = tid;
85
86                 if (ret >= 3)
87                         strncpy(map->name, name, sizeof(map->name));
88
89                 list_add_tail(&map->list, &the_map->list);
90         }
91
92         return the_map;
93 }
94
95 static struct pageframe *alloc_pageframe(void)
96 {
97         struct pageframe *pageframe;
98
99         pageframe = malloc(sizeof *pageframe);
100         if (pageframe == NULL)
101                 goto err;
102
103         clear_pageframe(pageframe);
104 err:
105         return 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 rb_root *root,
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;
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                         fprintf(stderr, "Error seeking to %lx: %m\n", start);
194                         continue;
195                 }
196
197                 for (i = 0; i < len; i++) {
198                         if (!ret) {
199                                 ret = fread(&pf, 1,
200                                         MIN(sizeof(pf), (len - i) * 8), file);
201                         }
202                         if (ret < 0) {
203                                 continue;
204                         }
205                         if (!pageframe)
206                                 pageframe = alloc_pageframe();
207                         ret -= sizeof(pf[0]);
208
209                         /* ignore unused pages */
210                         if (!pf[ret / sizeof(pf[0])])
211                                 continue;
212
213                         pageframe->pf = (pf[ret / sizeof(pf[0])]);
214
215                         /* ignore unused pages */
216                         if (!(page_swapped(pageframe) ||
217                                         page_present(pageframe)))
218                                 continue;
219
220                         if (should_add_to_tree(opts, pageframe, map))
221                                 match = pf_insert(root, pageframe);
222                         else
223                                 match = pf_search(root, pageframe);
224
225                         if (match == NULL)
226                                 continue;
227
228                         if (match == pageframe)
229                                 pageframe = NULL;
230
231                         match->refcount++;
232                         /*
233                          * Add a link from the physical page to this
234                          * process's page map
235                          */
236                         tmp = alloc_maplist();
237                         tmp->map = map;
238                         list_add(&tmp->list, &match->ml);
239
240                         if (page_present(match))
241                                 map->pages_present++;
242                         else if (page_swapped(match))
243                                 map->pages_swapped++;
244                 }
245         }
246
247         return 0;
248 }
249
250 static int read_pageframe(int pid, int tid, struct rb_root *root,
251                         struct process *process_list, struct parse_opts *opts)
252 {
253         struct maps *maps;
254         struct process *process;
255         struct pidlist *pidl, *n;
256         FILE *file;
257         char path[512];
258
259         process = calloc(sizeof(*process), 1);
260         INIT_LIST_HEAD(&process->list);
261
262         process->pid = pid;
263         process->tid = tid;
264
265         if (!should_scan_process(opts, process))
266                 goto free;
267
268         snprintf(path, sizeof(path), "/proc/%d/task/%d/maps", pid, tid);
269         file = fopen(path, "rb");
270
271         if (!file)
272                 goto free;
273
274         maps = parse_maps(file, pid, tid);
275         fclose(file);
276         process->maps = maps;
277
278         snprintf(path, sizeof(path), "/proc/%d/task/%d/pagemap", pid, tid);
279         file = fopen(path, "rb");
280
281         if (!file)
282                 goto free;
283
284         parse_pageframe(file, root, maps, opts);
285         fclose(file);
286
287         if (read_cmdline(pid, tid, process->name, sizeof(process->name)))
288                 goto free;
289
290         if (maps != NULL) {
291                 list_for_each_entry(maps, &process->maps->list, list) {
292                         process->pages_present += maps->pages_present;
293                         process->pages_swapped += maps->pages_swapped;
294                 }
295         }
296
297         if (!is_parse_option(opts, PARSE_NOADD_TREE))
298                 process->is_initial_pid = 1;
299
300         list_add_tail(&process->list, &process_list->list);
301
302         return 1;
303 free:
304         free(process);
305
306         /*
307          * Remove the pid from the list. It is no longer an
308          * interesting pid, since we can't access its data
309          */
310         list_for_each_entry_safe(pidl, n, &opts->pidlist, list) {
311                 if (pidl->pid == pid) {
312                         list_del(&pidl->list);
313                         free(pidl);
314                         break;
315                 }
316         }
317
318         return 0;
319 }
320
321 static int read_pageframe_with_threads(int pid,
322                                 struct rb_root *root,
323                                 struct process *process_list,
324                                 struct parse_opts *opts)
325 {
326         DIR *dir = NULL;
327         int tid;
328         int count = 0;
329
330         while (1) {
331                 if (opts->with_threads)
332                         tid = get_next_tid(pid, &dir);
333                 else
334                         tid = pid;
335
336                 if (tid <= 0)
337                         return count;
338
339                 count += read_pageframe(pid, tid, root, process_list, opts);
340
341                 if (!opts->with_threads)
342                         break;
343         }
344
345         return count;
346 }
347
348 int scan_all_pids(struct rb_root *root, struct process *process_list,
349                 struct parse_opts *opts)
350 {
351         struct pidlist *pidlist, *n;
352         DIR *dir = NULL;
353         int pid;
354         int count = 0;
355         int len = 0, i;
356
357         if (is_parse_option(opts, PARSE_PID)) {
358                 list_for_each_entry_safe(pidlist, n, &opts->pidlist, list) {
359                         count += read_pageframe_with_threads(pidlist->pid, root,
360                                                         process_list, opts);
361                 }
362         }
363
364         if ((count == 0) && !(is_parse_option(opts, PARSE_MAP_NAME))) {
365                 printf("Failed to find any matching processes "
366                         "with given arguments\n");
367                 return -1;
368         }
369
370         if (is_parse_option(opts, PARSE_DUMP))
371                 return 0;
372
373         if (is_parse_option(opts, PARSE_MAP_NAME) &&
374                 !is_parse_option(opts, PARSE_PID)) {
375                 printf("Scanning page mappings for process: ");
376                 while (1) {
377                         pid = get_next_pid(&dir);
378                         if (pid <= 0)
379                                 break;
380
381                         for (i = 0; i < len; i++)
382                                 putchar('\b');
383                         len = printf("% 5d", pid);
384                         fflush(stdout);
385
386                         read_pageframe_with_threads(pid, root, process_list,
387                                                 opts);
388                 }
389
390                 for (i = 0; i < len; i++)
391                         putchar('\b');
392                 printf("Done  \n");
393                 len = 0;
394         }
395         /* Do not add new pages in the tree after the initial scan */
396         opts->parse_mask |= PARSE_NOADD_TREE;
397
398         printf("Scanning page mappings for process: ");
399         while (1) {
400                 pid = get_next_pid(&dir);
401                 if (pid <= 0)
402                         break;
403
404                 for (i = 0; i < len; i++)
405                         putchar('\b');
406                 len = printf("% 5d", pid);
407                 fflush(stdout);
408
409                 read_pageframe_with_threads(pid, root, process_list, opts);
410         }
411         for (i = 0; i < len; i++)
412                 putchar('\b');
413         printf("Done  \n");
414
415         return 0;
416 }
417
418 int update_kpageflags(struct rb_root *root)
419 {
420         struct pageframe *pf;
421         int kpageflags_fd;
422         int kpagecount_fd;
423         int ret;
424
425         kpageflags_fd = open("/proc/kpageflags", O_RDONLY);
426         kpagecount_fd = open("/proc/kpagecount", O_RDONLY);
427
428         if (kpageflags_fd == -1 || kpagecount_fd == -1)
429                 return -1;
430
431         pf = rb_to_pageframe(rb_first(root));
432
433         while(pf) {
434                 long int pfnn = pfn(pf) * sizeof(pf->kpageflags);
435
436                 ret = lseek(kpageflags_fd, pfnn, SEEK_SET);
437                 if (ret < 0) {
438                         fprintf(stderr, "Error seeking to %lx: %m\n", pfnn);
439                         return -1;
440                 }
441
442                 ret = read(kpageflags_fd, &pf->kpageflags,
443                         sizeof(pf->kpageflags));
444                 if (ret < 0) {
445                         fprintf(stderr, "Error reading from %llx: %m\n",
446                                 pf->pf * sizeof(pf->kpageflags));
447                         return -1;
448                 }
449
450                 ret = lseek(kpagecount_fd, pfnn, SEEK_SET);
451                 if (ret < 0) {
452                         fprintf(stderr, "Error seeking to %lx: %m\n", pfnn);
453                         return -1;
454                 }
455
456                 ret = read(kpagecount_fd, &pf->kpagecount,
457                         sizeof(pf->kpagecount));
458                 if (ret < 0) {
459                         fprintf(stderr, "Error reading from %llx: %m\n",
460                                 pf->pf * sizeof(pf->kpagecount));
461                         return -1;
462                 }
463
464                 pf = rb_to_pageframe(rb_next(&pf->tree));
465         }
466
467         close(kpageflags_fd);
468         close(kpagecount_fd);
469
470         return 0;
471 }