Skip to content
Snippets Groups Projects
builtin-report.c 38.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	} else {
    		/*
    		 * If this is outside of all known maps,
    		 * and is a negative address, try to look it
    		 * up in the kernel dso, as it might be a
    		 * vsyscall (which executes in user-mode):
    		 */
    		if ((long long)ip < 0)
    		dso = kernel_dso;
    	}
    	dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
    
    	dprintf(" ...... map: %Lx -> %Lx\n", *ipp, ip);
    	*ipp  = ip;
    
    
    	if (dsop)
    		*dsop = dso;
    
    	if (!dso)
    		return NULL;
    got_dso:
    	return dso->find_symbol(dso, ip);
    }
    
    
    static int call__match(struct symbol *sym)
    
    	if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0))
    
    resolve_callchain(struct thread *thread, struct map *map __used,
    
    		    struct ip_callchain *chain, struct hist_entry *entry)
    {
    	u64 context = PERF_CONTEXT_MAX;
    
    	struct symbol **syms;
    	unsigned int i;
    
    
    	if (callchain) {
    		syms = calloc(chain->nr, sizeof(*syms));
    		if (!syms) {
    			fprintf(stderr, "Can't allocate memory for symbols\n");
    			exit(-1);
    		}
    	}
    
    	for (i = 0; i < chain->nr; i++) {
    		u64 ip = chain->ips[i];
    		struct dso *dso = NULL;
    		struct symbol *sym;
    
    		if (ip >= PERF_CONTEXT_MAX) {
    			context = ip;
    			continue;
    		}
    
    		switch (context) {
    
    		case PERF_CONTEXT_HV:
    			dso = hypervisor_dso;
    			break;
    
    		case PERF_CONTEXT_KERNEL:
    			dso = kernel_dso;
    			break;
    		default:
    			break;
    		}
    
    		sym = resolve_symbol(thread, NULL, &dso, &ip);
    
    		if (sym) {
    			if (sort__has_parent && call__match(sym) &&
    			    !entry->parent)
    				entry->parent = sym;
    			if (!callchain)
    				break;
    			syms[i] = sym;
    		}
    	}
    
    	return syms;
    }
    
    
    static int
    hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
    
    		struct symbol *sym, u64 ip, struct ip_callchain *chain,
    		char level, u64 count)
    
    	struct rb_node **p = &hist.rb_node;
    	struct rb_node *parent = NULL;
    	struct hist_entry *he;
    
    	struct symbol **syms = NULL;
    
    	struct hist_entry entry = {
    		.thread	= thread,
    		.map	= map,
    		.dso	= dso,
    		.sym	= sym,
    		.ip	= ip,
    		.level	= level,
    
    		.parent = NULL,
    
    	if ((sort__has_parent || callchain) && chain)
    		syms = resolve_callchain(thread, map, chain, &entry);
    
    	while (*p != NULL) {
    		parent = *p;
    		he = rb_entry(parent, struct hist_entry, rb_node);
    
    		cmp = hist_entry__cmp(&entry, he);
    
    		if (!cmp) {
    
    			if (callchain) {
    				append_chain(&he->callchain, chain, syms);
    				free(syms);
    			}
    
    			return 0;
    		}
    
    		if (cmp < 0)
    			p = &(*p)->rb_left;
    		else
    			p = &(*p)->rb_right;
    
    
    	he = malloc(sizeof(*he));
    	if (!he)
    		return -ENOMEM;
    	*he = entry;
    
    	if (callchain) {
    		callchain_init(&he->callchain);
    
    		append_chain(&he->callchain, chain, syms);
    		free(syms);
    
    	rb_link_node(&he->rb_node, parent, p);
    	rb_insert_color(&he->rb_node, &hist);
    
    	return 0;
    
    static void hist_entry__free(struct hist_entry *he)
    {
    	free(he);
    }
    
    /*
     * collapse the histogram
     */
    
    static struct rb_root collapse_hists;
    
    static void collapse__insert_entry(struct hist_entry *he)
    {
    	struct rb_node **p = &collapse_hists.rb_node;
    	struct rb_node *parent = NULL;
    	struct hist_entry *iter;
    	int64_t cmp;
    
    	while (*p != NULL) {
    		parent = *p;
    		iter = rb_entry(parent, struct hist_entry, rb_node);
    
    		cmp = hist_entry__collapse(iter, he);
    
    		if (!cmp) {
    			iter->count += he->count;
    			hist_entry__free(he);
    			return;
    		}
    
    		if (cmp < 0)
    			p = &(*p)->rb_left;
    		else
    			p = &(*p)->rb_right;
    	}
    
    	rb_link_node(&he->rb_node, parent, p);
    	rb_insert_color(&he->rb_node, &collapse_hists);
    }
    
    static void collapse__resort(void)
    {
    	struct rb_node *next;
    	struct hist_entry *n;
    
    	if (!sort__need_collapse)
    		return;
    
    	next = rb_first(&hist);
    	while (next) {
    		n = rb_entry(next, struct hist_entry, rb_node);
    		next = rb_next(&n->rb_node);
    
    		rb_erase(&n->rb_node, &hist);
    		collapse__insert_entry(n);
    	}
    }
    
    
    /*
     * reverse the map, sort on count.
     */
    
    static struct rb_root output_hists;
    
    
    static void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits)
    
    	struct rb_node **p = &output_hists.rb_node;
    
    	struct rb_node *parent = NULL;
    
    	struct hist_entry *iter;
    
    	if (callchain) {
    		if (callchain_mode == FLAT)
    
    			sort_chain_flat(&he->sorted_chain, &he->callchain,
    					min_callchain_hits);
    
    		else if (callchain_mode == GRAPH)
    
    			sort_chain_graph(&he->sorted_chain, &he->callchain,
    					 min_callchain_hits);
    
    	while (*p != NULL) {
    		parent = *p;
    
    		iter = rb_entry(parent, struct hist_entry, rb_node);
    
    		if (he->count > iter->count)
    
    			p = &(*p)->rb_left;
    		else
    			p = &(*p)->rb_right;
    	}
    
    
    	rb_link_node(&he->rb_node, parent, p);
    	rb_insert_color(&he->rb_node, &output_hists);
    
    static void output__resort(u64 total_samples)
    
    	struct rb_node *next;
    
    	struct hist_entry *n;
    
    	struct rb_root *tree = &hist;
    
    	u64 min_callchain_hits;
    
    	min_callchain_hits = total_samples * (callchain_min_percent / 100);
    
    	if (sort__need_collapse)
    
    		tree = &collapse_hists;
    
    	next = rb_first(tree);
    
    	while (next) {
    		n = rb_entry(next, struct hist_entry, rb_node);
    		next = rb_next(&n->rb_node);
    
    		rb_erase(&n->rb_node, tree);
    
    		output__insert_entry(n, min_callchain_hits);
    
    static size_t output__fprintf(FILE *fp, u64 total_samples)
    
    	struct hist_entry *pos;
    
    	struct rb_node *nd;
    	size_t ret = 0;
    
    
    	fprintf(fp, "\n");
    
    	fprintf(fp, "#\n");
    
    	fprintf(fp, "# (%Ld samples)\n", (u64)total_samples);
    
    	fprintf(fp, "#\n");
    
    	fprintf(fp, "# Overhead");
    
    	list_for_each_entry(se, &hist_entry__sort_list, list) {
    		if (exclude_other && (se == &sort_parent))
    			continue;
    
    		fprintf(fp, "  %s", se->header);
    
    	fprintf(fp, "\n");
    
    	fprintf(fp, "# ........");
    
    	list_for_each_entry(se, &hist_entry__sort_list, list) {
    
    		if (exclude_other && (se == &sort_parent))
    			continue;
    
    
    		fprintf(fp, "  ");
    
    		for (i = 0; i < strlen(se->header); i++)
    
    	for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) {
    		pos = rb_entry(nd, struct hist_entry, rb_node);
    		ret += hist_entry__fprintf(fp, pos, total_samples);
    
    	if (sort_order == default_sort_order &&
    			parent_pattern == default_parent_pattern) {
    
    		fprintf(fp, "# (For more details, try: perf report --sort comm,dso,symbol)\n");
    
    	fprintf(fp, "\n");
    
    static void register_idle_thread(void)
    {
    	struct thread *thread = threads__findnew(0);
    
    	if (thread == NULL ||
    			thread__set_comm(thread, "[idle]")) {
    		fprintf(stderr, "problem inserting idle task.\n");
    		exit(-1);
    	}
    }
    
    
    static unsigned long total = 0,
    		     total_mmap = 0,
    		     total_comm = 0,
    		     total_fork = 0,
    
    		     total_unknown = 0,
    		     total_lost = 0;
    
    static int validate_chain(struct ip_callchain *chain, event_t *event)
    
    {
    	unsigned int chain_size;
    
    	chain_size = event->header.size;
    	chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event;
    
    
    	if (chain->nr*sizeof(u64) > chain_size)
    
    process_sample_event(event_t *event, unsigned long offset, unsigned long head)
    
    {
    	char level;
    	int show = 0;
    	struct dso *dso = NULL;
    	struct thread *thread = threads__findnew(event->ip.pid);
    
    	u64 ip = event->ip.ip;
    	u64 period = 1;
    
    	struct map *map = NULL;
    
    	void *more_data = event->ip.__more_data;
    
    	struct ip_callchain *chain = NULL;
    
    	if (sample_type & PERF_SAMPLE_PERIOD) {
    
    		period = *(u64 *)more_data;
    		more_data += sizeof(u64);
    
    	dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d: %p period: %Ld\n",
    
    		(void *)(offset + head),
    		(void *)(long)(event->header.size),
    		event->header.misc,
    		event->ip.pid,
    
    	if (sample_type & PERF_SAMPLE_CALLCHAIN) {
    
    		dprintf("... chain: nr:%Lu\n", chain->nr);
    
    		if (validate_chain(chain, event) < 0) {
    			eprintf("call-chain problem with event, skipping it.\n");
    			return 0;
    		}
    
    		if (dump_trace) {
    
    			for (i = 0; i < chain->nr; i++)
    
    				dprintf("..... %2d: %016Lx\n", i, chain->ips[i]);
    
    	dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid);
    
    	if (thread == NULL) {
    
    		eprintf("problem processing %d event, skipping it.\n",
    
    			event->header.type);
    		return -1;
    	}
    
    	if (comm_list && !strlist__has_entry(comm_list, thread->comm))
    		return 0;
    
    
    	cpumode = event->header.misc & PERF_EVENT_MISC_CPUMODE_MASK;
    
    	if (cpumode == PERF_EVENT_MISC_KERNEL) {
    
    		show = SHOW_KERNEL;
    		level = 'k';
    
    		dso = kernel_dso;
    
    		dprintf(" ...... dso: %s\n", dso->name);
    
    	} else if (cpumode == PERF_EVENT_MISC_USER) {
    
    		show = SHOW_USER;
    		level = '.';
    
    	} else {
    		show = SHOW_HV;
    		level = 'H';
    
    
    		dso = hypervisor_dso;
    
    
    		dprintf(" ...... dso: [hypervisor]\n");
    	}
    
    	if (show & show_mask) {
    
    		struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip);
    
    		if (dso_list && dso && dso->name && !strlist__has_entry(dso_list, dso->name))
    			return 0;
    
    
    		if (sym_list && sym && !strlist__has_entry(sym_list, sym->name))
    			return 0;
    
    
    		if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) {
    
    			eprintf("problem incrementing symbol count, skipping event\n");
    
    static int
    process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
    {
    	struct thread *thread = threads__findnew(event->mmap.pid);
    	struct map *map = map__new(&event->mmap);
    
    
    	dprintf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n",
    
    		(void *)(offset + head),
    		(void *)(long)(event->header.size),
    
    		(void *)(long)event->mmap.start,
    		(void *)(long)event->mmap.len,
    		(void *)(long)event->mmap.pgoff,
    		event->mmap.filename);
    
    	if (thread == NULL || map == NULL) {
    		dprintf("problem processing PERF_EVENT_MMAP, skipping event.\n");
    
    	}
    
    	thread__insert_map(thread, map);
    	total_mmap++;
    
    	return 0;
    }
    
    static int
    process_comm_event(event_t *event, unsigned long offset, unsigned long head)
    {
    	struct thread *thread = threads__findnew(event->comm.pid);
    
    	dprintf("%p [%p]: PERF_EVENT_COMM: %s:%d\n",
    		(void *)(offset + head),
    		(void *)(long)(event->header.size),
    		event->comm.comm, event->comm.pid);
    
    	if (thread == NULL ||
    	    thread__set_comm(thread, event->comm.comm)) {
    		dprintf("problem processing PERF_EVENT_COMM, skipping event.\n");
    		return -1;
    
    static int
    process_fork_event(event_t *event, unsigned long offset, unsigned long head)
    {
    	struct thread *thread = threads__findnew(event->fork.pid);
    	struct thread *parent = threads__findnew(event->fork.ppid);
    
    	dprintf("%p [%p]: PERF_EVENT_FORK: %d:%d\n",
    		(void *)(offset + head),
    		(void *)(long)(event->header.size),
    		event->fork.pid, event->fork.ppid);
    
    	if (!thread || !parent || thread__fork(thread, parent)) {
    		dprintf("problem processing PERF_EVENT_FORK, skipping event.\n");
    		return -1;
    	}
    	total_fork++;
    
    	return 0;
    }
    
    
    static int
    process_period_event(event_t *event, unsigned long offset, unsigned long head)
    {
    	dprintf("%p [%p]: PERF_EVENT_PERIOD: time:%Ld, id:%Ld: period:%Ld\n",
    		(void *)(offset + head),
    		(void *)(long)(event->header.size),
    		event->period.time,
    		event->period.id,
    		event->period.sample_period);
    
    	return 0;
    }
    
    
    static int
    process_lost_event(event_t *event, unsigned long offset, unsigned long head)
    {
    	dprintf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n",
    		(void *)(offset + head),
    		(void *)(long)(event->header.size),
    		event->lost.id,
    		event->lost.lost);
    
    	total_lost += event->lost.lost;
    
    	return 0;
    }
    
    
    static void trace_event(event_t *event)
    {
    	unsigned char *raw_event = (void *)event;
    
    	char *color = PERF_COLOR_BLUE;
    
    	int i, j;
    
    	if (!dump_trace)
    		return;
    
    
    	dprintf(".");
    	cdprintf("\n. ... raw event: size %d bytes\n", event->header.size);
    
    
    	for (i = 0; i < event->header.size; i++) {
    
    		if ((i & 15) == 0) {
    			dprintf(".");
    			cdprintf("  %04x: ", i);
    		}
    
    		cdprintf(" %02x", raw_event[i]);
    
    
    		if (((i & 15) == 15) || i == event->header.size-1) {
    
    			for (j = 0; j < 15-(i & 15); j++)
    
    			for (j = 0; j < (i & 15); j++) {
    
    				if (isprint(raw_event[i-15+j]))
    
    					cdprintf("%c", raw_event[i-15+j]);
    
    static int
    process_read_event(event_t *event, unsigned long offset, unsigned long head)
    {
    	dprintf("%p [%p]: PERF_EVENT_READ: %d %d %Lu\n",
    			(void *)(offset + head),
    			(void *)(long)(event->header.size),
    			event->read.pid,
    			event->read.tid,
    			event->read.value);
    
    	return 0;
    }
    
    
    static int
    process_event(event_t *event, unsigned long offset, unsigned long head)
    {
    
    	trace_event(event);
    
    
    	switch (event->header.type) {
    
    	case PERF_EVENT_SAMPLE:
    		return process_sample_event(event, offset, head);
    
    
    	case PERF_EVENT_MMAP:
    		return process_mmap_event(event, offset, head);
    
    	case PERF_EVENT_COMM:
    		return process_comm_event(event, offset, head);
    
    
    	case PERF_EVENT_FORK:
    		return process_fork_event(event, offset, head);
    
    
    	case PERF_EVENT_PERIOD:
    		return process_period_event(event, offset, head);
    
    
    	case PERF_EVENT_LOST:
    		return process_lost_event(event, offset, head);
    
    
    	case PERF_EVENT_READ:
    		return process_read_event(event, offset, head);
    
    
    	/*
    	 * We dont process them right now but they are fine:
    	 */
    
    	case PERF_EVENT_THROTTLE:
    	case PERF_EVENT_UNTHROTTLE:
    		return 0;
    
    
    static struct perf_header	*header;
    
    
    static u64 perf_header__sample_type(void)
    
    	u64 sample_type = 0;
    
    	int i;
    
    	for (i = 0; i < header->attrs; i++) {
    		struct perf_header_attr *attr = header->attr[i];
    
    
    		if (!sample_type)
    			sample_type = attr->attr.sample_type;
    		else if (sample_type != attr->attr.sample_type)
    			die("non matching sample_type");
    
    	return sample_type;
    
    static int __cmd_report(void)
    {
    
    	int ret, rc = EXIT_FAILURE;
    
    	unsigned long offset = 0;
    
    	unsigned long head, shift;
    
    	struct stat stat;
    	event_t *event;
    	uint32_t size;
    
    
    	register_idle_thread();
    
    	input = open(input_name, O_RDONLY);
    	if (input < 0) {
    
    		fprintf(stderr, " failed to open file: %s", input_name);
    		if (!strcmp(input_name, "perf.data"))
    			fprintf(stderr, "  (try 'perf record' first)");
    		fprintf(stderr, "\n");
    
    		exit(-1);
    	}
    
    	ret = fstat(input, &stat);
    	if (ret < 0) {
    		perror("failed to stat file");
    		exit(-1);
    	}
    
    	if (!stat.st_size) {
    		fprintf(stderr, "zero-sized file, nothing to do!\n");
    		exit(0);
    	}
    
    
    	header = perf_header__read(input);
    	head = header->data_offset;
    
    	sample_type = perf_header__sample_type();
    
    	if (sort__has_parent && !(sample_type & PERF_SAMPLE_CALLCHAIN)) {
    
    		fprintf(stderr, "selected --sort parent, but no callchain data\n");
    		exit(-1);
    	}
    
    
    	if (load_kernel() < 0) {
    		perror("failed to load kernel symbols");
    		return EXIT_FAILURE;
    	}
    
    	if (!full_paths) {
    		if (getcwd(__cwd, sizeof(__cwd)) == NULL) {
    			perror("failed to get the current directory");
    			return EXIT_FAILURE;
    		}
    		cwdlen = strlen(cwd);
    	} else {
    		cwd = NULL;
    		cwdlen = 0;
    	}
    
    
    	shift = page_size * (head / page_size);
    	offset += shift;
    	head -= shift;
    
    
    remap:
    	buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
    			   MAP_SHARED, input, offset);
    	if (buf == MAP_FAILED) {
    		perror("failed to mmap file");
    		exit(-1);
    	}
    
    more:
    	event = (event_t *)(buf + head);
    
    	size = event->header.size;
    	if (!size)
    		size = 8;
    
    	if (head + event->header.size >= page_size * mmap_window) {
    		int ret;
    
    
    		shift = page_size * (head / page_size);
    
    
    		ret = munmap(buf, page_size * mmap_window);
    		assert(ret == 0);
    
    		offset += shift;
    		head -= shift;
    		goto remap;
    	}
    
    	size = event->header.size;
    
    
    	dprintf("\n%p [%p]: event: %d\n",
    
    			(void *)(offset + head),
    			(void *)(long)event->header.size,
    			event->header.type);
    
    
    	if (!size || process_event(event, offset, head) < 0) {
    
    
    		dprintf("%p [%p]: skipping unknown header type: %d\n",
    			(void *)(offset + head),
    			(void *)(long)(event->header.size),
    			event->header.type);
    
    		total_unknown++;
    
    
    		/*
    		 * assume we lost track of the stream, check alignment, and
    		 * increment a single u64 in the hope to catch on again 'soon'.
    		 */
    
    		if (unlikely(head & 7))
    			head &= ~7ULL;
    
    		size = 8;
    
    	if (offset + head >= header->data_offset + header->data_size)
    
    	if (offset + head < (unsigned long)stat.st_size)
    
    	dprintf("      IP events: %10ld\n", total);
    	dprintf("    mmap events: %10ld\n", total_mmap);
    	dprintf("    comm events: %10ld\n", total_comm);
    
    	dprintf("    fork events: %10ld\n", total_fork);
    
    	dprintf("    lost events: %10ld\n", total_lost);
    
    	dprintf(" unknown events: %10ld\n", total_unknown);
    
    	if (dump_trace)
    
    	collapse__resort();
    
    	output__fprintf(stdout, total);
    
    static int
    parse_callchain_opt(const struct option *opt __used, const char *arg,
    		    int unset __used)
    {
    
    	tok = strtok((char *)arg, ",");
    	if (!tok)
    		return -1;
    
    	/* get the output mode */
    	if (!strncmp(tok, "graph", strlen(arg)))
    
    	else if (!strncmp(tok, "flat", strlen(arg)))
    
    	/* get the min percentage */
    	tok = strtok(NULL, ",");
    	if (!tok)
    		return 0;
    
    	callchain_min_percent = strtod(tok, &endptr);
    	if (tok == endptr)
    		return -1;
    
    
    static const char * const report_usage[] = {
    	"perf report [<options>] <command>",
    	NULL
    };
    
    static const struct option options[] = {
    	OPT_STRING('i', "input", &input_name, "file",
    		    "input file name"),
    
    	OPT_BOOLEAN('v', "verbose", &verbose,
    		    "be more verbose (show symbol address, etc)"),
    
    	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
    		    "dump raw trace in ASCII"),
    
    	OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
    
    	OPT_BOOLEAN('m', "modules", &modules,
    		    "load module symbols - WARNING: use only with -k and LIVE kernel"),
    
    	OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
    
    		   "sort by key(s): pid, comm, dso, symbol, parent"),
    
    	OPT_BOOLEAN('P', "full-paths", &full_paths,
    		    "Don't shorten the pathnames taking into account the cwd"),
    
    	OPT_STRING('p', "parent", &parent_pattern, "regex",
    		   "regex filter to identify parent, see: '--sort parent'"),
    
    	OPT_BOOLEAN('x', "exclude-other", &exclude_other,
    		    "Only display entries with parent-match"),
    
    	OPT_CALLBACK_DEFAULT('c', "callchain", NULL, "output_type,min_percent",
    		     "Display callchains using output_type and min percent threshold. "
    		     "Default: flat,0", &parse_callchain_opt, "flat,100"),
    
    	OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]",
    		   "only consider symbols in these dsos"),
    
    	OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]",
    		   "only consider symbols in these comms"),
    
    	OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]",
    		   "only consider these symbols"),
    
    static void setup_sorting(void)
    {
    	char *tmp, *tok, *str = strdup(sort_order);
    
    	for (tok = strtok_r(str, ", ", &tmp);
    			tok; tok = strtok_r(NULL, ", ", &tmp)) {
    		if (sort_dimension__add(tok) < 0) {
    			error("Unknown --sort key: `%s'", tok);
    			usage_with_options(report_usage, options);
    		}
    	}
    
    	free(str);
    }
    
    
    static void setup_list(struct strlist **list, const char *list_str,
    		       const char *list_name)
    {
    	if (list_str) {
    		*list = strlist__new(true, list_str);
    		if (!*list) {
    			fprintf(stderr, "problems parsing %s list\n",
    				list_name);
    			exit(129);
    		}
    	}
    }
    
    
    int cmd_report(int argc, const char **argv, const char *prefix __used)
    
    
    	page_size = getpagesize();
    
    
    	argc = parse_options(argc, argv, options, report_usage, 0);
    
    	if (parent_pattern != default_parent_pattern)
    		sort_dimension__add("parent");
    	else
    		exclude_other = 0;
    
    
    	/*
    	 * Any (unrecognized) arguments left?
    	 */
    	if (argc)
    		usage_with_options(report_usage, options);
    
    
    	setup_list(&dso_list, dso_list_str, "dso");
    	setup_list(&comm_list, comm_list_str, "comm");
    
    	setup_list(&sym_list, sym_list_str, "symbol");
    
    	return __cmd_report();
    }