]> git.itanic.dy.fi Git - scan-pagemap/blobdiff - parse.c
Show full process argument list instead only executable name
[scan-pagemap] / parse.c
diff --git a/parse.c b/parse.c
index b4057064516ffefb7fc83cc7b39a12b14fc4d364..0c9ac14ce5a5ba2463ca1327b1bde1f774f0fd6d 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -1,23 +1,39 @@
+/*
+ * Copyright (C) 2010 Timo Kokkonen <timo.t.kokkonen@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
 #include <sys/types.h>
-#include <dirent.h>
+#include <sys/stat.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <libgen.h>
+#include <fcntl.h>
+#include <unistd.h>
 
 #include "parse.h"
 #include "pagemap.h"
+#include "pidlib.h"
 
 static struct maps_list *alloc_maplist(void)
 {
        struct maps_list *map;
 
-       map = malloc(sizeof *map);
+       map = calloc(sizeof *map, 1);
        if (map == NULL)
                goto err;
 
-       memset(map, 0, sizeof(*map));
        INIT_LIST_HEAD(&map->list);
 err:
        return map;
@@ -27,11 +43,10 @@ static struct maps *alloc_map(void)
 {
        struct maps *map;
 
-       map = malloc(sizeof *map);
+       map = calloc(sizeof(*map), 1);
        if (map == NULL)
                goto err;
 
-       memset(map, 0, sizeof(*map));
        INIT_LIST_HEAD(&map->list);
 err:
        return map;
@@ -46,7 +61,7 @@ static struct maps *parse_maps(FILE *file, int pid, int tid)
        while (fgets(line, sizeof(line), file)) {
                struct maps *map = alloc_map();
                unsigned long start, end;
-               char name[1024];
+               int skip;
 
                if (map == NULL)
                        return 0;
@@ -54,8 +69,8 @@ static struct maps *parse_maps(FILE *file, int pid, int tid)
                if (the_map == NULL)
                        the_map = map;
 
-               ret = sscanf(line, "%lx-%lx %*s %*s %*s %*s %s",
-                            &start, &end, name);
+               ret = sscanf(line, "%lx-%lx %*s %*s %*s %*s %n",
+                            &start, &end, &skip);
 
                if (ret < 2) {
                        printf("Error reading input: %s\n", line);
@@ -68,8 +83,10 @@ static struct maps *parse_maps(FILE *file, int pid, int tid)
                map->pid = pid;
                map->tid = tid;
 
-               if (ret >= 3)
-                       strncpy(map->name, name, sizeof(map->name));
+               strncpy(map->name, line + skip, sizeof(map->name) - 1);
+
+               /* zero out the newline */
+               map->name[MAX(strlen(map->name) - 1, 0)] = '\0';
 
                list_add_tail(&map->list, &the_map->list);
        }
@@ -77,11 +94,6 @@ static struct maps *parse_maps(FILE *file, int pid, int tid)
        return the_map;
 }
 
-static void clear_pageframe(struct pageframe *pf)
-{
-       memset(pf, 0, sizeof(*pf));
-}
-
 static struct pageframe *alloc_pageframe(void)
 {
        struct pageframe *pageframe;
@@ -95,99 +107,69 @@ err:
        return pageframe;
 }
 
-#define BITRANGE(first, last) (((2ll << (last - first)) - 1) << first)
-
-static void pageframe_to_struct(unsigned long long p, struct pageframe *pf)
+static int pid_is_match(int pidn, struct list_head *pidlist)
 {
-       /* Refer Documentation/vm/pagemap.txt for the format */
-       pf->page_present = !!(BITRANGE(63, 63) & p);
-       pf->page_swapped = !!(BITRANGE(62, 62) & p);
-       pf->page_shift   =   (BITRANGE(55, 60) & p) >> 55;
-       pf->pfn          =   (BITRANGE(0, 54) & p);
-       pf->swap_type    =   (BITRANGE(0, 4) & p);
-       pf->swap_offset  =   (BITRANGE(5, 54) & p) >> 5;
-#if 0
-       printf("pfn: %lx shift: %d present: %d swapped %d\n",
-               pf->pfn, pf->page_shift, pf->page_present, pf->page_swapped);
-#endif
-}
+       struct pidlist *pid;
 
