2 * Copyright (C) 2010 Timo Kokkonen <kaapeli@itanic.dy.fi>
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.
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.
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.
18 #include <sys/types.h>
31 static struct maps_list *alloc_maplist(void)
33 struct maps_list *map;
35 map = calloc(sizeof *map, 1);
39 INIT_LIST_HEAD(&map->list);
44 static struct maps *alloc_map(void)
48 map = calloc(sizeof(*map), 1);
52 INIT_LIST_HEAD(&map->list);
57 static struct maps *parse_maps(FILE *file, int pid, int tid)
59 struct maps *the_map = NULL;
63 while (fgets(line, sizeof(line), file)) {
64 struct maps *map = alloc_map();
65 unsigned long start, end;
74 ret = sscanf(line, "%lx-%lx %*s %*s %*s %*s %s",
78 printf("Error reading input: %s\n", line);
84 map->size = end - start;
89 strncpy(map->name, name, sizeof(map->name));
91 list_add_tail(&map->list, &the_map->list);
97 static struct pageframe *alloc_pageframe(void)
99 struct pageframe *pageframe;
101 pageframe = malloc(sizeof *pageframe);
102 if (pageframe == NULL)
105 clear_pageframe(pageframe);
110 static int compare_pageframe(struct bintree *at, struct bintree *bt)
112 struct pageframe *a, *b;
113 a = tree_to_pageframe(at);
114 b = tree_to_pageframe(bt);
116 return a->pf - b->pf;
119 struct bintree_ops pageframe_ops = {
120 .compare = compare_pageframe,
123 static int pid_is_match(int pidn, struct list_head *pidlist)
127 list_for_each_entry(pid, pidlist, list) {
128 if (pid->pid == pidn)
135 static int should_scan_process(struct parse_opts *opts, struct process *process)
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))
144 if (is_parse_option(opts, PARSE_PID)) {
145 if (pid_is_match(process->pid, &opts->pidlist))
149 if (is_parse_option(opts, PARSE_MAP_NAME))
153 if (is_parse_option(opts, PARSE_NOADD_TREE))
159 static int should_scan_mapping(struct parse_opts *opts, struct maps *map)
163 if (is_parse_option(opts, PARSE_MAP_NAME)) {
164 if (strstr(map->name, opts->name))
167 if (is_parse_option(opts, PARSE_NOADD_TREE))
175 static int should_add_to_tree(struct parse_opts *opts, struct pageframe *pf,
178 if (is_parse_option(opts, PARSE_NOADD_TREE))
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)
189 struct maps_list *tmp;
190 struct pageframe *match, *pageframe = NULL;
192 unsigned long long pf[10240];
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);
203 if (!should_scan_mapping(opts, map))
206 ret = fseek(file, start, SEEK_SET);
209 fprintf(stderr, "Error seeking to %lx: %s\n", start,
214 for (i = 0; i < len; i++) {
217 MIN(sizeof(pf), (len - i) * 8), file);
224 pageframe = alloc_pageframe();
225 ret -= sizeof(pf[0]);
227 /* ignore unused pages */
228 if (!pf[ret / sizeof(pf[0])])
231 pageframe->pf = (pf[ret / sizeof(pf[0])]);
233 /* ignore unused pages */
234 if (!(page_swapped(pageframe) ||
235 page_present(pageframe)))
238 if (should_add_to_tree(opts, pageframe, map)) {
239 match = tree_to_pageframe(
240 bintree_add(&pf_tree->tree,
244 match = tree_to_pageframe(
245 bintree_find(&pf_tree->tree,
253 if (match == pageframe)
258 * Add a link from the physical page to this
261 tmp = alloc_maplist();
263 list_add(&tmp->list, &match->ml);
265 if (page_present(match))
266 map->pages_present++;
267 else if (page_swapped(match))
268 map->pages_swapped++;
275 static int read_pageframe(int pid, int tid, struct pageframe *pageframe,
276 struct process *process_list, struct parse_opts *opts)
279 struct process *process;
280 struct pidlist *pidl, *n;
284 process = calloc(sizeof(*process), 1);
285 INIT_LIST_HEAD(&process->list);
290 if (!should_scan_process(opts, process))
293 snprintf(path, sizeof(path), "/proc/%d/task/%d/maps", pid, tid);
294 file = fopen(path, "rb");
299 maps = parse_maps(file, pid, tid);
301 process->maps = maps;
303 snprintf(path, sizeof(path), "/proc/%d/task/%d/pagemap", pid, tid);
304 file = fopen(path, "rb");
309 parse_pageframe(file, pageframe, maps, opts);
312 if (read_cmdline(pid, tid, process->name, sizeof(process->name)))
316 list_for_each_entry(maps, &process->maps->list, list) {
317 process->pages_present += maps->pages_present;
318 process->pages_swapped += maps->pages_swapped;
322 if (!is_parse_option(opts, PARSE_NOADD_TREE))
323 process->is_initial_pid = 1;
325 list_add_tail(&process->list, &process_list->list);
332 * Remove the pid from the list. It is no longer an
333 * interesting pid, since we can't access its data
335 list_for_each_entry_safe(pidl, n, &opts->pidlist, list) {
336 if (pidl->pid == pid) {
337 list_del(&pidl->list);
346 static int read_pageframe_with_threads(int pid,
347 struct pageframe *pageframe,
348 struct process *process_list,
349 struct parse_opts *opts)
356 if (opts->with_threads)
357 tid = get_next_tid(pid, &dir);
364 count += read_pageframe(pid, tid, pageframe, process_list,
367 if (!opts->with_threads)
374 int scan_all_pids(struct pageframe *pf, struct process *process_list,
375 struct parse_opts *opts)
377 struct pidlist *pidlist, *n;
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,
390 if ((count == 0) && !(is_parse_option(opts, PARSE_MAP_NAME))) {
391 printf("Failed to find any matching processes "
392 "with given arguments\n");
396 if (is_parse_option(opts, PARSE_DUMP))
399 if (is_parse_option(opts, PARSE_MAP_NAME) &&
400 !is_parse_option(opts, PARSE_PID)) {
401 printf("Scanning page mappings for process: ");
403 pid = get_next_pid(&dir);
407 for (i = 0; i < len; i++)
409 len = printf("% 5d", pid);
412 read_pageframe_with_threads(pid, pf, process_list,
416 for (i = 0; i < len; i++)
421 /* Do not add new pages in the tree after the initial scan */
422 opts->parse_mask |= PARSE_NOADD_TREE;
424 printf("Scanning page mappings for process: ");
426 pid = get_next_pid(&dir);
430 for (i = 0; i < len; i++)
432 len = printf("% 5d", pid);
435 read_pageframe_with_threads(pid, pf, process_list, opts);
437 for (i = 0; i < len; i++)
444 struct kpageflag_data {
445 struct bintree_ops ops;
451 #define bintree_ops_to_kpfd(bintree_ops) \
452 container_of((bintree_ops), struct kpageflag_data, ops)
454 static void _update_kpageflags(struct bintree *bt, struct bintree_ops *ops)
456 struct pageframe *pf = tree_to_pageframe(bt);
457 struct kpageflag_data *kpfd = bintree_ops_to_kpfd(ops);
459 long int pfnn = pfn(pf) * sizeof(pf->kpageflags);
461 ret = lseek(kpfd->kpageflags_fd, pfnn, SEEK_SET);
464 fprintf(stderr, "Error seeking to %lx: %s\n",
465 pfnn, strerror(error));
469 ret = read(kpfd->kpageflags_fd, &pf->kpageflags,
470 sizeof(pf->kpageflags));
473 fprintf(stderr, "Error reading from %llx: %s\n",
474 pf->pf * sizeof(pf->kpageflags),
479 ret = lseek(kpfd->kpagecount_fd, pfnn, SEEK_SET);
482 fprintf(stderr, "Error seeking to %lx: %s\n",
483 pfnn, strerror(error));
487 ret = read(kpfd->kpagecount_fd, &pf->kpagecount,
488 sizeof(pf->kpagecount));
491 fprintf(stderr, "Error reading from %llx: %s\n",
492 pf->pf * sizeof(pf->kpagecount),
498 int update_kpageflags(struct pageframe *pf)
500 struct kpageflag_data kpfd = {
502 .callback = _update_kpageflags,
504 .kpageflags_fd = open("/proc/kpageflags", O_RDONLY),
505 .kpagecount_fd = open("/proc/kpagecount", O_RDONLY),
508 if (kpfd.kpageflags_fd == -1 || kpfd.kpagecount_fd == -1)
511 bintree_walk(&pf->tree, &kpfd.ops);
513 close(kpfd.kpageflags_fd);
514 close(kpfd.kpagecount_fd);