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) \
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:
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);
}
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),
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);
}
}
"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);
{ .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) {
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;
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);