-static int compare_pageframe(struct bintree *at, struct bintree *bt)
-{
-       struct pageframe *a, *b;
-       a = tree_to_pageframe(at);
-       b = tree_to_pageframe(bt);
+       list_for_each_entry(pid, pidlist, list) {
+               if (pid->pid == pidn)
+                       return 1;
+       }
 
-       return a->pfn - b->pfn;
+       return 0;
 }
 
-struct bintree_ops pageframe_ops = {
-       .compare = compare_pageframe,
-};
-
-static int read_cmdline(int pid, int tid, char *cmdline, size_t len)
+static int should_scan_process(struct parse_opts *opts, struct process *process)
 {
-       FILE *file;
-       char path[512];
-       int ret;
-
-       snprintf(path, sizeof(path), "/proc/%d/task/%d/cmdline", pid, tid);
-       file = fopen(path, "rb");
+       int match = 0;
+
+       if (is_parse_option(opts, PARSE_PID) &&
+               is_parse_option(opts, PARSE_MAP_NAME)) {
+               if (pid_is_match(process->pid, &opts->pidlist))
+                       match = 1;
+       } else {
+               if (is_parse_option(opts, PARSE_PID)) {
+                       if (pid_is_match(process->pid, &opts->pidlist))
+                               match = 1;
+               }
 
-       if (!file)
-               return -1;
+               if (is_parse_option(opts, PARSE_MAP_NAME))
+                       match = 1;
+       }
 
-       ret = fread(cmdline, 1, len, file);
-       if (ret > 0)
-               cmdline[ret - 1] = 0;
-       fclose(file);
+       if (is_parse_option(opts, PARSE_NOADD_TREE))
+               match = !match;
 
-       return ret > 0 ? 0 : -1;
+       return match;
 }
 
-static char *get_name_by_pid(int pid)
+static int should_scan_mapping(struct parse_opts *opts, struct maps *map)
 {
-       static int last_pid;
-       static char cmdline[128];
-       static char *bname;
+       int match = 0;
 
-       if (last_pid == pid)
-               return bname;
-
-       if (read_cmdline(pid, pid, cmdline, sizeof(cmdline))) {
-               bname = NULL;
-               return NULL;
-       }
+       if (is_parse_option(opts, PARSE_MAP_NAME)) {
+               if (strstr(map->name, opts->name))
+                       match = 1;
 
-       bname = basename(cmdline);
+               if (is_parse_option(opts, PARSE_NOADD_TREE))
+                       match = !match;
+       } else
+               match = 1;
 
-       last_pid = pid;
-       return bname;
+       return match;
 }
 
