Merge branch 'perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux-2.6 into perf/core
This commit is contained in:
		
						commit
						075de90c46
					
				| @ -401,6 +401,7 @@ LIB_H += util/include/dwarf-regs.h | ||||
| LIB_H += util/include/asm/dwarf2.h | ||||
| LIB_H += util/include/asm/cpufeature.h | ||||
| LIB_H += perf.h | ||||
| LIB_H += util/annotate.h | ||||
| LIB_H += util/cache.h | ||||
| LIB_H += util/callchain.h | ||||
| LIB_H += util/build-id.h | ||||
| @ -444,6 +445,7 @@ LIB_H += $(ARCH_INCLUDE) | ||||
| 
 | ||||
| LIB_OBJS += $(OUTPUT)util/abspath.o | ||||
| LIB_OBJS += $(OUTPUT)util/alias.o | ||||
| LIB_OBJS += $(OUTPUT)util/annotate.o | ||||
| LIB_OBJS += $(OUTPUT)util/build-id.o | ||||
| LIB_OBJS += $(OUTPUT)util/config.o | ||||
| LIB_OBJS += $(OUTPUT)util/ctype.o | ||||
|  | ||||
| @ -9,6 +9,7 @@ | ||||
| 
 | ||||
| #include "util/util.h" | ||||
| 
 | ||||
| #include "util/util.h" | ||||
| #include "util/color.h" | ||||
| #include <linux/list.h> | ||||
| #include "util/cache.h" | ||||
| @ -18,6 +19,7 @@ | ||||
| #include "perf.h" | ||||
| #include "util/debug.h" | ||||
| 
 | ||||
