]> git.itanic.dy.fi Git - scan-pagemap/blobdiff - analyze.c
Add support for listing mappings that are shared between processes
[scan-pagemap] / analyze.c
index 309043179c97c4fb321b0e1f63c874aed7278388..7a67fb47cc0370b71008fdf7c149b6692c15597e 100644 (file)
--- a/analyze.c
+++ b/analyze.c
 
 struct analyze_frames {
        struct bintree_ops ops;
+
+       int pid;
+
+       struct list_head *pidlist;
+       struct maps *map;
+       int pids;
+
        long int pages_present;
        long int pages_swapped;
        long int pages_unique;
-       int pid;
 };
 
 #define bintree_ops_to_af(bintree_ops)                         \
@@ -42,11 +48,61 @@ static void count_pages(struct bintree *b, struct bintree_ops *ops)
        struct maps_list *ml;
 
        if (af->pid) {
+               /* Find pages which reference at least once a pid */
                list_for_each_entry(ml, &pf->ml, list) {
                        if (ml->map->pid == af->pid)
                                goto get_stats;
                }
                return;
+       } else if (af->pidlist && af->map) {
+               /*
+                * Find pages that reference at least once all of the
+                * given pids and a given mapping
+                */
+               struct pidlist *pid;
+               int matches = 0;
+
+               /*
+                * Check that we reference the given mapping at least
+                * once
+                */
+               list_for_each_entry(ml, &pf->ml, list) {
+                       if (ml->map == af->map) {
+                               matches++;
+                               break;
+                       }
+               }
+
+               if (!matches)
+                       return;
+               matches = 0;
+
+               /*
+                * Check that we reference all of the given pids
+                * too. The order of the loops is important here. We
+                * must scan through all the references and test for a
+                * given pid. If we would iterate through the
+                * references in the outer loop, we might get
+                * duplicate matches for a pid since it is possible
+                * that a page is mapped multiple times in a process's
+                * addrses space.
+                */
+               list_for_each_entry(pid, af->pidlist, list) {
+                       list_for_each_entry(ml, &pf->ml, list) {
+                               if (ml->map->pid == pid->pid) {
+                                       matches++;
+                                       break;
+                               }
+                       }
+
+                       /*
+                        * If we have found as many matches as ther
+                        * are pids, we will count the stats
+                        */
+                       if (matches == af->pids)
+                               goto get_stats;
+               }
+               return;
        }
 
 get_stats:
@@ -161,14 +217,35 @@ restart:
        printf("Total %d processes\n", processes);
 }
 
-static void _dump_process_maps(struct process *ps)
+static void _dump_process_maps(struct pageframe *pf, struct process *ps,
+                       struct parse_opts *opts)
 {
        struct maps *map;
        long int swapped, present, total;
        long int biggest = 0, second_biggest;
-       int count, processes = 0;
+       int count, processes = 0, pids = 0;
+
+       if (is_parse_option(opts, PARSE_SHARED_MAPPING)) {
+               struct pidlist *pid;
+               list_for_each_entry(pid, &opts->pidlist, list)
+                       pids++;
+       }
 
        list_for_each_entry(map, &ps->maps->list, list) {
+               struct analyze_frames af;
+
+               if (is_parse_option(opts, PARSE_SHARED_MAPPING)) {
+                       memset(&af, 0, sizeof(af));
+                       af.ops.callback = count_pages;
+                       af.pidlist = &opts->pidlist;
+                       af.pids = pids;
+                       af.map = map;
+
+                       bintree_walk(&pf->tree, &af.ops);
+                       map->pages_present = af.pages_present;
+                       map->pages_swapped = af.pages_swapped;
+               }
+
                biggest = MAX(biggest, map->pages_present + map->pages_swapped);
        }
 
@@ -190,6 +267,13 @@ restart:
                if (total != biggest)
                        continue;
 
+               /*
+                * Do not print zero sized mappings if
+                * --shared-mappings is enabled
+                */
+               if (is_parse_option(opts, PARSE_SHARED_MAPPING) && total == 0)
+                       continue;
+
                printf("%5lld %sB %5lld %sB %5lld %sB %5lld %sB %s\n",
                        NICE_DIV(map->size), NICE_UNIT(map->size),
                        PAGE_TO_NICE(present), PAGE_TO_NICE_UNIT(present),
@@ -208,11 +292,12 @@ restart:
        printf("\n");
 }
 
-void dump_process_maps(struct process *process_list)
+void dump_process_maps(struct pageframe *pf, struct process *process_list,
+               struct parse_opts *opts)
 {
        struct process *ps;
 
        list_for_each_entry(ps, &process_list->list, list) {
-               _dump_process_maps(ps);
+               _dump_process_maps(pf, ps, opts);
        }
 }