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