| #include "util/annotate.h" | ||||
| #include "util/event.h" | ||||
| #include "util/parse-options.h" | ||||
| #include "util/parse-events.h" | ||||
| @ -55,7 +57,18 @@ static int hists__add_entry(struct hists *self, struct addr_location *al) | ||||
| 	if (he == NULL) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	return hist_entry__inc_addr_samples(he, al->addr); | ||||
| 	if (he->ms.sym != NULL) { | ||||
| 		/*
 | ||||
| 		 * All aggregated on the first sym_hist. | ||||
| 		 */ | ||||
| 		struct annotation *notes = symbol__annotation(he->ms.sym); | ||||
| 		if (notes->histograms == NULL && symbol__alloc_hist(he->ms.sym, 1) < 0) | ||||
| 			return -ENOMEM; | ||||
| 
 | ||||
| 		return hist_entry__inc_addr_samples(he, 0, al->addr); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int process_sample_event(union perf_event *event, | ||||
| @ -79,245 +92,10 @@ static int process_sample_event(union perf_event *event, | ||||
| 	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, int evidx) | ||||
| { | ||||
| 	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; | ||||
| } | ||||
| 
 | ||||
| static struct rb_root root_sym_ext; | ||||
| 
 | ||||
| static void insert_source_line(struct sym_ext *sym_ext) | ||||
| { | ||||
| 	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 %016" PRIx64, 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("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2, | ||||
| 			       sym->start + offset, h->ip[offset]); | ||||
| 	printf("%*s: %" PRIu64 "\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; | ||||
| 	return symbol__tty_annotate(he->ms.sym, he->ms.map, evidx, | ||||
| 				    print_line, full_paths, 0, 0); | ||||
| } | ||||
| 
 | ||||
| static void hists__find_annotations(struct hists *self) | ||||
| @ -327,13 +105,13 @@ static void hists__find_annotations(struct hists *self) | ||||
| 
 | ||||
| 	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->histograms == NULL) { | ||||
| find_next: | ||||
| 			if (key == KEY_LEFT) | ||||
| 				nd = rb_prev(nd); | ||||
| @ -343,7 +121,8 @@ find_next: | ||||
| 		} | ||||
| 
 | ||||
| 		if (use_browser > 0) { | ||||
| 			key = hist_entry__tui_annotate(he); | ||||
| 			/* For now all is aggregated on the first */ | ||||
| 			key = hist_entry__tui_annotate(he, 0); | ||||
| 			switch (key) { | ||||
| 			case KEY_RIGHT: | ||||
| 				next = rb_next(nd); | ||||
| @ -358,15 +137,16 @@ find_next: | ||||
| 			if (next != NULL) | ||||
| 				nd = next; | ||||
| 		} else { | ||||
| 			hist_entry__tty_annotate(he); | ||||
| 			/* For now all is aggregated on the first */ | ||||
| 			hist_entry__tty_annotate(he, 0); | ||||
| 			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->histogram to signal we already | ||||
| 			 * processed this symbol. | ||||
| 			 */ | ||||
| 			free(priv->hist); | ||||
| 			priv->hist = NULL; | ||||
| 			free(notes->histograms); | ||||
| 			notes->histograms = NULL; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @ -454,7 +234,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used) | ||||
| 
 | ||||
| 	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) | ||||
|  | ||||
| @ -9,6 +9,7 @@ | ||||
| 
 | ||||
| #include "util/util.h" | ||||
| 
 | ||||
| #include "util/annotate.h" | ||||
| #include "util/color.h" | ||||
| #include <linux/list.h> | ||||
| #include "util/cache.h" | ||||
| @ -117,8 +118,17 @@ static int perf_session__add_hist_entry(struct perf_session *session, | ||||
| 	 * so we don't allocated the extra space needed because the stdio | ||||
| 	 * code will not use it. | ||||
| 	 */ | ||||
| 	if (use_browser > 0) | ||||
| 		err = hist_entry__inc_addr_samples(he, al->addr); | ||||
| 	if (al->sym != NULL && use_browser > 0) { | ||||
| 		/*
 | ||||
| 		 * All aggregated on the first sym_hist. | ||||
| 		 */ | ||||
| 		struct annotation *notes = symbol__annotation(he->ms.sym); | ||||
| 		if (notes->histograms == NULL && | ||||
| 		    symbol__alloc_hist(he->ms.sym, 1) < 0) | ||||
| 			err = -ENOMEM; | ||||
| 		else | ||||
| 			err = hist_entry__inc_addr_samples(he, 0, al->addr); | ||||
| 	} | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| @ -348,7 +358,7 @@ static int __cmd_report(void) | ||||
| 	} | ||||
| 
 | ||||
| 	if (use_browser > 0) | ||||
| 		hists__tui_browse_tree(&session->hists_tree, help); | ||||
| 		hists__tui_browse_tree(&session->hists_tree, help, 0); | ||||
| 	else | ||||
| 		hists__tty_browse_tree(&session->hists_tree, help); | ||||
| 
 | ||||
| @ -508,7 +518,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | ||||
| 	 * implementation. | ||||
| 	 */ | ||||
| 	if (use_browser > 0) { | ||||
| 		symbol_conf.priv_size = sizeof(struct sym_priv); | ||||
| 		symbol_conf.priv_size = sizeof(struct annotation); | ||||
| 		/*
 | ||||
|  		 * For searching by name on the "Browse map details". | ||||
|  		 * providing it only in verbose mode not to bloat too | ||||
|  | ||||
| @ -20,6 +20,7 @@ | ||||
| 
 | ||||
| #include "perf.h" | ||||
| 
 | ||||
| #include "util/annotate.h" | ||||
| #include "util/cache.h" | ||||
| #include "util/color.h" | ||||
| #include "util/evlist.h" | ||||
| @ -140,10 +141,7 @@ static int parse_source(struct sym_entry *syme) | ||||
| 	struct symbol *sym; | ||||
| 	struct sym_entry_source *source; | ||||
| 	struct map *map; | ||||
| 	FILE *file; | ||||
| 	char command[PATH_MAX*2]; | ||||
| 	const char *path; | ||||
| 	u64 len; | ||||
| 	int err = -1; | ||||
| 
 | ||||
| 	if (!syme) | ||||
| 		return -1; | ||||
| @ -162,197 +160,80 @@ static int parse_source(struct sym_entry *syme) | ||||
| 		if (syme->src == NULL) | ||||
| 			return -1; | ||||
| 		pthread_mutex_init(&syme->src->lock, NULL); | ||||
| 		INIT_LIST_HEAD(&syme->src->head); | ||||
| 	} | ||||
| 
 | ||||
| 	source = syme->src; | ||||
| 
 | ||||
| 	if (source->lines) { | ||||
| 	if (symbol__annotation(sym)->histograms != NULL) { | ||||
| 		pthread_mutex_lock(&source->lock); | ||||
| 		goto out_assign; | ||||
| 	} | ||||
| 	path = map->dso->long_name; | ||||
| 
 | ||||
| 	len = sym->end - sym->start; | ||||
| 
 | ||||
| 	sprintf(command, | ||||
| 		"objdump --start-address=%#0*" PRIx64 " --stop-address=%#0*" PRIx64 " -dS %s", | ||||
| 		BITS_PER_LONG / 4, map__rip_2objdump(map, sym->start), | ||||
| 		BITS_PER_LONG / 4, map__rip_2objdump(map, sym->end), path); | ||||
| 
 | ||||
| 	file = popen(command, "r"); | ||||
| 	if (!file) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	pthread_mutex_lock(&source->lock); | ||||
| 	source->lines_tail = &source->lines; | ||||
| 	while (!feof(file)) { | ||||
| 		struct source_line *src; | ||||
| 		size_t dummy = 0; | ||||
| 		char *c, *sep; | ||||
| 
 | ||||
| 		src = malloc(sizeof(struct source_line)); | ||||
| 		assert(src != NULL); | ||||
| 		memset(src, 0, sizeof(struct source_line)); | ||||
| 
 | ||||
| 		if (getline(&src->line, &dummy, file) < 0) | ||||
| 			break; | ||||
| 		if (!src->line) | ||||
| 			break; | ||||
| 
 | ||||
| 		c = strchr(src->line, '\n'); | ||||
| 		if (c) | ||||
| 			*c = 0; | ||||
| 
 | ||||
| 		src->next = NULL; | ||||
| 		*source->lines_tail = src; | ||||
| 		source->lines_tail = &src->next; | ||||
| 
 | ||||
| 		src->eip = strtoull(src->line, &sep, 16); | ||||
| 		if (*sep == ':') | ||||
| 			src->eip = map__objdump_2ip(map, src->eip); | ||||
| 		else /* this line has no ip info (e.g. source line) */ | ||||
| 			src->eip = 0; | ||||
| 	if (symbol__alloc_hist(sym, top.evlist->nr_entries) < 0) { | ||||
| 		pr_err("Not enough memory for annotating '%s' symbol!\n", | ||||
| 		       sym->name); | ||||
| 		goto out_unlock; | ||||
| 	} | ||||
| 	pclose(file); | ||||
| 
 | ||||
| 	err = symbol__annotate(sym, syme->map, &source->head, 0); | ||||
| 	if (err == 0) { | ||||
| out_assign: | ||||
| 	sym_filter_entry = syme; | ||||
| 	} | ||||
| out_unlock: | ||||
| 	pthread_mutex_unlock(&source->lock); | ||||
| 	return 0; | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static void __zero_source_counters(struct sym_entry *syme) | ||||
| { | ||||
| 	int i; | ||||
| 	struct source_line *line; | ||||
| 
 | ||||
| 	line = syme->src->lines; | ||||
| 	while (line) { | ||||
| 		for (i = 0; i < top.evlist->nr_entries; i++) | ||||
| 			line->count[i] = 0; | ||||
| 		line = line->next; | ||||
| 	} | ||||
| 	struct symbol *sym = sym_entry__symbol(syme); | ||||
| 	symbol__annotate_zero_histograms(sym); | ||||
| } | ||||
| 
 | ||||
| static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) | ||||
| { | ||||
| 	struct source_line *line; | ||||
| 
 | ||||
| 	if (syme != sym_filter_entry) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (pthread_mutex_trylock(&syme->src->lock)) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (syme->src == NULL || syme->src->source == NULL) | ||||
| 		goto out_unlock; | ||||
| 	ip = syme->map->map_ip(syme->map, ip); | ||||
| 	symbol__inc_addr_samples(sym_entry__symbol(syme), syme->map, counter, ip); | ||||
| 
 | ||||
| 	for (line = syme->src->lines; line; line = line->next) { | ||||
| 		/* skip lines without IP info */ | ||||
| 		if (line->eip == 0) | ||||
| 			continue; | ||||
| 		if (line->eip == ip) { | ||||
| 			line->count[counter]++; | ||||
| 			break; | ||||
| 		} | ||||
| 		if (line->eip > ip) | ||||
| 			break; | ||||
| 	} | ||||
| out_unlock: | ||||
| 	pthread_mutex_unlock(&syme->src->lock); | ||||
| } | ||||
| 
 | ||||
| #define PATTERN_LEN		(BITS_PER_LONG / 4 + 2) | ||||
| 
 | ||||
| static void lookup_sym_source(struct sym_entry *syme) | ||||
| { | ||||
| 	struct symbol *symbol = sym_entry__symbol(syme); | ||||
| 	struct source_line *line; | ||||
| 	char pattern[PATTERN_LEN + 1]; | ||||
| 
 | ||||
| 	sprintf(pattern, "%0*" PRIx64 " <", BITS_PER_LONG / 4, | ||||
| 		map__rip_2objdump(syme->map, symbol->start)); | ||||
| 
 | ||||
| 	pthread_mutex_lock(&syme->src->lock); | ||||
| 	for (line = syme->src->lines; line; line = line->next) { | ||||
| 		if (memcmp(line->line, pattern, PATTERN_LEN) == 0) { | ||||
| 			syme->src->source = line; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	pthread_mutex_unlock(&syme->src->lock); | ||||
| } | ||||
| 
 | ||||
| static void show_lines(struct source_line *queue, int count, int total) | ||||
| { | ||||
| 	int i; | ||||
| 	struct source_line *line; | ||||
| 
 | ||||
| 	line = queue; | ||||
| 	for (i = 0; i < count; i++) { | ||||
| 		float pcnt = 100.0*(float)line->count[top.sym_counter]/(float)total; | ||||
| 
 | ||||
| 		printf("%8li %4.1f%%\t%s\n", line->count[top.sym_counter], pcnt, line->line); | ||||
| 		line = line->next; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #define TRACE_COUNT     3 | ||||
| 
 | ||||
| static void show_details(struct sym_entry *syme) | ||||
| { | ||||
| 	struct symbol *symbol; | ||||
| 	struct source_line *line; | ||||
| 	struct source_line *line_queue = NULL; | ||||
| 	int displayed = 0; | ||||
| 	int line_queue_count = 0, total = 0, more = 0; | ||||
| 	int more; | ||||
| 
 | ||||
| 	if (!syme) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (!syme->src->source) | ||||
| 		lookup_sym_source(syme); | ||||
| 
 | ||||
| 	if (!syme->src->source) | ||||
| 	symbol = sym_entry__symbol(syme); | ||||
| 	if (!syme->src || symbol__annotation(symbol)->histograms == NULL) | ||||
| 		return; | ||||
| 
 | ||||
| 	symbol = sym_entry__symbol(syme); | ||||
| 	printf("Showing %s for %s\n", event_name(top.sym_evsel), symbol->name); | ||||
| 	printf("  Events  Pcnt (>=%d%%)\n", sym_pcnt_filter); | ||||
| 
 | ||||
| 	pthread_mutex_lock(&syme->src->lock); | ||||
| 	line = syme->src->source; | ||||
| 	while (line) { | ||||
| 		total += line->count[top.sym_counter]; | ||||
| 		line = line->next; | ||||
| 	} | ||||
| 
 | ||||
| 	line = syme->src->source; | ||||
| 	while (line) { | ||||
| 		float pcnt = 0.0; | ||||
| 
 | ||||
| 		if (!line_queue_count) | ||||
| 			line_queue = line; | ||||
| 		line_queue_count++; | ||||
| 
 | ||||
| 		if (line->count[top.sym_counter]) | ||||
| 			pcnt = 100.0 * line->count[top.sym_counter] / (float)total; | ||||
| 		if (pcnt >= (float)sym_pcnt_filter) { | ||||
| 			if (displayed <= top.print_entries) | ||||
| 				show_lines(line_queue, line_queue_count, total); | ||||
| 			else more++; | ||||
| 			displayed += line_queue_count; | ||||
| 			line_queue_count = 0; | ||||
| 			line_queue = NULL; | ||||
| 		} else if (line_queue_count > TRACE_COUNT) { | ||||
| 			line_queue = line_queue->next; | ||||
| 			line_queue_count--; | ||||
| 		} | ||||
| 
 | ||||
| 		line->count[top.sym_counter] = top.zero ? 0 : line->count[top.sym_counter] * 7 / 8; | ||||
| 		line = line->next; | ||||
| 	} | ||||
| 	more = symbol__annotate_printf(symbol, syme->map, &syme->src->head, | ||||
| 				       top.sym_evsel->idx, 0, sym_pcnt_filter, | ||||
| 				       top.print_entries); | ||||
| 	if (top.zero) | ||||
| 		symbol__annotate_zero_histogram(symbol, top.sym_evsel->idx); | ||||
| 	else | ||||
| 		symbol__annotate_decay_histogram(symbol, &syme->src->head, | ||||
| 						 top.sym_evsel->idx); | ||||
| 	pthread_mutex_unlock(&syme->src->lock); | ||||
| 	if (more) | ||||
| 	if (more != 0) | ||||
| 		printf("%d lines not displayed, maybe increase display entries [e]\n", more); | ||||
| } | ||||
| 
 | ||||
| @ -787,9 +668,6 @@ static int symbol_filter(struct map *map, struct symbol *sym) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (!syme->skip) | ||||
| 		syme->name_len = strlen(sym->name); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| @ -1175,7 +1053,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | ||||
| 
 | ||||
| 	top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); | ||||
| 
 | ||||
| 	symbol_conf.priv_size = (sizeof(struct sym_entry) + | ||||
| 	symbol_conf.priv_size = (sizeof(struct sym_entry) + sizeof(struct annotation) + | ||||
| 				 (top.evlist->nr_entries + 1) * sizeof(unsigned long)); | ||||
| 
 | ||||
| 	symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); | ||||
|  | ||||
							
								
								
									
										550
									
								
								tools/perf/util/annotate.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										550
									
								
								tools/perf/util/annotate.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,550 @@ | ||||
| /*
 | ||||
|  * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||||
|  * | ||||
|  * Parts came from builtin-annotate.c, see those files for further | ||||
|  * copyright notes. | ||||
|  * | ||||
|  * Released under the GPL v2. (and only v2, not any later version) | ||||
|  */ | ||||
| 
 | ||||
| #include "util.h" | ||||
| #include "build-id.h" | ||||
| #include "color.h" | ||||
| #include "cache.h" | ||||
| #include "symbol.h" | ||||
| #include "debug.h" | ||||
| #include "annotate.h" | ||||
| 
 | ||||
| int symbol__alloc_hist(struct symbol *sym, int nevents) | ||||
| { | ||||
| 	struct annotation *notes = symbol__annotation(sym); | ||||
| 
 | ||||
| 	notes->sizeof_sym_hist = (sizeof(*notes->histograms) + | ||||
| 				  (sym->end - sym->start) * sizeof(u64)); | ||||
| 	notes->histograms = calloc(nevents, notes->sizeof_sym_hist); | ||||
| 	notes->nr_histograms = nevents; | ||||
| 	return notes->histograms == NULL ? -1 : 0; | ||||
| } | ||||
| 
 | ||||
| void symbol__annotate_zero_histograms(struct symbol *sym) | ||||
| { | ||||
| 	struct annotation *notes = symbol__annotation(sym); | ||||
| 
 | ||||
| 	if (notes->histograms != NULL) | ||||
| 		memset(notes->histograms, 0, | ||||
| 		       notes->nr_histograms * notes->sizeof_sym_hist); | ||||
| } | ||||
| 
 | ||||
| int symbol__inc_addr_samples(struct symbol *sym, struct map *map, | ||||
| 			     int evidx, u64 addr) | ||||
| { | ||||
| 	unsigned offset; | ||||
| 	struct annotation *notes; | ||||
| 	struct sym_hist *h; | ||||
| 
 | ||||
| 	notes = symbol__annotation(sym); | ||||
| 	if (notes->histograms == NULL) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); | ||||
| 
 | ||||
| 	if (addr >= sym->end) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	offset = addr - sym->start; | ||||
| 	h = annotation__histogram(notes, evidx); | ||||
| 	h->sum++; | ||||
| 	h->addr[offset]++; | ||||
| 
 | ||||
| 	pr_debug3("%#" PRIx64 " %s: period++ [addr: %#" PRIx64 ", %#" PRIx64 | ||||
| 		  ", evidx=%d] => %" PRIu64 "\n", sym->start, sym->name, | ||||
| 		  addr, addr - sym->start, evidx, h->addr[offset]); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize) | ||||
| { | ||||
| 	struct objdump_line *self = malloc(sizeof(*self) + privsize); | ||||
| 
 | ||||
| 	if (self != NULL) { | ||||
| 		self->offset = offset; | ||||
| 		self->line = line; | ||||
| 	} | ||||
| 
 | ||||
| 	return self; | ||||
| } | ||||
| 
 | ||||
| void objdump_line__free(struct objdump_line *self) | ||||
| { | ||||
| 	free(self->line); | ||||
| 	free(self); | ||||
| } | ||||
| 
 | ||||
| static void objdump__add_line(struct list_head *head, struct objdump_line *line) | ||||
| { | ||||
| 	list_add_tail(&line->node, head); | ||||
| } | ||||
| 
 | ||||
| struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||||
| 					       struct objdump_line *pos) | ||||
| { | ||||
| 	list_for_each_entry_continue(pos, head, node) | ||||
| 		if (pos->offset >= 0) | ||||
| 			return pos; | ||||
| 
 | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static int objdump_line__print(struct objdump_line *oline, | ||||
| 			       struct list_head *head, struct symbol *sym, | ||||
| 			       int evidx, u64 len, int min_pcnt, | ||||
| 			       int printed, int max_lines) | ||||
| { | ||||
| 	static const char *prev_line; | ||||
| 	static const char *prev_color; | ||||
| 
 | ||||
| 	if (oline->offset != -1) { | ||||
| 		const char *path = NULL; | ||||
| 		unsigned int hits = 0; | ||||
| 		double percent = 0.0; | ||||
| 		const char *color; | ||||
| 		struct annotation *notes = symbol__annotation(sym); | ||||
| 		struct source_line *src_line = notes->src_line; | ||||
| 		struct sym_hist *h = annotation__histogram(notes, evidx); | ||||
| 		s64 offset = oline->offset; | ||||
| 		struct objdump_line *next = objdump__get_next_ip_line(head, oline); | ||||
| 
 | ||||
| 		while (offset < (s64)len && | ||||
| 		       (next == NULL || offset < next->offset)) { | ||||
| 			if (src_line) { | ||||
| 				if (path == NULL) | ||||
| 					path = src_line[offset].path; | ||||
| 				percent += src_line[offset].percent; | ||||
| 			} else | ||||
| 				hits += h->addr[offset]; | ||||
| 
 | ||||
| 			++offset; | ||||
| 		} | ||||
| 
 | ||||
| 		if (src_line == NULL && h->sum) | ||||
| 			percent = 100.0 * hits / h->sum; | ||||
| 
 | ||||
| 		if (percent < min_pcnt) | ||||
| 			return -1; | ||||
| 
 | ||||
| 		if (printed >= max_lines) | ||||
| 			return 1; | ||||
| 
 | ||||
| 		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 addr 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", oline->line); | ||||
| 	} else if (printed >= max_lines) | ||||
| 		return 1; | ||||
| 	else { | ||||
| 		if (!*oline->line) | ||||
| 			printf("         :\n"); | ||||
| 		else | ||||
| 			printf("         :	%s\n", oline->line); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, FILE *file, | ||||
| 				      struct list_head *head, size_t privsize) | ||||
| { | ||||
| 	struct objdump_line *objdump_line; | ||||
| 	char *line = NULL, *tmp, *tmp2, *c; | ||||
| 	size_t line_len; | ||||
| 	s64 line_ip, offset = -1; | ||||
| 
 | ||||
| 	if (getline(&line, &line_len, file) < 0) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	if (!line) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	while (line_len != 0 && isspace(line[line_len - 1])) | ||||
| 		line[--line_len] = '\0'; | ||||
| 
 | ||||
| 	c = strchr(line, '\n'); | ||||
| 	if (c) | ||||
| 		*c = 0; | ||||
| 
 | ||||
| 	line_ip = -1; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Strip leading spaces: | ||||
| 	 */ | ||||
| 	tmp = line; | ||||
| 	while (*tmp) { | ||||
| 		if (*tmp != ' ') | ||||
| 			break; | ||||
| 		tmp++; | ||||
| 	} | ||||
| 
 | ||||
| 	if (*tmp) { | ||||
| 		/*
 | ||||
| 		 * Parse hexa addresses followed by ':' | ||||
| 		 */ | ||||
| 		line_ip = strtoull(tmp, &tmp2, 16); | ||||
| 		if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0') | ||||
| 			line_ip = -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (line_ip != -1) { | ||||
| 		u64 start = map__rip_2objdump(map, sym->start), | ||||
| 		    end = map__rip_2objdump(map, sym->end); | ||||
| 
 | ||||
| 		offset = line_ip - start; | ||||
| 		if (offset < 0 || (u64)line_ip > end) | ||||
| 			offset = -1; | ||||
| 	} | ||||
| 
 | ||||
| 	objdump_line = objdump_line__new(offset, line, privsize); | ||||
| 	if (objdump_line == NULL) { | ||||
| 		free(line); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	objdump__add_line(head, objdump_line); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int symbol__annotate(struct symbol *sym, struct map *map, | ||||
| 		     struct list_head *head, size_t privsize) | ||||
| { | ||||
| 	struct dso *dso = map->dso; | ||||
| 	char *filename = dso__build_id_filename(dso, NULL, 0); | ||||
| 	bool free_filename = true; | ||||
| 	char command[PATH_MAX * 2]; | ||||
| 	FILE *file; | ||||
| 	int err = 0; | ||||
| 	u64 len; | ||||
| 	char symfs_filename[PATH_MAX]; | ||||
| 
 | ||||
| 	if (filename) { | ||||
| 		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | ||||
| 			 symbol_conf.symfs, filename); | ||||
| 	} | ||||
| 
 | ||||
| 	if (filename == NULL) { | ||||
| 		if (dso->has_build_id) { | ||||
| 			pr_err("Can't annotate %s: not enough memory\n", | ||||
| 			       sym->name); | ||||
| 			return -ENOMEM; | ||||
| 		} | ||||
| 		goto fallback; | ||||
| 	} else if (readlink(symfs_filename, command, sizeof(command)) < 0 || | ||||
| 		   strstr(command, "[kernel.kallsyms]") || | ||||
| 		   access(symfs_filename, R_OK)) { | ||||
| 		free(filename); | ||||
| fallback: | ||||
| 		/*
 | ||||
| 		 * If we don't have build-ids or the build-id file isn't in the | ||||
| 		 * cache, or is just a kallsyms file, well, lets hope that this | ||||
| 		 * DSO is the same as when 'perf record' ran. | ||||
| 		 */ | ||||
| 		filename = dso->long_name; | ||||
| 		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | ||||
| 			 symbol_conf.symfs, filename); | ||||
| 		free_filename = false; | ||||
| 	} | ||||
| 
 | ||||
| 	if (dso->origin == DSO__ORIG_KERNEL) { | ||||
| 		if (dso->annotate_warned) | ||||
| 			goto out_free_filename; | ||||
| 		err = -ENOENT; | ||||
| 		dso->annotate_warned = 1; | ||||
| 		pr_err("Can't annotate %s: No vmlinux file was found in the " | ||||
| 		       "path\n", sym->name); | ||||
| 		goto out_free_filename; | ||||
| 	} | ||||
| 
 | ||||
| 	pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, | ||||
| 		 filename, sym->name, map->unmap_ip(map, sym->start), | ||||
| 		 map->unmap_ip(map, sym->end)); | ||||
| 
 | ||||
| 	len = sym->end - sym->start; | ||||
| 
 | ||||
| 	pr_debug("annotating [%p] %30s : [%p] %30s\n", | ||||
| 		 dso, dso->long_name, sym, sym->name); | ||||
| 
 | ||||
| 	snprintf(command, sizeof(command), | ||||
| 		 "objdump --start-address=0x%016" PRIx64 | ||||
| 		 " --stop-address=0x%016" PRIx64 " -dS -C %s|grep -v %s|expand", | ||||
| 		 map__rip_2objdump(map, sym->start), | ||||
| 		 map__rip_2objdump(map, sym->end), | ||||
| 		 symfs_filename, filename); | ||||
| 
 | ||||
| 	pr_debug("Executing: %s\n", command); | ||||
| 
 | ||||
| 	file = popen(command, "r"); | ||||
| 	if (!file) | ||||
| 		goto out_free_filename; | ||||
| 
 | ||||
| 	while (!feof(file)) | ||||
| 		if (symbol__parse_objdump_line(sym, map, file, head, privsize) < 0) | ||||
| 			break; | ||||
| 
 | ||||
| 	pclose(file); | ||||
| out_free_filename: | ||||
| 	if (free_filename) | ||||
| 		free(filename); | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static void insert_source_line(struct rb_root *root, struct source_line *src_line) | ||||
| { | ||||
| 	struct source_line *iter; | ||||
| 	struct rb_node **p = &root->rb_node; | ||||
| 	struct rb_node *parent = NULL; | ||||
| 
 | ||||
| 	while (*p != NULL) { | ||||
| 		parent = *p; | ||||
| 		iter = rb_entry(parent, struct source_line, node); | ||||
| 
 | ||||
| 		if (src_line->percent > iter->percent) | ||||
| 			p = &(*p)->rb_left; | ||||
| 		else | ||||
| 			p = &(*p)->rb_right; | ||||
| 	} | ||||
| 
 | ||||
| 	rb_link_node(&src_line->node, parent, p); | ||||
| 	rb_insert_color(&src_line->node, root); | ||||
| } | ||||
| 
 | ||||
| static void symbol__free_source_line(struct symbol *sym, int len) | ||||
| { | ||||
| 	struct annotation *notes = symbol__annotation(sym); | ||||
| 	struct source_line *src_line = notes->src_line; | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < len; i++) | ||||
| 		free(src_line[i].path); | ||||
| 
 | ||||
| 	free(src_line); | ||||
| 	notes->src_line = NULL; | ||||
| } | ||||
| 
 | ||||
| /* Get the filename:line for the colored entries */ | ||||
| static int symbol__get_source_line(struct symbol *sym, struct map *map, | ||||
| 				   int evidx, struct rb_root *root, int len, | ||||
| 				   const char *filename) | ||||
| { | ||||
| 	u64 start; | ||||
| 	int i; | ||||
| 	char cmd[PATH_MAX * 2]; | ||||
| 	struct source_line *src_line; | ||||
| 	struct annotation *notes = symbol__annotation(sym); | ||||
| 	struct sym_hist *h = annotation__histogram(notes, evidx); | ||||
| 
 | ||||
| 	if (!h->sum) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	src_line = notes->src_line = calloc(len, sizeof(struct source_line)); | ||||
| 	if (!notes->src_line) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	start = map->unmap_ip(map, sym->start); | ||||
| 
 | ||||
| 	for (i = 0; i < len; i++) { | ||||
| 		char *path = NULL; | ||||
| 		size_t line_len; | ||||
| 		u64 offset; | ||||
| 		FILE *fp; | ||||
| 
 | ||||
| 		src_line[i].percent = 100.0 * h->addr[i] / h->sum; | ||||
| 		if (src_line[i].percent <= 0.5) | ||||
| 			continue; | ||||
| 
 | ||||
| 		offset = start + i; | ||||
| 		sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset); | ||||
| 		fp = popen(cmd, "r"); | ||||
| 		if (!fp) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (getline(&path, &line_len, fp) < 0 || !line_len) | ||||
| 			goto next; | ||||
| 
 | ||||
| 		src_line[i].path = malloc(sizeof(char) * line_len + 1); | ||||
| 		if (!src_line[i].path) | ||||
| 			goto next; | ||||
| 
 | ||||
| 		strcpy(src_line[i].path, path); | ||||
| 		insert_source_line(root, &src_line[i]); | ||||
| 
 | ||||
| 	next: | ||||
| 		pclose(fp); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void print_summary(struct rb_root *root, const char *filename) | ||||
| { | ||||
| 	struct source_line *src_line; | ||||
| 	struct rb_node *node; | ||||
| 
 | ||||
| 	printf("\nSorted summary for file %s\n", filename); | ||||
| 	printf("----------------------------------------------\n\n"); | ||||
| 
 | ||||
| 	if (RB_EMPTY_ROOT(root)) { | ||||
| 		printf(" Nothing higher than %1.1f%%\n", MIN_GREEN); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	node = rb_first(root); | ||||
| 	while (node) { | ||||
| 		double percent; | ||||
| 		const char *color; | ||||
| 		char *path; | ||||
| 
 | ||||
| 		src_line = rb_entry(node, struct source_line, node); | ||||
| 		percent = src_line->percent; | ||||
| 		color = get_percent_color(percent); | ||||
| 		path = src_line->path; | ||||
| 
 | ||||
| 		color_fprintf(stdout, color, " %7.2f %s", percent, path); | ||||
| 		node = rb_next(node); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void symbol__annotate_hits(struct symbol *sym, int evidx) | ||||
| { | ||||
| 	struct annotation *notes = symbol__annotation(sym); | ||||
| 	struct sym_hist *h = annotation__histogram(notes, evidx); | ||||
| 	u64 len = sym->end - sym->start, offset; | ||||
| 
 | ||||
| 	for (offset = 0; offset < len; ++offset) | ||||
| 		if (h->addr[offset] != 0) | ||||
| 			printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2, | ||||
| 			       sym->start + offset, h->addr[offset]); | ||||
| 	printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum); | ||||
| } | ||||
| 
 | ||||
| int symbol__annotate_printf(struct symbol *sym, struct map *map, | ||||
| 			    struct list_head *head, int evidx, bool full_paths, | ||||
| 			    int min_pcnt, int max_lines) | ||||
| { | ||||
| 	struct dso *dso = map->dso; | ||||
| 	const char *filename = dso->long_name, *d_filename; | ||||
| 	struct objdump_line *pos; | ||||
| 	int printed = 2; | ||||
| 	int more = 0; | ||||
| 	u64 len; | ||||
| 
 | ||||
| 	if (full_paths) | ||||
| 		d_filename = filename; | ||||
| 	else | ||||
| 		d_filename = basename(filename); | ||||
| 
 | ||||
| 	len = sym->end - sym->start; | ||||
| 
 | ||||
| 	printf(" Percent |	Source code & Disassembly of %s\n", d_filename); | ||||
| 	printf("------------------------------------------------\n"); | ||||
| 
 | ||||
| 	if (verbose) | ||||
| 		symbol__annotate_hits(sym, evidx); | ||||
| 
 | ||||
| 	list_for_each_entry(pos, head, node) { | ||||
| 		switch (objdump_line__print(pos, head, sym, evidx, len, min_pcnt, | ||||
| 					    printed, max_lines)) { | ||||
| 		case 0: | ||||
| 			++printed; | ||||
| 			break; | ||||
| 		case 1: | ||||
| 			/* filtered by max_lines */ | ||||
| 			++more; | ||||
| 			break; | ||||
| 		case -1: | ||||
| 		default: | ||||
| 			/* filtered by min_pcnt */ | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return more; | ||||
| } | ||||
| 
 | ||||
| void symbol__annotate_zero_histogram(struct symbol *sym, int evidx) | ||||
| { | ||||
| 	struct annotation *notes = symbol__annotation(sym); | ||||
| 	struct sym_hist *h = annotation__histogram(notes, evidx); | ||||
| 
 | ||||
| 	memset(h, 0, notes->sizeof_sym_hist); | ||||
| } | ||||
| 
 | ||||
| void symbol__annotate_decay_histogram(struct symbol *sym, | ||||
| 				      struct list_head *head, int evidx) | ||||
| { | ||||
| 	struct annotation *notes = symbol__annotation(sym); | ||||
| 	struct sym_hist *h = annotation__histogram(notes, evidx); | ||||
| 	struct objdump_line *pos; | ||||
| 
 | ||||
| 	h->sum = 0; | ||||
| 
 | ||||
| 	list_for_each_entry(pos, head, node) { | ||||
| 		if (pos->offset != -1) { | ||||
| 			h->addr[pos->offset] = h->addr[pos->offset] * 7 / 8; | ||||
| 			h->sum += h->addr[pos->offset]; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void objdump_line_list__purge(struct list_head *head) | ||||
| { | ||||
| 	struct objdump_line *pos, *n; | ||||
| 
 | ||||
| 	list_for_each_entry_safe(pos, n, head, node) { | ||||
| 		list_del(&pos->node); | ||||
| 		objdump_line__free(pos); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, | ||||
| 			 bool print_lines, bool full_paths, int min_pcnt, | ||||
| 			 int max_lines) | ||||
| { | ||||
| 	struct dso *dso = map->dso; | ||||
| 	const char *filename = dso->long_name; | ||||
| 	struct rb_root source_line = RB_ROOT; | ||||
| 	LIST_HEAD(head); | ||||
| 	u64 len; | ||||
| 
 | ||||
| 	if (symbol__annotate(sym, map, &head, 0) < 0) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	len = sym->end - sym->start; | ||||
| 
 | ||||
| 	if (print_lines) { | ||||
| 		symbol__get_source_line(sym, map, evidx, &source_line, | ||||
| 					len, filename); | ||||
| 		print_summary(&source_line, filename); | ||||
| 	} | ||||
| 
 | ||||
| 	symbol__annotate_printf(sym, map, &head, evidx, full_paths, | ||||
| 				min_pcnt, max_lines); | ||||
| 	if (print_lines) | ||||
| 		symbol__free_source_line(sym, len); | ||||
| 
 | ||||
| 	objdump_line_list__purge(&head); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										94
									
								
								tools/perf/util/annotate.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								tools/perf/util/annotate.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,94 @@ | ||||
| #ifndef __PERF_ANNOTATE_H | ||||
| #define __PERF_ANNOTATE_H | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| #include "types.h" | ||||
| #include "symbol.h" | ||||
| #include <linux/list.h> | ||||
| #include <linux/rbtree.h> | ||||
| 
 | ||||
| struct objdump_line { | ||||
| 	struct list_head node; | ||||
| 	s64		 offset; | ||||
| 	char		 *line; | ||||
| }; | ||||
| 
 | ||||
| void objdump_line__free(struct objdump_line *self); | ||||
| struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||||
| 					       struct objdump_line *pos); | ||||
| 
 | ||||
| struct sym_hist { | ||||
| 	u64		sum; | ||||
| 	u64		addr[0]; | ||||
| }; | ||||
| 
 | ||||
| struct source_line { | ||||
| 	struct rb_node	node; | ||||
| 	double		percent; | ||||
| 	char		*path; | ||||
| }; | ||||
| 
 | ||||
| /** struct annotation - symbols with hits have this attached as in sannotation
 | ||||
|  * | ||||
|  * @histogram: Array of addr hit histograms per event being monitored | ||||
|  * @src_line: If 'print_lines' is specified, per source code line percentages | ||||
|  * | ||||
|  * src_line is allocated, percentages calculated and all sorted by percentage | ||||
|  * when the annotation is about to be presented, so the percentages are for | ||||
|  * one of the entries in the histogram array, i.e. for the event/counter being | ||||
|  * presented. It is deallocated right after symbol__{tui,tty,etc}_annotate | ||||
|  * returns. | ||||
|  */ | ||||
| struct annotation { | ||||
| 	struct source_line *src_line; | ||||
| 	struct sym_hist	   *histograms; | ||||
| 	int    		   nr_histograms; | ||||
| 	int    		   sizeof_sym_hist; | ||||
| }; | ||||
| 
 | ||||
| struct sannotation { | ||||
| 	struct annotation annotation; | ||||
| 	struct symbol	  symbol; | ||||
| }; | ||||
| 
 | ||||
| static inline struct sym_hist *annotation__histogram(struct annotation *notes, int idx) | ||||
| { | ||||
| 	return ((void *)notes->histograms) + (notes->sizeof_sym_hist * idx); | ||||
| } | ||||
| 
 | ||||
| static inline struct annotation *symbol__annotation(struct symbol *sym) | ||||
| { | ||||
| 	struct sannotation *a = container_of(sym, struct sannotation, symbol); | ||||
| 	return &a->annotation; | ||||
| } | ||||
| 
 | ||||
| int symbol__inc_addr_samples(struct symbol *sym, struct map *map, | ||||
| 			     int evidx, u64 addr); | ||||
| int symbol__alloc_hist(struct symbol *sym, int nevents); | ||||
| void symbol__annotate_zero_histograms(struct symbol *sym); | ||||
| 
 | ||||
| int symbol__annotate(struct symbol *sym, struct map *map, | ||||
| 		     struct list_head *head, size_t privsize); | ||||
| int symbol__annotate_printf(struct symbol *sym, struct map *map, | ||||
| 			    struct list_head *head, int evidx, bool full_paths, | ||||
| 			    int min_pcnt, int max_lines); | ||||
| void symbol__annotate_zero_histogram(struct symbol *sym, int evidx); | ||||
| void symbol__annotate_decay_histogram(struct symbol *sym, | ||||
| 				      struct list_head *head, int evidx); | ||||
| void objdump_line_list__purge(struct list_head *head); | ||||
| 
 | ||||
| int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, | ||||
| 			 bool print_lines, bool full_paths, int min_pcnt, | ||||
| 			 int max_lines); | ||||
| 
 | ||||
| #ifdef NO_NEWT_SUPPORT | ||||
| static inline int symbol__tui_annotate(symbol *sym __used, | ||||
| 				       struct map *map __used, int evidx __used) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| #else | ||||
| int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx); | ||||
| #endif | ||||
| 
 | ||||
| #endif	/* __PERF_ANNOTATE_H */ | ||||
| @ -1,3 +1,4 @@ | ||||
| #include "annotate.h" | ||||
| #include "util.h" | ||||
| #include "build-id.h" | ||||
| #include "hist.h" | ||||
| @ -949,225 +950,15 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int symbol__alloc_hist(struct symbol *self) | ||||
| int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) | ||||
| { | ||||
| 	struct sym_priv *priv = symbol__priv(self); | ||||
| 	const int size = (sizeof(*priv->hist) + | ||||
| 			  (self->end - self->start) * sizeof(u64)); | ||||
| 
 | ||||
| 	priv->hist = zalloc(size); | ||||
| 	return priv->hist == NULL ? -1 : 0; | ||||
| 	return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); | ||||
| } | ||||
| 
 | ||||
| int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) | ||||
| { | ||||
| 	unsigned int sym_size, offset; | ||||
| 	struct symbol *sym = self->ms.sym; | ||||
| 	struct sym_priv *priv; | ||||
| 	struct sym_hist *h; | ||||
| 
 | ||||
| 	if (!sym || !self->ms.map) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	priv = symbol__priv(sym); | ||||
| 	if (priv->hist == NULL && symbol__alloc_hist(sym) < 0) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	sym_size = sym->end - sym->start; | ||||
| 	offset = ip - sym->start; | ||||
| 
 | ||||
| 	pr_debug3("%s: ip=%#" PRIx64 "\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip)); | ||||
| 
 | ||||
| 	if (offset >= sym_size) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	h = priv->hist; | ||||
| 	h->sum++; | ||||
| 	h->ip[offset]++; | ||||
| 
 | ||||
| 	pr_debug3("%#" PRIx64 " %s: period++ [ip: %#" PRIx64 ", %#" PRIx64 | ||||
| 		  "] => %" PRIu64 "\n", self->ms.sym->start, self->ms.sym->name, | ||||
| 		  ip, ip - self->ms.sym->start, h->ip[offset]); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize) | ||||
| { | ||||
| 	struct objdump_line *self = malloc(sizeof(*self) + privsize); | ||||
| 
 | ||||
| 	if (self != NULL) { | ||||
| 		self->offset = offset; | ||||
| 		self->line = line; | ||||
| 	} | ||||
| 
 | ||||
| 	return self; | ||||
| } | ||||
| 
 | ||||
| void objdump_line__free(struct objdump_line *self) | ||||
| { | ||||
| 	free(self->line); | ||||
| 	free(self); | ||||
| } | ||||
| 
 | ||||
| static void objdump__add_line(struct list_head *head, struct objdump_line *line) | ||||
| { | ||||
| 	list_add_tail(&line->node, head); | ||||
| } | ||||
| 
 | ||||
| struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||||
| 					       struct objdump_line *pos) | ||||
| { | ||||
| 	list_for_each_entry_continue(pos, head, node) | ||||
| 		if (pos->offset >= 0) | ||||
| 			return pos; | ||||
| 
 | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, | ||||
| 					  struct list_head *head, size_t privsize) | ||||
| { | ||||
| 	struct symbol *sym = self->ms.sym; | ||||
| 	struct objdump_line *objdump_line; | ||||
| 	char *line = NULL, *tmp, *tmp2, *c; | ||||
| 	size_t line_len; | ||||
| 	s64 line_ip, offset = -1; | ||||
| 
 | ||||
| 	if (getline(&line, &line_len, file) < 0) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	if (!line) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	while (line_len != 0 && isspace(line[line_len - 1])) | ||||
| 		line[--line_len] = '\0'; | ||||
| 
 | ||||
| 	c = strchr(line, '\n'); | ||||
| 	if (c) | ||||
| 		*c = 0; | ||||
| 
 | ||||
| 	line_ip = -1; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Strip leading spaces: | ||||
| 	 */ | ||||
| 	tmp = line; | ||||
| 	while (*tmp) { | ||||
| 		if (*tmp != ' ') | ||||
| 			break; | ||||
| 		tmp++; | ||||
| 	} | ||||
| 
 | ||||
| 	if (*tmp) { | ||||
| 		/*
 | ||||
| 		 * Parse hexa addresses followed by ':' | ||||
| 		 */ | ||||
| 		line_ip = strtoull(tmp, &tmp2, 16); | ||||
| 		if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0') | ||||
| 			line_ip = -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (line_ip != -1) { | ||||
| 		u64 start = map__rip_2objdump(self->ms.map, sym->start), | ||||
| 		    end = map__rip_2objdump(self->ms.map, sym->end); | ||||
| 
 | ||||
| 		offset = line_ip - start; | ||||
| 		if (offset < 0 || (u64)line_ip > end) | ||||
| 			offset = -1; | ||||
| 	} | ||||
| 
 | ||||
| 	objdump_line = objdump_line__new(offset, line, privsize); | ||||
| 	if (objdump_line == NULL) { | ||||
| 		free(line); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	objdump__add_line(head, objdump_line); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int hist_entry__annotate(struct hist_entry *self, struct list_head *head, | ||||
| int hist_entry__annotate(struct hist_entry *he, struct list_head *head, | ||||
| 			 size_t privsize) | ||||
| { | ||||
| 	struct symbol *sym = self->ms.sym; | ||||
| 	struct map *map = self->ms.map; | ||||
| 	struct dso *dso = map->dso; | ||||
| 	char *filename = dso__build_id_filename(dso, NULL, 0); | ||||
| 	bool free_filename = true; | ||||
| 	char command[PATH_MAX * 2]; | ||||
| 	FILE *file; | ||||
| 	int err = 0; | ||||
| 	u64 len; | ||||
| 	char symfs_filename[PATH_MAX]; | ||||
| 
 | ||||
| 	if (filename) { | ||||
| 		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | ||||
| 			 symbol_conf.symfs, filename); | ||||
| 	} | ||||
| 
 | ||||
| 	if (filename == NULL) { | ||||
| 		if (dso->has_build_id) { | ||||
| 			pr_err("Can't annotate %s: not enough memory\n", | ||||
| 			       sym->name); | ||||
| 			return -ENOMEM; | ||||
| 		} | ||||
| 		goto fallback; | ||||
| 	} else if (readlink(symfs_filename, command, sizeof(command)) < 0 || | ||||
| 		   strstr(command, "[kernel.kallsyms]") || | ||||
| 		   access(symfs_filename, R_OK)) { | ||||
| 		free(filename); | ||||
| fallback: | ||||
| 		/*
 | ||||
| 		 * If we don't have build-ids or the build-id file isn't in the | ||||
| 		 * cache, or is just a kallsyms file, well, lets hope that this | ||||
| 		 * DSO is the same as when 'perf record' ran. | ||||
| 		 */ | ||||
| 		filename = dso->long_name; | ||||
| 		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | ||||
| 			 symbol_conf.symfs, filename); | ||||
| 		free_filename = false; | ||||
| 	} | ||||
| 
 | ||||
| 	if (dso->origin == DSO__ORIG_KERNEL) { | ||||
| 		if (dso->annotate_warned) | ||||
| 			goto out_free_filename; | ||||
| 		err = -ENOENT; | ||||
| 		dso->annotate_warned = 1; | ||||
| 		pr_err("Can't annotate %s: No vmlinux file was found in the " | ||||
| 		       "path\n", sym->name); | ||||
| 		goto out_free_filename; | ||||
| 	} | ||||
| 
 | ||||
| 	pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, | ||||
| 		 filename, sym->name, map->unmap_ip(map, sym->start), | ||||
| 		 map->unmap_ip(map, sym->end)); | ||||
| 
 | ||||
| 	len = sym->end - sym->start; | ||||
| 
 | ||||
| 	pr_debug("annotating [%p] %30s : [%p] %30s\n", | ||||
| 		 dso, dso->long_name, sym, sym->name); | ||||
| 
 | ||||
| 	snprintf(command, sizeof(command), | ||||
| 		 "objdump --start-address=0x%016" PRIx64 " --stop-address=0x%016" PRIx64 " -dS -C %s|grep -v %s|expand", | ||||
| 		 map__rip_2objdump(map, sym->start), | ||||
| 		 map__rip_2objdump(map, sym->end), | ||||
| 		 symfs_filename, filename); | ||||
| 
 | ||||
| 	pr_debug("Executing: %s\n", command); | ||||
| 
 | ||||
| 	file = popen(command, "r"); | ||||
| 	if (!file) | ||||
| 		goto out_free_filename; | ||||
| 
 | ||||
| 	while (!feof(file)) | ||||
| 		if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0) | ||||
| 			break; | ||||
| 
 | ||||
| 	pclose(file); | ||||
| out_free_filename: | ||||
| 	if (free_filename) | ||||
| 		free(filename); | ||||
| 	return err; | ||||
| 	return symbol__annotate(he->ms.sym, he->ms.map, head, privsize); | ||||
| } | ||||
| 
 | ||||
| void hists__inc_nr_events(struct hists *self, u32 type) | ||||
|  | ||||
| @ -9,33 +9,6 @@ extern struct callchain_param callchain_param; | ||||
| struct hist_entry; | ||||
| struct addr_location; | ||||
| struct symbol; | ||||
| struct rb_root; | ||||
| 
 | ||||
| struct objdump_line { | ||||
| 	struct list_head node; | ||||
| 	s64		 offset; | ||||
| 	char		 *line; | ||||
| }; | ||||
| 
 | ||||
| void objdump_line__free(struct objdump_line *self); | ||||
| struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||||
| 					       struct objdump_line *pos); | ||||
| 
 | ||||
| struct sym_hist { | ||||
| 	u64		sum; | ||||
| 	u64		ip[0]; | ||||
| }; | ||||
| 
 | ||||
| struct sym_ext { | ||||
| 	struct rb_node	node; | ||||
| 	double		percent; | ||||
| 	char		*path; | ||||
| }; | ||||
| 
 | ||||
| struct sym_priv { | ||||
| 	struct sym_hist	*hist; | ||||
| 	struct sym_ext	*ext; | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * The kernel collects the number of events it couldn't send in a stretch and | ||||
| @ -104,7 +77,7 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); | ||||
| size_t hists__fprintf(struct hists *self, struct hists *pair, | ||||
| 		      bool show_displacement, FILE *fp); | ||||
| 
 | ||||
| int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip); | ||||
| int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); | ||||
| int hist_entry__annotate(struct hist_entry *self, struct list_head *head, | ||||
| 			 size_t privsize); | ||||
| 
 | ||||
| @ -118,18 +91,20 @@ bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); | ||||
| #ifdef NO_NEWT_SUPPORT | ||||
| static inline int hists__browse(struct hists *self __used, | ||||
| 				const char *helpline __used, | ||||
| 				const char *ev_name __used) | ||||
| 				const char *ev_name __used, int evidx __used) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static inline int hists__tui_browse_tree(struct rb_root *self __used, | ||||
| 					 const char *help __used) | ||||
| 					 const char *help __used, | ||||
| 					 int evidx __used) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static inline int hist_entry__tui_annotate(struct hist_entry *self __used) | ||||
| static inline int hist_entry__tui_annotate(struct hist_entry *self __used, | ||||
| 					   int evidx __used) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| @ -138,13 +113,13 @@ static inline int hist_entry__tui_annotate(struct hist_entry *self __used) | ||||
| #else | ||||
| #include <newt.h> | ||||
| int hists__browse(struct hists *self, const char *helpline, | ||||
| 		  const char *ev_name); | ||||
| int hist_entry__tui_annotate(struct hist_entry *self); | ||||
| 		  const char *ev_name, int evidx); | ||||
| int hist_entry__tui_annotate(struct hist_entry *self, int evidx); | ||||
| 
 | ||||
