]> 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 c9b99e3c1b7c4b7d711ede51339187a4423dc049..7a67fb47cc0370b71008fdf7c149b6692c15597e 100644 (file)
--- a/analyze.c
+++ b/analyze.c
@@ -9,16 +9,16 @@
 #define SI_M   (SI_k * SI_k)
 #define SI_G   (SI_M * SI_k)
 
-#define PRETTY_THRESH  100
+#define PRETTY_THRESH  98
 #define NICE_DIV(a)                                                    \
-       ((a) < SI_k * PRETTY_THRESH ? (a) :                             \
+       ((a) < SI_k * 4 ? (a) :                         \
                (a < SI_M * PRETTY_THRESH ? ((a) / SI_k) :              \
                        (a < SI_G * PRETTY_THRESH ? ((a) / SI_M) :      \
                                ((a) / SI_G))))
 
 #define NICE_UNIT(a)                                                   \
-       ((a) < (SI_k * PRETTY_THRESH) ? " " :                           \
-               ((a) < (SI_G * PRETTY_THRESH) ? "k" :                   \
+       ((a) < (SI_k * 4) ? " " :                               \
+               ((a) < (SI_M * PRETTY_THRESH) ? "k" :                   \
                        ((a) < (SI_G * PRETTY_THRESH) ? "M" : "G")))
 
 #define PAGE_TO_NICE(a)                NICE_DIV((long long)a * PAGE_SIZE)
 
 struct analyze_frames {
        struct bintree_ops ops;
-       long int present_pages;
-       long int swapped_pages;
-       long int unused_pages;
+
+       int pid;
+
+       struct list_head *pidlist;
+       struct maps *map;
+       int pids;
+
+       long int pages_present;
+       long int pages_swapped;
+       long int pages_unique;
 };
 
 #define bintree_ops_to_af(bintree_ops)                         \
@@ -38,14 +45,73 @@ static void count_pages(struct bintree *b, struct bintree_ops *ops)
 {
        struct pageframe *pf = tree_to_pageframe(b);
        struct analyze_frames *af = bintree_ops_to_af(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;
 
-       if (pf->page_present) {
-               af->present_pages += pf->refcount;
-       } else if (pf->page_swapped) {
-               af->swapped_pages += pf->refcount;
-       } else {
-               af->unused_pages += pf->refcount;
+               /*
+                * 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:
+       if (page_present(pf))
+               af->pages_present++;
+       else if (page_swapped(pf))
+               af->pages_swapped++;
+       if (pf->refcount == 1)
+               af->pages_unique++;
 }
 
 /*
@@ -62,45 +128,176 @@ void print_page_stats(struct pageframe *pf)
        count = bintree_walk(&pf->tree, &af.ops);
        printf("present pages: %ld, %lld %sB\n"
                "Swapped pages: %ld, %lld %sB\n"
-               "Unused pages: %ld, %lld %sB\n"
+               "Unique pages: %ld, %lld %sB\n"
                "Total %ld physical pages, %lld %sB\n",
-               af.present_pages,
-               PAGE_TO_NICE(af.present_pages),
-               PAGE_TO_NICE_UNIT(af.present_pages),
-               af.swapped_pages,
-               PAGE_TO_NICE(af.swapped_pages),
-               PAGE_TO_NICE_UNIT(af.swapped_pages),
-               af.unused_pages,
-               PAGE_TO_NICE(af.unused_pages),
-               PAGE_TO_NICE_UNIT(af.unused_pages),
+               af.pages_present,
+               PAGE_TO_NICE(af.pages_present),
+               PAGE_TO_NICE_UNIT(af.pages_present),
+               af.pages_swapped,
+               PAGE_TO_NICE(af.pages_swapped),
+               PAGE_TO_NICE_UNIT(af.pages_swapped),
+               af.pages_unique,
+               PAGE_TO_NICE(af.pages_unique),
+               PAGE_TO_NICE_UNIT(af.pages_unique),
                count,
                PAGE_TO_NICE(count),
                PAGE_TO_NICE_UNIT(count));
 }
 
-void print_pid_stats(struct pageframe *pf, struct process *process_list)
+void print_pid_stats(struct pageframe *pf, struct process *process_list,
+               struct parse_opts *opts)
 {
+       struct analyze_frames af;
        struct process *ps;
-       struct maps *map;
-       long int swapped, present;
+       long int swapped, present, total;
+       long int biggest = 0, second_biggest;
+       int count, processes = 0;
+
+       /*
+        * walk through all processes, find the one with most present
+        * pages
+        */
+       list_for_each_entry(ps, &process_list->list, list) {
+               memset(&af, 0, sizeof(af));
+               af.ops.callback = count_pages;
+               af.pid = ps->pid;
 
-       printf("   in ram   swapped    pid name\n");
+               bintree_walk(&pf->tree, &af.ops);
+               ps->pages_present = af.pages_present;
+               ps->pages_swapped = af.pages_swapped;
+               biggest = MAX(biggest, ps->pages_present + ps->pages_swapped);
+       }
+
+       printf("  in ram  swapped    total   pid");
+       if (opts->with_threads)
+               printf("   tid");
+       printf("   name\n");
 
+restart:
+       second_biggest = 0;
+       count = 0;
        list_for_each_entry(ps, &process_list->list, list) {
-               swapped = present = 0;
 
-               if (ps->maps)
-                       list_for_each_entry(map, &ps->maps->list, list) {
-                               present += map->pages_present;
-                               swapped += map->pages_swapped;
-                       }
+               present = ps->pages_present;
+               swapped = ps->pages_swapped;
+               total = present + swapped;
+
+               second_biggest = (total < biggest) &&
+                       (second_biggest < total) ?
+                       total : second_biggest;
 
-               if ((swapped + present) == 0)
+               if (total != biggest)
                        continue;
 
-               printf("% 6lld %sB % 6lld %sB % 5d %s\n",
+               if (total == 0)
+                       continue;
+
+               printf("%5lld %sB %5lld %sB %5lld %sB %5d ",
                        PAGE_TO_NICE(present), PAGE_TO_NICE_UNIT(present),
                        PAGE_TO_NICE(swapped), PAGE_TO_NICE_UNIT(swapped),
-                       ps->pid, ps->name);
+                       PAGE_TO_NICE(total), PAGE_TO_NICE_UNIT(total),
+                       ps->pid);
+
+               if (opts->with_threads)
+                       printf("%5d ", ps->tid);
+
+               printf("%c %s\n",
+                       ps->is_initial_pid ? '*' : ' ',
+                       ps->name);
+
+               count++;
+               processes++;
+       }
+
+       if (count > 0) {
+               biggest = second_biggest;
+               goto restart;
+       }
+
+       printf("Total %d processes\n", processes);
+}
+
+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, 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);
+       }
+
+       printf("process: [%d] %s\n", ps->pid, ps->name);
+       printf("    size   in ram  swapped    total name\n");
+restart:
+       second_biggest = 0;
+       count = 0;
+       list_for_each_entry(map, &ps->maps->list, list) {
+
+               present = map->pages_present;
+               swapped = map->pages_swapped;
+               total = present + swapped;
+
+               second_biggest = (total < biggest) &&
+                       (second_biggest < total) ?
+                       total : second_biggest;
+
+               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),
+                       PAGE_TO_NICE(swapped), PAGE_TO_NICE_UNIT(swapped),
+                       PAGE_TO_NICE(total), PAGE_TO_NICE_UNIT(total),
+                       map->name);
+
+               count++;
+               processes++;
+       }
+
+       if (count > 0 && biggest > 0) {
+               biggest = second_biggest;
+               goto restart;
+       }
+       printf("\n");
+}
+
+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(pf, ps, opts);
        }
 }