From 5b1b6ca895e76da8ade5f7d33a2730808eeef5ab Mon Sep 17 00:00:00 2001 From: Timo Kokkonen Date: Sat, 21 Aug 2010 13:42:54 +0300 Subject: [PATCH] Add support for listing mappings that are shared between processes This counts the amoutn of pages that are being shared among the list of given processes for each mapping the process has. Signed-off-by: Timo Kokkonen --- analyze.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- analyze.h | 3 +- main.c | 9 ++++-- pagemap.h | 1 + 4 files changed, 100 insertions(+), 8 deletions(-) diff --git a/analyze.c b/analyze.c index 3090431..7a67fb4 100644 --- a/analyze.c +++ b/analyze.c @@ -26,10 +26,16 @@ 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); } } diff --git a/analyze.h b/analyze.h index 2d5010c..a803209 100644 --- a/analyze.h +++ b/analyze.h @@ -6,6 +6,7 @@ void print_page_stats(struct pageframe *pf); void print_pid_stats(struct pageframe *pf, struct process *process_list, struct parse_opts *opts); -void dump_process_maps(struct process *process_list); +void dump_process_maps(struct pageframe *pf, struct process *process_list, + struct parse_opts *opts); #endif diff --git a/main.c b/main.c index 9f9b5c5..c28ed2a 100644 --- a/main.c +++ b/main.c @@ -18,6 +18,7 @@ void print_help_and_die(char *name) "given name\n" "-m, --map=mapname scan maps with given mapping name\n" "-d, --dump dump process maps\n" + "-s, --shared-mappings dump only shared mappings\n" "-h, --help show this help\n", name); @@ -50,9 +51,10 @@ void read_args(int argc, char *argv[], struct parse_opts *opts) { .val = 'm', .name = "map", .has_arg = 1, }, { .val = OPT_WITH_THREADS, .name = "with-threads" }, { .val = 'd', .name = "dump", }, + { .val = 's', .name = "shared-mappings", }, { .val = 'h', .name = "help", }, }; - char short_options[] = "p:P:m:dh"; + char short_options[] = "p:P:m:sdh"; opts->parse_mask = 0; while (1) { @@ -87,6 +89,9 @@ void read_args(int argc, char *argv[], struct parse_opts *opts) case OPT_WITH_THREADS: opts->with_threads = 1; break; + case 's': + opts->parse_mask |= PARSE_SHARED_MAPPING; + /* implies --dump */ case 'd': opts->parse_mask |= PARSE_DUMP; break; @@ -126,7 +131,7 @@ int main(int argc, char *argv[]) return 1; if (opts.parse_mask & PARSE_DUMP) - dump_process_maps(&process_list); + dump_process_maps(&pf, &process_list, &opts); else print_pid_stats(&pf, &process_list, &opts); diff --git a/pagemap.h b/pagemap.h index 4bb1302..43d34ba 100644 --- a/pagemap.h +++ b/pagemap.h @@ -104,6 +104,7 @@ struct process { #define PARSE_MAP_NAME 0x2 #define PARSE_DUMP 0x8 #define PARSE_NOADD_TREE 0x10 +#define PARSE_SHARED_MAPPING 0x20 struct pidlist { struct list_head list; -- 2.44.0