| #define KEY_LEFT NEWT_KEY_LEFT | ||||
| #define KEY_RIGHT NEWT_KEY_RIGHT | ||||
| 
 | ||||
| int hists__tui_browse_tree(struct rb_root *self, const char *help); | ||||
| int hists__tui_browse_tree(struct rb_root *self, const char *help, int evidx); | ||||
| #endif | ||||
| 
 | ||||
| unsigned int hists__sort_list_width(struct hists *self); | ||||
|  | ||||
| @ -200,6 +200,7 @@ void perf_top__find_widths(struct perf_top *top, struct rb_root *root, | ||||
| 
 | ||||
| 	for (nd = rb_first(root); nd; nd = rb_next(nd)) { | ||||
| 		struct sym_entry *syme = rb_entry(nd, struct sym_entry, rb_node); | ||||
| 		struct symbol *sym = sym_entry__symbol(syme); | ||||
| 
 | ||||
| 		if (++printed > top->print_entries || | ||||
| 		    (int)syme->snap_count < top->count_filter) | ||||
| @ -211,7 +212,7 @@ void perf_top__find_widths(struct perf_top *top, struct rb_root *root, | ||||
| 		if (syme->map->dso->short_name_len > *dso_short_width) | ||||
| 			*dso_short_width = syme->map->dso->short_name_len; | ||||
| 
 | ||||
| 		if (syme->name_len > *sym_width) | ||||
| 			*sym_width = syme->name_len; | ||||
| 		if (sym->namelen > *sym_width) | ||||
| 			*sym_width = sym->namelen; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -11,17 +11,8 @@ | ||||
| struct perf_evlist; | ||||
| struct perf_evsel; | ||||
| 
 | ||||
| struct source_line { | ||||
| 	u64			eip; | ||||
| 	unsigned long		count[MAX_COUNTERS]; /* FIXME */ | ||||
| 	char			*line; | ||||
| 	struct source_line	*next; | ||||
| }; | ||||
| 
 | ||||
| struct sym_entry_source { | ||||
| 	struct source_line	*source; | ||||
| 	struct source_line	*lines; | ||||
| 	struct source_line	**lines_tail; | ||||
| 	struct list_head	head; | ||||
| 	pthread_mutex_t		lock; | ||||
| }; | ||||
| 
 | ||||
| @ -31,7 +22,6 @@ struct sym_entry { | ||||
| 	unsigned long		snap_count; | ||||
| 	double			weight; | ||||
| 	int			skip; | ||||
| 	u16			name_len; | ||||
| 	u8			origin; | ||||
| 	struct map		*map; | ||||
| 	struct sym_entry_source	*src; | ||||
|  | ||||
| @ -1,9 +1,11 @@ | ||||
| #include "../browser.h" | ||||
| #include "../helpline.h" | ||||
| #include "../libslang.h" | ||||
| #include "../../annotate.h" | ||||
| #include "../../hist.h" | ||||
| #include "../../sort.h" | ||||
| #include "../../symbol.h" | ||||
| #include "../../annotate.h" | ||||
| 
 | ||||
| static void ui__error_window(const char *fmt, ...) | ||||
| { | ||||
| @ -59,31 +61,33 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro | ||||
| 
 | ||||
| static double objdump_line__calc_percent(struct objdump_line *self, | ||||
| 					 struct list_head *head, | ||||
| 					 struct symbol *sym) | ||||
| 					 struct symbol *sym, int evidx) | ||||
| { | ||||
| 	double percent = 0.0; | ||||
| 
 | ||||
| 	if (self->offset != -1) { | ||||
| 		int len = sym->end - sym->start; | ||||
| 		unsigned int hits = 0; | ||||
| 		struct sym_priv *priv = symbol__priv(sym); | ||||
| 		struct sym_ext *sym_ext = priv->ext; | ||||
| 		struct sym_hist *h = priv->hist; | ||||
| 		struct annotation *notes = symbol__annotation(sym); | ||||
| 		struct source_line *src_line = notes->src_line; | ||||
| 		struct sym_hist *h = annotation__histogram(notes, evidx); | ||||
| 		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) { | ||||
| 				percent += sym_ext[offset].percent; | ||||
| 			if (src_line) { | ||||
| 				percent += src_line[offset].percent; | ||||
| 			} else | ||||
| 				hits += h->ip[offset]; | ||||
| 				hits += h->addr[offset]; | ||||
| 
 | ||||
| 			++offset; | ||||
| 		} | ||||
| 
 | ||||
| 		if (sym_ext == NULL && h->sum) | ||||
| 		/*
 | ||||
|  		 * If the percentage wasn't already calculated in | ||||
|  		 * symbol__get_source_line, do it now: | ||||
|  		 */ | ||||
| 		if (src_line == NULL && h->sum) | ||||
| 			percent = 100.0 * hits / h->sum; | ||||
| 	} | ||||
| 
 | ||||
| @ -136,10 +140,10 @@ static void annotate_browser__set_top(struct annotate_browser *self, | ||||
| static int annotate_browser__run(struct annotate_browser *self) | ||||
| { | ||||
| 	struct rb_node *nd; | ||||
| 	struct hist_entry *he = self->b.priv; | ||||
| 	struct symbol *sym = self->b.priv; | ||||
| 	int key; | ||||
| 
 | ||||
| 	if (ui_browser__show(&self->b, he->ms.sym->name, | ||||
| 	if (ui_browser__show(&self->b, sym->name, | ||||
| 			     "<-, -> or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0) | ||||
| 		return -1; | ||||
| 	/*
 | ||||
| @ -179,7 +183,12 @@ out: | ||||
| 	return key; | ||||
| } | ||||
| 
 | ||||
| int hist_entry__tui_annotate(struct hist_entry *self) | ||||
| int hist_entry__tui_annotate(struct hist_entry *he, int evidx) | ||||
| { | ||||
| 	return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx); | ||||
| } | ||||
| 
 | ||||
| int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx) | ||||
| { | ||||
| 	struct objdump_line *pos, *n; | ||||
| 	struct objdump_line_rb_node *rbpos; | ||||
| @ -190,18 +199,18 @@ int hist_entry__tui_annotate(struct hist_entry *self) | ||||
| 			.refresh = ui_browser__list_head_refresh, | ||||
| 			.seek	 = ui_browser__list_head_seek, | ||||
| 			.write	 = annotate_browser__write, | ||||
| 			.priv	 = self, | ||||
| 			.priv	 = sym, | ||||
| 		}, | ||||
| 	}; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (self->ms.sym == NULL) | ||||
| 	if (sym == NULL) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	if (self->ms.map->dso->annotate_warned) | ||||
| 	if (map->dso->annotate_warned) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	if (hist_entry__annotate(self, &head, sizeof(*rbpos)) < 0) { | ||||
| 	if (symbol__annotate(sym, map, &head, sizeof(*rbpos)) < 0) { | ||||
| 		ui__error_window(ui_helpline__last_msg); | ||||
| 		return -1; | ||||
| 	} | ||||
| @ -214,7 +223,7 @@ int hist_entry__tui_annotate(struct hist_entry *self) | ||||
| 			browser.b.width = line_len; | ||||
| 		rbpos = objdump_line__rb(pos); | ||||
| 		rbpos->idx = browser.b.nr_entries++; | ||||
| 		rbpos->percent = objdump_line__calc_percent(pos, &head, self->ms.sym); | ||||
| 		rbpos->percent = objdump_line__calc_percent(pos, &head, sym, evidx); | ||||
| 		if (rbpos->percent < 0.01) | ||||
| 			continue; | ||||
| 		objdump__insert_line(&browser.entries, rbpos); | ||||
|  | ||||
| @ -797,7 +797,8 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size, | ||||
| 	return printed; | ||||
| } | ||||
| 
 | ||||
| int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | ||||
| int hists__browse(struct hists *self, const char *helpline, | ||||
| 		  const char *ev_name, int evidx) | ||||
| { | ||||
| 	struct hist_browser *browser = hist_browser__new(self); | ||||
| 	struct pstack *fstack; | ||||
| @ -935,7 +936,7 @@ do_annotate: | ||||
| 			if (he == NULL) | ||||
| 				continue; | ||||
| 
 | ||||
| 			hist_entry__tui_annotate(he); | ||||
| 			hist_entry__tui_annotate(he, evidx); | ||||
| 		} else if (choice == browse_map) | ||||
| 			map__browse(browser->selection->map); | ||||
| 		else if (choice == zoom_dso) { | ||||
| @ -984,7 +985,7 @@ out: | ||||
| 	return key; | ||||
| } | ||||
| 
 | ||||
| int hists__tui_browse_tree(struct rb_root *self, const char *help) | ||||
| int hists__tui_browse_tree(struct rb_root *self, const char *help, int evidx) | ||||
| { | ||||
| 	struct rb_node *first = rb_first(self), *nd = first, *next; | ||||
| 	int key = 0; | ||||
| @ -993,7 +994,7 @@ int hists__tui_browse_tree(struct rb_root *self, const char *help) | ||||
| 		struct hists *hists = rb_entry(nd, struct hists, rb_node); | ||||
| 		const char *ev_name = __event_name(hists->type, hists->config); | ||||
| 
 | ||||
| 		key = hists__browse(hists, help, ev_name); | ||||
| 		key = hists__browse(hists, help, ev_name, evidx); | ||||
| 		switch (key) { | ||||
| 		case NEWT_KEY_TAB: | ||||
| 			next = rb_next(nd); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user