diff options
Diffstat (limited to 'tools/perf/builtin-annotate.c')
| -rw-r--r-- | tools/perf/builtin-annotate.c | 524 | 
1 files changed, 211 insertions, 313 deletions
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 6d5604d8df9..1ec429fef2b 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -8,7 +8,6 @@  #include "builtin.h"  #include "util/util.h" -  #include "util/color.h"  #include <linux/list.h>  #include "util/cache.h" @@ -18,6 +17,9 @@  #include "perf.h"  #include "util/debug.h" +#include "util/evlist.h" +#include "util/evsel.h" +#include "util/annotate.h"  #include "util/event.h"  #include "util/parse-options.h"  #include "util/parse-events.h" @@ -25,23 +27,35 @@  #include "util/sort.h"  #include "util/hist.h"  #include "util/session.h" +#include "util/tool.h" +#include "util/data.h" +#include "arch/common.h" + +#include <dlfcn.h> +#include <linux/bitmap.h> + +struct perf_annotate { +	struct perf_tool tool; +	bool	   force, use_tui, use_stdio, use_gtk; +	bool	   full_paths; +	bool	   print_line; +	bool	   skip_missing; +	const char *sym_hist_filter; +	const char *cpu_list; +	DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); +}; -static char		const *input_name = "perf.data"; - -static bool		force, use_tui, use_stdio; - -static bool		full_paths; - -static bool		print_line; - -static const char *sym_hist_filter; - -static int hists__add_entry(struct hists *self, struct addr_location *al) +static int perf_evsel__add_sample(struct perf_evsel *evsel, +				  struct perf_sample *sample __maybe_unused, +				  struct addr_location *al, +				  struct perf_annotate *ann)  {  	struct hist_entry *he; +	int ret; -	if (sym_hist_filter != NULL && -	    (al->sym == NULL || strcmp(sym_hist_filter, al->sym->name) != 0)) { +	if (ann->sym_hist_filter != NULL && +	    (al->sym == NULL || +	     strcmp(ann->sym_hist_filter, al->sym->name) != 0)) {  		/* We're only interested in a symbol named sym_hist_filter */  		if (al->sym != NULL) {  			rb_erase(&al->sym->rb_node, @@ -51,25 +65,35 @@ static int hists__add_entry(struct hists *self, struct addr_location *al)  		return 0;  	} -	he = __hists__add_entry(self, al, NULL, 1); +	he = __hists__add_entry(&evsel->hists, al, NULL, NULL, NULL, 1, 1, 0, +				true);  	if (he == NULL)  		return -ENOMEM; -	return hist_entry__inc_addr_samples(he, al->addr); +	ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); +	hists__inc_nr_samples(&evsel->hists, true); +	return ret;  } -static int process_sample_event(event_t *event, struct perf_session *session) +static int process_sample_event(struct perf_tool *tool, +				union perf_event *event, +				struct perf_sample *sample, +				struct perf_evsel *evsel, +				struct machine *machine)  { +	struct perf_annotate *ann = container_of(tool, struct perf_annotate, tool);  	struct addr_location al; -	struct sample_data data; -	if (event__preprocess_sample(event, session, &al, &data, NULL) < 0) { +	if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {  		pr_warning("problem processing %d event, skipping it.\n",  			   event->header.type);  		return -1;  	} -	if (!al.filtered && hists__add_entry(&session->hists, &al)) { +	if (ann->cpu_list && !test_bit(sample->cpu, ann->cpu_bitmap)) +		return 0; + +	if (!al.filtered && perf_evsel__add_sample(evsel, sample, &al, ann)) {  		pr_warning("problem incrementing symbol count, "  			   "skipping event\n");  		return -1; @@ -78,276 +102,68 @@ static int process_sample_event(event_t *event, struct perf_session *session)  	return 0;  } -static int objdump_line__print(struct objdump_line *self, -			       struct list_head *head, -			       struct hist_entry *he, u64 len) +static int hist_entry__tty_annotate(struct hist_entry *he, +				    struct perf_evsel *evsel, +				    struct perf_annotate *ann)  { -	struct symbol *sym = he->ms.sym; -	static const char *prev_line; -	static const char *prev_color; - -	if (self->offset != -1) { -		const char *path = NULL; -		unsigned int hits = 0; -		double percent = 0.0; -		const char *color; -		struct sym_priv *priv = symbol__priv(sym); -		struct sym_ext *sym_ext = priv->ext; -		struct sym_hist *h = priv->hist; -		s64 offset = self->offset; -		struct objdump_line *next = objdump__get_next_ip_line(head, self); - -		while (offset < (s64)len && -		       (next == NULL || offset < next->offset)) { -			if (sym_ext) { -				if (path == NULL) -					path = sym_ext[offset].path; -				percent += sym_ext[offset].percent; -			} else -				hits += h->ip[offset]; - -			++offset; -		} - -		if (sym_ext == NULL && h->sum) -			percent = 100.0 * hits / h->sum; - -		color = get_percent_color(percent); - -		/* -		 * Also color the filename and line if needed, with -		 * the same color than the percentage. Don't print it -		 * twice for close colored ip with the same filename:line -		 */ -		if (path) { -			if (!prev_line || strcmp(prev_line, path) -				       || color != prev_color) { -				color_fprintf(stdout, color, " %s", path); -				prev_line = path; -				prev_color = color; -			} -		} - -		color_fprintf(stdout, color, " %7.2f", percent); -		printf(" :	"); -		color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", self->line); -	} else { -		if (!*self->line) -			printf("         :\n"); -		else -			printf("         :	%s\n", self->line); -	} - -	return 0; +	return symbol__tty_annotate(he->ms.sym, he->ms.map, evsel, +				    ann->print_line, ann->full_paths, 0, 0);  } -static struct rb_root root_sym_ext; - -static void insert_source_line(struct sym_ext *sym_ext) +static void hists__find_annotations(struct hists *hists, +				    struct perf_evsel *evsel, +				    struct perf_annotate *ann)  { -	struct sym_ext *iter; -	struct rb_node **p = &root_sym_ext.rb_node; -	struct rb_node *parent = NULL; - -	while (*p != NULL) { -		parent = *p; -		iter = rb_entry(parent, struct sym_ext, node); - -		if (sym_ext->percent > iter->percent) -			p = &(*p)->rb_left; -		else -			p = &(*p)->rb_right; -	} - -	rb_link_node(&sym_ext->node, parent, p); -	rb_insert_color(&sym_ext->node, &root_sym_ext); -} - -static void free_source_line(struct hist_entry *he, int len) -{ -	struct sym_priv *priv = symbol__priv(he->ms.sym); -	struct sym_ext *sym_ext = priv->ext; -	int i; - -	if (!sym_ext) -		return; - -	for (i = 0; i < len; i++) -		free(sym_ext[i].path); -	free(sym_ext); - -	priv->ext = NULL; -	root_sym_ext = RB_ROOT; -} - -/* Get the filename:line for the colored entries */ -static void -get_source_line(struct hist_entry *he, int len, const char *filename) -{ -	struct symbol *sym = he->ms.sym; -	u64 start; -	int i; -	char cmd[PATH_MAX * 2]; -	struct sym_ext *sym_ext; -	struct sym_priv *priv = symbol__priv(sym); -	struct sym_hist *h = priv->hist; - -	if (!h->sum) -		return; - -	sym_ext = priv->ext = calloc(len, sizeof(struct sym_ext)); -	if (!priv->ext) -		return; - -	start = he->ms.map->unmap_ip(he->ms.map, sym->start); - -	for (i = 0; i < len; i++) { -		char *path = NULL; -		size_t line_len; -		u64 offset; -		FILE *fp; - -		sym_ext[i].percent = 100.0 * h->ip[i] / h->sum; -		if (sym_ext[i].percent <= 0.5) -			continue; - -		offset = start + i; -		sprintf(cmd, "addr2line -e %s %016llx", filename, offset); -		fp = popen(cmd, "r"); -		if (!fp) -			continue; - -		if (getline(&path, &line_len, fp) < 0 || !line_len) -			goto next; - -		sym_ext[i].path = malloc(sizeof(char) * line_len + 1); -		if (!sym_ext[i].path) -			goto next; - -		strcpy(sym_ext[i].path, path); -		insert_source_line(&sym_ext[i]); - -	next: -		pclose(fp); -	} -} - -static void print_summary(const char *filename) -{ -	struct sym_ext *sym_ext; -	struct rb_node *node; - -	printf("\nSorted summary for file %s\n", filename); -	printf("----------------------------------------------\n\n"); - -	if (RB_EMPTY_ROOT(&root_sym_ext)) { -		printf(" Nothing higher than %1.1f%%\n", MIN_GREEN); -		return; -	} - -	node = rb_first(&root_sym_ext); -	while (node) { -		double percent; -		const char *color; -		char *path; - -		sym_ext = rb_entry(node, struct sym_ext, node); -		percent = sym_ext->percent; -		color = get_percent_color(percent); -		path = sym_ext->path; - -		color_fprintf(stdout, color, " %7.2f %s", percent, path); -		node = rb_next(node); -	} -} - -static void hist_entry__print_hits(struct hist_entry *self) -{ -	struct symbol *sym = self->ms.sym; -	struct sym_priv *priv = symbol__priv(sym); -	struct sym_hist *h = priv->hist; -	u64 len = sym->end - sym->start, offset; - -	for (offset = 0; offset < len; ++offset) -		if (h->ip[offset] != 0) -			printf("%*Lx: %Lu\n", BITS_PER_LONG / 2, -			       sym->start + offset, h->ip[offset]); -	printf("%*s: %Lu\n", BITS_PER_LONG / 2, "h->sum", h->sum); -} - -static int hist_entry__tty_annotate(struct hist_entry *he) -{ -	struct map *map = he->ms.map; -	struct dso *dso = map->dso; -	struct symbol *sym = he->ms.sym; -	const char *filename = dso->long_name, *d_filename; -	u64 len; -	LIST_HEAD(head); -	struct objdump_line *pos, *n; - -	if (hist_entry__annotate(he, &head, 0) < 0) -		return -1; - -	if (full_paths) -		d_filename = filename; -	else -		d_filename = basename(filename); - -	len = sym->end - sym->start; - -	if (print_line) { -		get_source_line(he, len, filename); -		print_summary(filename); -	} - -	printf("\n\n------------------------------------------------\n"); -	printf(" Percent |	Source code & Disassembly of %s\n", d_filename); -	printf("------------------------------------------------\n"); - -	if (verbose) -		hist_entry__print_hits(he); - -	list_for_each_entry_safe(pos, n, &head, node) { -		objdump_line__print(pos, &head, he, len); -		list_del(&pos->node); -		objdump_line__free(pos); -	} - -	if (print_line) -		free_source_line(he, len); - -	return 0; -} - -static void hists__find_annotations(struct hists *self) -{ -	struct rb_node *nd = rb_first(&self->entries), *next; -	int key = KEY_RIGHT; +	struct rb_node *nd = rb_first(&hists->entries), *next; +	int key = K_RIGHT;  	while (nd) {  		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); -		struct sym_priv *priv; +		struct annotation *notes;  		if (he->ms.sym == NULL || he->ms.map->dso->annotate_warned)  			goto find_next; -		priv = symbol__priv(he->ms.sym); -		if (priv->hist == NULL) { +		notes = symbol__annotation(he->ms.sym); +		if (notes->src == NULL) {  find_next: -			if (key == KEY_LEFT) +			if (key == K_LEFT)  				nd = rb_prev(nd);  			else  				nd = rb_next(nd);  			continue;  		} -		if (use_browser > 0) { -			key = hist_entry__tui_annotate(he); +		if (use_browser == 2) { +			int ret; +			int (*annotate)(struct hist_entry *he, +					struct perf_evsel *evsel, +					struct hist_browser_timer *hbt); + +			annotate = dlsym(perf_gtk_handle, +					 "hist_entry__gtk_annotate"); +			if (annotate == NULL) { +				ui__error("GTK browser not found!\n"); +				return; +			} + +			ret = annotate(he, evsel, NULL); +			if (!ret || !ann->skip_missing) +				return; + +			/* skip missing symbols */ +			nd = rb_next(nd); +		} else if (use_browser == 1) { +			key = hist_entry__tui_annotate(he, evsel, NULL);  			switch (key) { -			case KEY_RIGHT: +			case -1: +				if (!ann->skip_missing) +					return; +				/* fall through */ +			case K_RIGHT:  				next = rb_next(nd);  				break; -			case KEY_LEFT: +			case K_LEFT:  				next = rb_prev(nd);  				break;  			default: @@ -357,36 +173,50 @@ find_next:  			if (next != NULL)  				nd = next;  		} else { -			hist_entry__tty_annotate(he); +			hist_entry__tty_annotate(he, evsel, ann);  			nd = rb_next(nd);  			/*  			 * Since we have a hist_entry per IP for the same -			 * symbol, free he->ms.sym->hist to signal we already +			 * symbol, free he->ms.sym->src to signal we already  			 * processed this symbol.  			 */ -			free(priv->hist); -			priv->hist = NULL; +			zfree(¬es->src);  		}  	}  } -static struct perf_event_ops event_ops = { -	.sample	= process_sample_event, -	.mmap	= event__process_mmap, -	.comm	= event__process_comm, -	.fork	= event__process_task, -}; - -static int __cmd_annotate(void) +static int __cmd_annotate(struct perf_annotate *ann)  {  	int ret;  	struct perf_session *session; - -	session = perf_session__new(input_name, O_RDONLY, force, false); +	struct perf_evsel *pos; +	u64 total_nr_samples; +	struct perf_data_file file = { +		.path  = input_name, +		.mode  = PERF_DATA_MODE_READ, +		.force = ann->force, +	}; + +	session = perf_session__new(&file, false, &ann->tool);  	if (session == NULL)  		return -ENOMEM; -	ret = perf_session__process_events(session, &event_ops); +	machines__set_symbol_filter(&session->machines, symbol__annotate_init); + +	if (ann->cpu_list) { +		ret = perf_session__cpu_bitmap(session, ann->cpu_list, +					       ann->cpu_bitmap); +		if (ret) +			goto out_delete; +	} + +	if (!objdump_path) { +		ret = perf_session_env__lookup_objdump(&session->header.env); +		if (ret) +			goto out_delete; +	} + +	ret = perf_session__process_events(session, &ann->tool);  	if (ret)  		goto out_delete; @@ -401,79 +231,147 @@ static int __cmd_annotate(void)  	if (verbose > 2)  		perf_session__fprintf_dsos(session, stdout); -	hists__collapse_resort(&session->hists); -	hists__output_resort(&session->hists); -	hists__find_annotations(&session->hists); -out_delete: -	perf_session__delete(session); +	total_nr_samples = 0; +	evlist__for_each(session->evlist, pos) { +		struct hists *hists = &pos->hists; +		u32 nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; + +		if (nr_samples > 0) { +			total_nr_samples += nr_samples; +			hists__collapse_resort(hists, NULL); +			hists__output_resort(hists); +			if (symbol_conf.event_group && +			    !perf_evsel__is_group_leader(pos)) +				continue; + +			hists__find_annotations(hists, pos, ann); +		} +	} + +	if (total_nr_samples == 0) { +		ui__error("The %s file has no samples!\n", file.path); +		goto out_delete; +	} + +	if (use_browser == 2) { +		void (*show_annotations)(void); + +		show_annotations = dlsym(perf_gtk_handle, +					 "perf_gtk__show_annotations"); +		if (show_annotations == NULL) { +			ui__error("GTK browser not found!\n"); +			goto out_delete; +		} +		show_annotations(); +	} + +out_delete: +	/* +	 * Speed up the exit process, for large files this can +	 * take quite a while. +	 * +	 * XXX Enable this when using valgrind or if we ever +	 * librarize this command. +	 * +	 * Also experiment with obstacks to see how much speed +	 * up we'll get here. +	 * +	 * perf_session__delete(session); +	 */  	return ret;  }  static const char * const annotate_usage[] = { -	"perf annotate [<options>] <command>", +	"perf annotate [<options>]",  	NULL  }; -static const struct option options[] = { +int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) +{ +	struct perf_annotate annotate = { +		.tool = { +			.sample	= process_sample_event, +			.mmap	= perf_event__process_mmap, +			.mmap2	= perf_event__process_mmap2, +			.comm	= perf_event__process_comm, +			.exit	= perf_event__process_exit, +			.fork	= perf_event__process_fork, +			.ordered_samples = true, +			.ordering_requires_timestamps = true, +		}, +	}; +	const struct option options[] = {  	OPT_STRING('i', "input", &input_name, "file",  		    "input file name"),  	OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",  		   "only consider symbols in these dsos"), -	OPT_STRING('s', "symbol", &sym_hist_filter, "symbol", +	OPT_STRING('s', "symbol", &annotate.sym_hist_filter, "symbol",  		    "symbol to annotate"), -	OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), +	OPT_BOOLEAN('f', "force", &annotate.force, "don't complain, do it"),  	OPT_INCR('v', "verbose", &verbose,  		    "be more verbose (show symbol address, etc)"),  	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,  		    "dump raw trace in ASCII"), -	OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"), -	OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"), +	OPT_BOOLEAN(0, "gtk", &annotate.use_gtk, "Use the GTK interface"), +	OPT_BOOLEAN(0, "tui", &annotate.use_tui, "Use the TUI interface"), +	OPT_BOOLEAN(0, "stdio", &annotate.use_stdio, "Use the stdio interface"),  	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,  		   "file", "vmlinux pathname"),  	OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,  		    "load module symbols - WARNING: use only with -k and LIVE kernel"), -	OPT_BOOLEAN('l', "print-line", &print_line, +	OPT_BOOLEAN('l', "print-line", &annotate.print_line,  		    "print matching source lines (may be slow)"), -	OPT_BOOLEAN('P', "full-paths", &full_paths, +	OPT_BOOLEAN('P', "full-paths", &annotate.full_paths,  		    "Don't shorten the displayed pathnames"), +	OPT_BOOLEAN(0, "skip-missing", &annotate.skip_missing, +		    "Skip symbols that cannot be annotated"), +	OPT_STRING('C', "cpu", &annotate.cpu_list, "cpu", "list of cpus to profile"), +	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", +		   "Look for files with symbols relative to this directory"), +	OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src, +		    "Interleave source code with assembly code (default)"), +	OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw, +		    "Display raw encoding of assembly instructions (default)"), +	OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", +		   "Specify disassembler style (e.g. -M intel for intel syntax)"), +	OPT_STRING(0, "objdump", &objdump_path, "path", +		   "objdump binary to use for disassembly and annotations"), +	OPT_BOOLEAN(0, "group", &symbol_conf.event_group, +		    "Show event group information together"),  	OPT_END() -}; +	}; -int cmd_annotate(int argc, const char **argv, const char *prefix __used) -{  	argc = parse_options(argc, argv, options, annotate_usage, 0); -	if (use_stdio) +	if (annotate.use_stdio)  		use_browser = 0; -	else if (use_tui) +	else if (annotate.use_tui)  		use_browser = 1; +	else if (annotate.use_gtk) +		use_browser = 2; -	setup_browser(); +	setup_browser(true); -	symbol_conf.priv_size = sizeof(struct sym_priv); +	symbol_conf.priv_size = sizeof(struct annotation);  	symbol_conf.try_vmlinux_path = true;  	if (symbol__init() < 0)  		return -1; -	setup_sorting(annotate_usage, options); +	if (setup_sorting() < 0) +		usage_with_options(annotate_usage, options);  	if (argc) {  		/* -		 * Special case: if there's an argument left then assume tha +		 * Special case: if there's an argument left then assume that  		 * it's a symbol filter:  		 */  		if (argc > 1)  			usage_with_options(annotate_usage, options); -		sym_hist_filter = argv[0]; -	} - -	if (field_sep && *field_sep == '.') { -		pr_err("'.' is the only non valid --field-separator argument\n"); -		return -1; +		annotate.sym_hist_filter = argv[0];  	} -	return __cmd_annotate(); +	return __cmd_annotate(&annotate);  }  | 
