]> git.itanic.dy.fi Git - scan-pagemap/blobdiff - parse.c
parser: Fix the order of argumets for list_add()
[scan-pagemap] / parse.c
diff --git a/parse.c b/parse.c
index 32660e2007e5217d02536b0bce937c075582117d..850b2a1a55938961f493e0969c69839d72c725e9 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -4,6 +4,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <libgen.h>
 
 #include "parse.h"
 #include "pagemap.h"
@@ -36,13 +37,13 @@ err:
        return map;
 }
 
-static struct maps *parse_maps(FILE *file, int pid)
+static struct maps *parse_maps(FILE *file, int pid, int tid)
 {
        struct maps *the_map = NULL;
        char line[1024];
        int ret;
 
-       while(fgets(line, sizeof(line), file)) {
+       while (fgets(line, sizeof(line), file)) {
                struct maps *map = alloc_map();
                unsigned long start, end;
                char name[1024];
@@ -56,9 +57,6 @@ static struct maps *parse_maps(FILE *file, int pid)
                ret = sscanf(line, "%lx-%lx %*s %*s %*s %*s %s",
                             &start, &end, name);
 
-               printf("%x, start %lx, end %lx, str: %s\n",
-                       ret, start, end, ret >= 3 ? name : "");
-
                if (ret < 2) {
                        printf("Error reading input: %s\n", line);
                        break;
@@ -68,6 +66,7 @@ static struct maps *parse_maps(FILE *file, int pid)
                map->end = end;
                map->size = end - start;
                map->pid = pid;
+               map->tid = tid;
 
                if (ret >= 3)
                        strncpy(map->name, name, sizeof(map->name));
@@ -100,6 +99,7 @@ err:
 
 static void pageframe_to_struct(unsigned long long p, struct pageframe *pf)
 {
+       /* 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;
@@ -125,24 +125,85 @@ struct bintree_ops pageframe_ops = {
        .compare = compare_pageframe,
 };
 
+static int read_cmdline(int pid, int tid, char *cmdline, size_t len)
+{
+       FILE *file;
+       char path[512];
+       int ret;
+
+       snprintf(path, sizeof(path), "/proc/%d/task/%d/cmdline", pid, tid);
+       file = fopen(path, "rb");
+
+       if (!file)
+               return -1;
+
+       ret = fread(cmdline, 1, len, file);
+       if (ret > 0)
+               cmdline[ret - 1] = 0;
+       fclose(file);
+
+       return ret > 0 ? 0 : -1;
+}
+
+static char *get_name_by_pid(int pid)
+{
+       static int last_pid;
+       static char cmdline[128];
+       static char *bname;
+
+       if (last_pid == pid)
+               return bname;
+
+       if (read_cmdline(pid, pid, cmdline, sizeof(cmdline))) {
+               bname = NULL;
+               return NULL;
+       }
+
+       bname = basename(cmdline);
+
+       last_pid = pid;
+       return bname;
+}
+
+static int check_parse_opts(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;
+       }
+
+       return 0;
+}
+
 /* Read data from the /proc/pid/pagemap file */
 static int parse_pageframe(FILE *file, struct pageframe *pf_tree,
-                       struct maps *maps)
+                       struct maps *maps, struct parse_opts *opts)
 {
        struct maps *map;
        struct maps_list *tmp;
-       struct pageframe *match, *pageframe;
+       struct pageframe *match, *pageframe = NULL;
        long start, len, i;
-       unsigned long long pf;
+       unsigned long long pf[10240];
        int ret, error;
 
+       if (maps == NULL)
+               return 0;
+
        /* Go through the list of allocated memory areas */
        list_for_each_entry(map, &maps->list, list) {
                start = map->start >> (PAGE_SHIFT - 3);
-               len = map->size >> (PAGE_SHIFT - 3);
-
-               printf("start: %lx len %lx: name: %s\n",
-                       start, len, map->name);
+               len = map->size >> (PAGE_SHIFT);
 
                ret = fseek(file, start, SEEK_SET);
                if (ret) {
@@ -150,27 +211,50 @@ static int parse_pageframe(FILE *file, struct pageframe *pf_tree,
                        fprintf(stderr, "Error seeking to %lx: %s\n", start,
                                strerror(error));
                        continue;
-                       return -1;
                }
 
                for (i = 0; i < len; i++) {
-                       ret = fread(&pf, 1, sizeof(pf), file);
-                       if (ret != sizeof(pf)) {
+                       if (!ret) {
+                               ret = fread(&pf, 1,
+                                       MIN(sizeof(pf), (len - i) * 8), file);
+                       }
+                       if (ret < 0) {
                                error = errno;
-                               fprintf(stderr, 
-                                       "Error reading %ld bytes, got %d: %s\n",
-                                       sizeof(pf), ret, strerror(errno));
                                continue;
-                               return -1;
                        }
-                       pageframe = alloc_pageframe();
-                       pageframe_to_struct(pf, pageframe);
-                       match = tree_to_pageframe(
-                               bintree_add(&pf_tree->tree,
-                                       &pageframe->tree, &pageframe_ops));
-
-                       if (match != pageframe)
-                               free(pageframe);
+                       if (!pageframe)
+                               pageframe = alloc_pageframe();
+                       ret -= sizeof(pf[0]);
+
+                       /* ignore unused pages */
+                       if (!pf[ret / sizeof(pf[0])])
+                               continue;
+
+                       pageframe_to_struct(pf[ret / sizeof(pf[0])], pageframe);
+
+                       /* ignore unused pages */
+                       if (!(pageframe->page_swapped ||
+                                       pageframe->page_present))
+                               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 (match == NULL)
+                               continue;
+
+                       if (match == pageframe)
+                               pageframe = NULL;
+
                        match->refcount++;
                        /*
                         * Add a link from the physical page to this
@@ -182,88 +266,222 @@ static int parse_pageframe(FILE *file, struct pageframe *pf_tree,
                        } else {
                                tmp = alloc_maplist();
                                tmp->map = map;
-                               list_add(&match->ml->list, &tmp->list);
+                               list_add(&tmp->list, &match->ml->list);
                        }
+
+                       if (match->page_present)
+                               map->pages_present++;
+                       else if (match->page_swapped)
+                               map->pages_swapped++;
                }
        }
 
        return 0;
 }
 
-void read_pageframe(int pid, struct pageframe *pageframe)
+static int read_pageframe(int pid, int tid, struct pageframe *pageframe,
+                       struct process **process_list, struct parse_opts *opts)
 {
        struct maps *maps;
+       struct process *process;
        FILE *file;
-       int error;
        char path[512];
 
-       snprintf(path, sizeof(path), "/proc/%d/maps", pid);
+       process = malloc(sizeof(*process));
+       memset(process, 0, sizeof(*process));
+       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);
+
+       snprintf(path, sizeof(path), "/proc/%d/task/%d/maps", pid, tid);
        file = fopen(path, "rb");
 
        if (!file)
-               goto err_out;
+               return 0;
 
-       maps = parse_maps(file, pid);
+       maps = parse_maps(file, pid, tid);
+       fclose(file);
+       process->maps = maps;
 
-       snprintf(path, sizeof(path), "/proc/%d/pagemap", pid);
+       snprintf(path, sizeof(path), "/proc/%d/task/%d/pagemap", pid, tid);
        file = fopen(path, "rb");
 
        if (!file)
-               goto err_out;
+               return 0;
 
-       parse_pageframe(file, pageframe, maps);
+       parse_pageframe(file, pageframe, maps, opts);
+       fclose(file);
 
-       return;
-err_out:
-       error = errno;
-       fprintf(stderr, "Failed to open file %s: %s\n", path, strerror(error));
+       if (read_cmdline(pid, tid, process->name, sizeof(process->name)))
+               return 1;
 
-       return;
+       if (maps == NULL)
+               return 1;
+
+       list_for_each_entry(maps, &process->maps->list, list) {
+               process->pages_present += maps->pages_present;
+               process->pages_swapped += maps->pages_swapped;
+       }
+
+       return 1;
 }
 
-static int get_next_pid(void)
+static int parse_pid(DIR **dir)
 {
-       static DIR *dir = NULL;
        struct dirent *dirent;
        int error;
 
-       if (!dir) {
-               dir = opendir("/proc");
-               if (!dir) {
-                       error = errno;
-                       printf("Failed to open /proc directory: %s\n",
-                               strerror(error));
-                       return -1;
-               }
-       }
-
 restart:
-       dirent = readdir(dir);
+       dirent = readdir(*dir);
        if (!dirent) {
                if (errno == 0) {
-                       closedir(dir);
-                       dir = NULL;
+                       closedir(*dir);
+                       *dir = NULL;
                        return 0;
                }
                error = errno;
-               printf("Failed to read directory: %s\n", strerror(error));
+               printf("Failed to read /proc directory: %s\n", strerror(error));
                return -1;
        }
 
-       printf("%s\n", dirent->d_name);
        if (dirent->d_name[0] < '0' || dirent->d_name[0] > '9')
                goto restart;
 
        return atoi(dirent->d_name);
 }
 
-void scan_all_pids(struct pageframe *pf)
+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)
+                       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 parse_opts *opts)
 {
+       DIR *dir = NULL;
+       int tid;
+       int count = 0;
+
+       while (1) {
+               if (opts->with_threads)
+                       tid = get_next_tid(pid, &dir);
+               else
+                       tid = pid;
+
+               if (tid <= 0)
+                       return count;
+
+               count += read_pageframe(pid, tid, pageframe, process_list,
+                                       opts);
+
+               if (!opts->with_threads)
+                       break;
+       }
+
+       return count;
+}
+
+int scan_all_pids(struct pageframe *pf, struct process **process_list,
+               struct parse_opts *opts)
+{
+       DIR *dir = NULL;
        int pid;
-       while(1) {
-               pid = get_next_pid();
+       int count = 0;
+
+       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);
+               }
+               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)) {
+               printf("Failed to find any matching processes "
+                       "with given arguments\n");
+               return -1;
+       }
+
+       if (opts->parse_mask & PARSE_DUMP)
+               return 0;
+
+       while (1) {
+               pid = get_next_pid(&dir);
                if (pid <= 0)
                        break;
-               read_pageframe(pid, pf);
+               read_pageframe_with_threads(pid, pf, process_list, opts);
        }
+
+       return 0;
 }