-static int check_parse_opts(struct parse_opts *opts, struct pageframe *pf,
+static int should_add_to_tree(struct parse_opts *opts, struct pageframe *pf,
                struct maps *map)
 {
-       if (opts->parse_mask & PARSE_PID) {
-               if (opts->pid == map->pid)
-                       return 1;
-       }
-
-       if (opts->parse_mask & PARSE_MAP_NAME) {
-               if (!strcmp(opts->name, map->name))
-                       return 1;
-       }
-
-       if (opts->parse_mask & PARSE_PROCESS_NAME) {
-               if (!strcmp(opts->name, get_name_by_pid(map->pid)))
-                       return 1;
-       }
+       if (is_parse_option(opts, PARSE_NOADD_TREE))
+               return 0;
 
-       return 0;
+       return 1;
 }
 
 /* Read data from the /proc/pid/pagemap file */
-static int parse_pageframe(FILE *file, struct pageframe *pf_tree,
+static int parse_pageframe(FILE *file, struct rb_root *root,
                        struct maps *maps, struct parse_opts *opts)
 {
        struct maps *map;
@@ -195,7 +177,7 @@ static int parse_pageframe(FILE *file, struct pageframe *pf_tree,
        struct pageframe *match, *pageframe = NULL;
        long start, len, i;
        unsigned long long pf[10240];
-       int ret, error;
+       int ret;
 
        if (maps == NULL)
                return 0;
@@ -205,11 +187,12 @@ static int parse_pageframe(FILE *file, struct pageframe *pf_tree,
                start = map->start >> (PAGE_SHIFT - 3);
                len = map->size >> (PAGE_SHIFT);
 
+               if (!should_scan_mapping(opts, map))
+                       continue;
+
                ret = fseek(file, start, SEEK_SET);
                if (ret) {
-                       error = errno;
-                       fprintf(stderr, "Error seeking to %lx: %s\n", start,
-                               strerror(error));
+                       fprintf(stderr, "Error seeking to %lx: %m\n", start);
                        continue;
                }
 
@@ -219,7 +202,6 @@ static int parse_pageframe(FILE *file, struct pageframe *pf_tree,
                                        MIN(sizeof(pf), (len - i) * 8), file);
                        }
                        if (ret < 0) {
-                               error = errno;
                                continue;
                        }
                        if (!pageframe)
@@ -230,24 +212,17 @@ static int parse_pageframe(FILE *file, struct pageframe *pf_tree,
                        if (!pf[ret / sizeof(pf[0])])
                                continue;
 
-                       pageframe_to_struct(pf[ret / sizeof(pf[0])], pageframe);
+                       pageframe->pf = (pf[ret / sizeof(pf[0])]);
 
                        /* ignore unused pages */
-                       if (!(pageframe->page_swapped ||
-                                       pageframe->page_present))
+                       if (!(page_swapped(pageframe) ||
+                                       page_present(pageframe)))
                                continue;
 
-                       if (check_parse_opts(opts, pageframe, map)) {
-                               match = tree_to_pageframe(
-                                       bintree_add(&pf_tree->tree,
-                                               &pageframe->tree,
-                                               &pageframe_ops));
-                       } else {
-                               match = tree_to_pageframe(
-                                       bintree_find(&pf_tree->tree,
-                                               &pageframe->tree,
-                                               &pageframe_ops));
-                       }
+                       if (should_add_to_tree(opts, pageframe, map))
+                               match = pf_insert(root, pageframe);
+                       else
+                               match = pf_search(root, pageframe);
 
                        if (match == NULL)
                                continue;
@@ -260,18 +235,13 @@ static int parse_pageframe(FILE *file, struct pageframe *pf_tree,
                         * Add a link from the physical page to this
                         * process's page map
                         */
-                       if (!match->ml) {
-                               match->ml = alloc_maplist();
-                               match->ml->map = map;
-                       } else {
-                               tmp = alloc_maplist();
-                               tmp->map = map;
-                               list_add(&match->ml->list, &tmp->list);
-                       }
+                       tmp = alloc_maplist();
+                       tmp->map = map;
+                       list_add(&tmp->list, &match->ml);
 
-                       if (match->page_present)
+                       if (page_present(match))
                                map->pages_present++;
-                       else if (match->page_swapped)
+                       else if (page_swapped(match))
                                map->pages_swapped++;
                }
        }
@@ -279,31 +249,29 @@ static int parse_pageframe(FILE *file, struct pageframe *pf_tree,
        return 0;
 }
 
-static int read_pageframe(int pid, int tid, struct pageframe *pageframe,
-                       struct process **process_list, struct parse_opts *opts)
+static int read_pageframe(int pid, int tid, struct rb_root *root,
+                       struct process *process_list, struct parse_opts *opts)
 {
        struct maps *maps;
        struct process *process;
+       struct pidlist *pidl, *n;
        FILE *file;
        char path[512];
 
-       process = malloc(sizeof(*process));
-       memset(process, 0, sizeof(*process));
+       process = calloc(sizeof(*process), 1);
        INIT_LIST_HEAD(&process->list);
 
-       if (*process_list == NULL)
-               *process_list = process;
-
        process->pid = pid;
        process->tid = tid;
 
-       list_add_tail(&process->list, &(*process_list)->list);
+       if (!should_scan_process(opts, process))
+               goto free;
 
        snprintf(path, sizeof(path), "/proc/%d/task/%d/maps", pid, tid);
        file = fopen(path, "rb");
 
        if (!file)
-               return 0;
+               goto free;
 
        maps = parse_maps(file, pid, tid);
        fclose(file);
@@ -313,115 +281,41 @@ static int read_pageframe(int pid, int tid, struct pageframe *pageframe,
        file = fopen(path, "rb");
 
        if (!file)
-               return 0;
+               goto free;
 
-       parse_pageframe(file, pageframe, maps, opts);
+       parse_pageframe(file, root, maps, opts);
        fclose(file);
 
        if (read_cmdline(pid, tid, process->name, sizeof(process->name)))
-               return 1;
+               goto free;
 
-       if (maps == NULL)
-               return 1;
+       if (!is_parse_option(opts, PARSE_NOADD_TREE))
+               process->is_initial_pid = 1;
 
-       list_for_each_entry(maps, &process->maps->list, list) {
-               process->pages_present += maps->pages_present;
-               process->pages_swapped += maps->pages_swapped;
-       }
+       list_add_tail(&process->list, &process_list->list);
 
        return 1;
-}
-
-static int parse_pid(DIR **dir)
-{
-       struct dirent *dirent;
-       int error;
-
-restart:
-       dirent = readdir(*dir);
-       if (!dirent) {
-               if (errno == 0) {
-                       closedir(*dir);
-                       *dir = NULL;
-                       return 0;
-               }
-               error = errno;
-               printf("Failed to read /proc directory: %s\n", strerror(error));
-               return -1;
-       }
-
-       if (dirent->d_name[0] < '0' || dirent->d_name[0] > '9')
-               goto restart;
-
-       return atoi(dirent->d_name);
-}
-
-static int opendir_check(DIR **dir, const char *path)
-{
-       int error;
-
-       if (!*dir) {
-               *dir = opendir(path);
-               if (!dir) {
-                       error = errno;
-                       fprintf(stderr, "Failed to open %s directory: %s\n",
-                               path, strerror(error));
-                       return -1;
-               }
-       }
-
-       return 0;
-}
-
-static int get_next_tid(int pid, DIR **dir)
-{
-       if (*dir == NULL) {
-               char path[64];
-
-               snprintf(path, sizeof(path), "/proc/%d/task/", pid);
-               if (opendir_check(dir, path))
-                       return -1;
-       }
-
-       return parse_pid(dir);
-}
-
-static int get_next_pid(DIR **dir)
-{
-       if (opendir_check(dir, "/proc"))
-               return -1;
-
-       return parse_pid(dir);
-}
-
-static int get_next_pid_by_name(DIR **dir, char *name)
-{
-       int pid;
-       char *pname;
-
-       if (opendir_check(dir, "/proc"))
-               return -1;
-
-       while (1) {
-               pid = parse_pid(dir);
-               if (pid <= 0)
+free:
+       free(process);
+
+       /*
+        * Remove the pid from the list. It is no longer an
+        * interesting pid, since we can't access its data
+        */
+       list_for_each_entry_safe(pidl, n, &opts->pidlist, list) {
+               if (pidl->pid == pid) {
+                       list_del(&pidl->list);
+                       free(pidl);
                        break;
-
-               pname = get_name_by_pid(pid);
-               if (pname == NULL)
-                       continue;
-               if (strcmp(pname, name))
-                       continue;
-
-               return pid;
+               }
        }
 
        return 0;
 }
 
 static int read_pageframe_with_threads(int pid,
-                               struct pageframe *pageframe,
-                               struct process **process_list,
+                               struct rb_root *root,
+                               struct process *process_list,
                                struct parse_opts *opts)
 {
        DIR *dir = NULL;
@@ -437,8 +331,7 @@ static int read_pageframe_with_threads(int pid,
                if (tid <= 0)
                        return count;
 
-               count += read_pageframe(pid, tid, pageframe, process_list,
-                                       opts);
+               count += read_pageframe(pid, tid, root, process_list, opts);
 
                if (!opts->with_threads)
                        break;
@@ -447,38 +340,129 @@ static int read_pageframe_with_threads(int pid,
        return count;
 }
 
-int scan_all_pids(struct pageframe *pf, struct process **process_list,
+int scan_all_pids(struct rb_root *root, struct process *process_list,
                struct parse_opts *opts)
 {
+       struct pidlist *pidlist, *n;
        DIR *dir = NULL;
        int pid;
        int count = 0;
+       int len = 0, i;
 
-       if (opts->parse_mask & PARSE_PROCESS_NAME) {
-               while ((pid = get_next_pid_by_name(&dir, opts->name))) {
-                       count += read_pageframe_with_threads(pid, pf,
-                                                       process_list,
-                                                       opts);
+       if (is_parse_option(opts, PARSE_PID)) {
+               list_for_each_entry_safe(pidlist, n, &opts->pidlist, list) {
+                       count += read_pageframe_with_threads(pidlist->pid, root,
+                                                       process_list, opts);
                }
-               dir = NULL;
        }
 
-       if (opts->parse_mask & PARSE_PID)
-               count = read_pageframe_with_threads(opts->pid, pf, process_list,
-                                               opts);
-
-       if ((count == 0) && !(opts->parse_mask & PARSE_MAP_NAME)) {
+       if ((count == 0) && !(is_parse_option(opts, PARSE_MAP_NAME))) {
                printf("Failed to find any matching processes "
                        "with given arguments\n");
                return -1;
        }
 
+       if (is_parse_option(opts, PARSE_DUMP))
+               return 0;
+
+       if (is_parse_option(opts, PARSE_MAP_NAME) &&
+               !is_parse_option(opts, PARSE_PID)) {
+               printf("Scanning page mappings for process: ");
+               while (1) {
+                       pid = get_next_pid(&dir);
+                       if (pid <= 0)
+                               break;
+
+                       for (i = 0; i < len; i++)
+                               putchar('\b');
+                       len = printf("% 5d", pid);
+                       fflush(stdout);
+
+                       read_pageframe_with_threads(pid, root, process_list,
+                                               opts);
+               }
+
+               for (i = 0; i < len; i++)
+                       putchar('\b');
+               printf("Done  \n");
+               len = 0;
+       }
+       /* Do not add new pages in the tree after the initial scan */
+       opts->parse_mask |= PARSE_NOADD_TREE;
+
+       printf("Scanning page mappings for process: ");
        while (1) {
                pid = get_next_pid(&dir);
                if (pid <= 0)
                        break;
-               read_pageframe_with_threads(pid, pf, process_list, opts);
+
+               for (i = 0; i < len; i++)
+                       putchar('\b');
+               len = printf("% 5d", pid);
+               fflush(stdout);
+
+               read_pageframe_with_threads(pid, root, process_list, opts);
+       }
+       for (i = 0; i < len; i++)
+               putchar('\b');
+       printf("Done  \n");
+
+       return 0;
+}
+
+int update_kpageflags(struct rb_root *root)
+{
+       struct pageframe *pf;
+       int kpageflags_fd;
+       int kpagecount_fd;
+       int ret;
+
+       kpageflags_fd = open("/proc/kpageflags", O_RDONLY);
+       if (kpageflags_fd < 0)
+               return -1;
+
+       kpagecount_fd = open("/proc/kpagecount", O_RDONLY);
+       if (kpagecount_fd < 0)
+               return -1;
+
+       pf = rb_to_pageframe(rb_first(root));
+
+       while(pf) {
+               long int pfnn = pfn(pf) * sizeof(pf->kpageflags);
+
+               ret = lseek(kpageflags_fd, pfnn, SEEK_SET);
+               if (ret < 0) {
+                       fprintf(stderr, "Error seeking to %lx: %m\n", pfnn);
+                       return -1;
+               }
+
+               ret = read(kpageflags_fd, &pf->kpageflags,
+                       sizeof(pf->kpageflags));
+               if (ret < 0) {
+                       fprintf(stderr, "Error reading from %llx: %m\n",
+                               pf->pf * sizeof(pf->kpageflags));
+                       return -1;
+               }
+
+               ret = lseek(kpagecount_fd, pfnn, SEEK_SET);
+               if (ret < 0) {
+                       fprintf(stderr, "Error seeking to %lx: %m\n", pfnn);
+                       return -1;
+               }
+
+               ret = read(kpagecount_fd, &pf->kpagecount,
+                       sizeof(pf->kpagecount));
+               if (ret < 0) {
+                       fprintf(stderr, "Error reading from %llx: %m\n",
+                               pf->pf * sizeof(pf->kpagecount));
+                       return -1;
+               }
+
+               pf = rb_to_pageframe(rb_next(&pf->tree));
        }
 
+       close(kpageflags_fd);
+       close(kpagecount_fd);
+
        return 0;
 }