diff options
Diffstat (limited to 'tools/perf/builtin-annotate.c')
| -rw-r--r-- | tools/perf/builtin-annotate.c | 1012 | 
1 files changed, 251 insertions, 761 deletions
| diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 1ec74161581..6ad7148451c 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -19,29 +19,26 @@  #include "perf.h"  #include "util/debug.h" +#include "util/event.h"  #include "util/parse-options.h"  #include "util/parse-events.h"  #include "util/thread.h" +#include "util/sort.h" +#include "util/hist.h" +#include "util/session.h"  static char		const *input_name = "perf.data"; -static char		default_sort_order[] = "comm,symbol"; -static char		*sort_order = default_sort_order; -  static int		force; -static int		input; -static int		show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;  static int		full_paths;  static int		print_line; -static unsigned long	page_size; -static unsigned long	mmap_window = 32; - -static struct rb_root	threads; -static struct thread	*last_match; - +struct sym_hist { +	u64		sum; +	u64		ip[0]; +};  struct sym_ext {  	struct rb_node	node; @@ -49,636 +46,157 @@ struct sym_ext {  	char		*path;  }; -/* - * histogram, sorted on item, collects counts - */ - -static struct rb_root hist; - -struct hist_entry { -	struct rb_node	 rb_node; - -	struct thread	 *thread; -	struct map	 *map; -	struct dso	 *dso; -	struct symbol	 *sym; -	u64	 ip; -	char		 level; - -	uint32_t	 count; -}; - -/* - * configurable sorting bits - */ - -struct sort_entry { -	struct list_head list; - -	const char *header; - -	int64_t (*cmp)(struct hist_entry *, struct hist_entry *); -	int64_t (*collapse)(struct hist_entry *, struct hist_entry *); -	size_t	(*print)(FILE *fp, struct hist_entry *); -}; - -/* --sort pid */ - -static int64_t -sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) -{ -	return right->thread->pid - left->thread->pid; -} - -static size_t -sort__thread_print(FILE *fp, struct hist_entry *self) -{ -	return fprintf(fp, "%16s:%5d", self->thread->comm ?: "", self->thread->pid); -} - -static struct sort_entry sort_thread = { -	.header = "         Command:  Pid", -	.cmp	= sort__thread_cmp, -	.print	= sort__thread_print, -}; - -/* --sort comm */ - -static int64_t -sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) -{ -	return right->thread->pid - left->thread->pid; -} - -static int64_t -sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) -{ -	char *comm_l = left->thread->comm; -	char *comm_r = right->thread->comm; - -	if (!comm_l || !comm_r) { -		if (!comm_l && !comm_r) -			return 0; -		else if (!comm_l) -			return -1; -		else -			return 1; -	} - -	return strcmp(comm_l, comm_r); -} - -static size_t -sort__comm_print(FILE *fp, struct hist_entry *self) -{ -	return fprintf(fp, "%16s", self->thread->comm); -} - -static struct sort_entry sort_comm = { -	.header		= "         Command", -	.cmp		= sort__comm_cmp, -	.collapse	= sort__comm_collapse, -	.print		= sort__comm_print, -}; - -/* --sort dso */ - -static int64_t -sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) -{ -	struct dso *dso_l = left->dso; -	struct dso *dso_r = right->dso; - -	if (!dso_l || !dso_r) { -		if (!dso_l && !dso_r) -			return 0; -		else if (!dso_l) -			return -1; -		else -			return 1; -	} - -	return strcmp(dso_l->name, dso_r->name); -} - -static size_t -sort__dso_print(FILE *fp, struct hist_entry *self) -{ -	if (self->dso) -		return fprintf(fp, "%-25s", self->dso->name); - -	return fprintf(fp, "%016llx         ", (u64)self->ip); -} - -static struct sort_entry sort_dso = { -	.header = "Shared Object            ", -	.cmp	= sort__dso_cmp, -	.print	= sort__dso_print, -}; - -/* --sort symbol */ - -static int64_t -sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) -{ -	u64 ip_l, ip_r; - -	if (left->sym == right->sym) -		return 0; - -	ip_l = left->sym ? left->sym->start : left->ip; -	ip_r = right->sym ? right->sym->start : right->ip; - -	return (int64_t)(ip_r - ip_l); -} - -static size_t -sort__sym_print(FILE *fp, struct hist_entry *self) -{ -	size_t ret = 0; - -	if (verbose) -		ret += fprintf(fp, "%#018llx  ", (u64)self->ip); - -	if (self->sym) { -		ret += fprintf(fp, "[%c] %s", -			self->dso == kernel_dso ? 'k' : '.', self->sym->name); -	} else { -		ret += fprintf(fp, "%#016llx", (u64)self->ip); -	} - -	return ret; -} - -static struct sort_entry sort_sym = { -	.header = "Symbol", -	.cmp	= sort__sym_cmp, -	.print	= sort__sym_print, -}; - -static int sort__need_collapse = 0; - -struct sort_dimension { -	const char		*name; -	struct sort_entry	*entry; -	int			taken; +struct sym_priv { +	struct sym_hist	*hist; +	struct sym_ext	*ext;  }; -static struct sort_dimension sort_dimensions[] = { -	{ .name = "pid",	.entry = &sort_thread,	}, -	{ .name = "comm",	.entry = &sort_comm,	}, -	{ .name = "dso",	.entry = &sort_dso,	}, -	{ .name = "symbol",	.entry = &sort_sym,	}, -}; - -static LIST_HEAD(hist_entry__sort_list); - -static int sort_dimension__add(char *tok) -{ -	unsigned int i; - -	for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { -		struct sort_dimension *sd = &sort_dimensions[i]; - -		if (sd->taken) -			continue; - -		if (strncasecmp(tok, sd->name, strlen(tok))) -			continue; - -		if (sd->entry->collapse) -			sort__need_collapse = 1; - -		list_add_tail(&sd->entry->list, &hist_entry__sort_list); -		sd->taken = 1; - -		return 0; -	} - -	return -ESRCH; -} - -static int64_t -hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) -{ -	struct sort_entry *se; -	int64_t cmp = 0; - -	list_for_each_entry(se, &hist_entry__sort_list, list) { -		cmp = se->cmp(left, right); -		if (cmp) -			break; -	} - -	return cmp; -} +static const char *sym_hist_filter; -static int64_t -hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) +static int sym__alloc_hist(struct symbol *self)  { -	struct sort_entry *se; -	int64_t cmp = 0; +	struct sym_priv *priv = symbol__priv(self); +	const int size = (sizeof(*priv->hist) + +			  (self->end - self->start) * sizeof(u64)); -	list_for_each_entry(se, &hist_entry__sort_list, list) { -		int64_t (*f)(struct hist_entry *, struct hist_entry *); - -		f = se->collapse ?: se->cmp; - -		cmp = f(left, right); -		if (cmp) -			break; -	} - -	return cmp; +	priv->hist = zalloc(size); +	return priv->hist == NULL ? -1 : 0;  }  /*   * collect histogram counts   */ -static void hist_hit(struct hist_entry *he, u64 ip) +static int annotate__hist_hit(struct hist_entry *he, u64 ip)  {  	unsigned int sym_size, offset;  	struct symbol *sym = he->sym; +	struct sym_priv *priv; +	struct sym_hist *h;  	he->count++; -	if (!sym || !sym->hist) -		return; +	if (!sym || !he->map) +		return 0; + +	priv = symbol__priv(sym); +	if (priv->hist == NULL && sym__alloc_hist(sym) < 0) +		return -ENOMEM;  	sym_size = sym->end - sym->start;  	offset = ip - sym->start; +	pr_debug3("%s: ip=%#Lx\n", __func__, he->map->unmap_ip(he->map, ip)); +  	if (offset >= sym_size) -		return; +		return 0; -	sym->hist_sum++; -	sym->hist[offset]++; +	h = priv->hist; +	h->sum++; +	h->ip[offset]++; -	if (verbose >= 3) -		printf("%p %s: count++ [ip: %p, %08Lx] => %Ld\n", -			(void *)(unsigned long)he->sym->start, -			he->sym->name, -			(void *)(unsigned long)ip, ip - he->sym->start, -			sym->hist[offset]); +	pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->sym->start, +		  he->sym->name, ip, ip - he->sym->start, h->ip[offset]); +	return 0;  } -static int -hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, -		struct symbol *sym, u64 ip, char level) +static int perf_session__add_hist_entry(struct perf_session *self, +					struct addr_location *al, u64 count)  { -	struct rb_node **p = &hist.rb_node; -	struct rb_node *parent = NULL; +	bool hit;  	struct hist_entry *he; -	struct hist_entry entry = { -		.thread	= thread, -		.map	= map, -		.dso	= dso, -		.sym	= sym, -		.ip	= ip, -		.level	= level, -		.count	= 1, -	}; -	int cmp; - -	while (*p != NULL) { -		parent = *p; -		he = rb_entry(parent, struct hist_entry, rb_node); - -		cmp = hist_entry__cmp(&entry, he); - -		if (!cmp) { -			hist_hit(he, ip); -			return 0; +	if (sym_hist_filter != NULL && +	    (al->sym == NULL || strcmp(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, +				 &al->map->dso->symbols[al->map->type]); +			symbol__delete(al->sym);  		} - -		if (cmp < 0) -			p = &(*p)->rb_left; -		else -			p = &(*p)->rb_right; +		return 0;  	} -	he = malloc(sizeof(*he)); -	if (!he) +	he = __perf_session__add_hist_entry(&self->hists, al, NULL, count, &hit); +	if (he == NULL)  		return -ENOMEM; -	*he = entry; -	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) -{ -	struct rb_node **p = &output_hists.rb_node; -	struct rb_node *parent = NULL; -	struct hist_entry *iter; - -	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); +	return annotate__hist_hit(he, al->addr);  } -static void output__resort(void) +static int process_sample_event(event_t *event, struct perf_session *session)  { -	struct rb_node *next; -	struct hist_entry *n; -	struct rb_root *tree = &hist; - -	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); -	} -} +	struct addr_location al; -static unsigned long total = 0, -		     total_mmap = 0, -		     total_comm = 0, -		     total_fork = 0, -		     total_unknown = 0; +	dump_printf("(IP, %d): %d: %#Lx\n", event->header.misc, +		    event->ip.pid, event->ip.ip); -static int -process_sample_event(event_t *event, unsigned long offset, unsigned long head) -{ -	char level; -	int show = 0; -	struct dso *dso = NULL; -	struct thread *thread; -	u64 ip = event->ip.ip; -	struct map *map = NULL; - -	thread = threads__findnew(event->ip.pid, &threads, &last_match); - -	dump_printf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n", -		(void *)(offset + head), -		(void *)(long)(event->header.size), -		event->header.misc, -		event->ip.pid, -		(void *)(long)ip); - -	dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); - -	if (thread == NULL) { -		fprintf(stderr, "problem processing %d event, skipping it.\n", -			event->header.type); +	if (event__preprocess_sample(event, session, &al, NULL) < 0) { +		pr_warning("problem processing %d event, skipping it.\n", +			   event->header.type);  		return -1;  	} -	if (event->header.misc & PERF_RECORD_MISC_KERNEL) { -		show = SHOW_KERNEL; -		level = 'k'; - -		dso = kernel_dso; - -		dump_printf(" ...... dso: %s\n", dso->name); - -	} else if (event->header.misc & PERF_RECORD_MISC_USER) { - -		show = SHOW_USER; -		level = '.'; - -		map = thread__find_map(thread, ip); -		if (map != NULL) { -			ip = map->map_ip(map, ip); -			dso = map->dso; -		} 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; -		} -		dump_printf(" ...... dso: %s\n", dso ? dso->name : "<not found>"); - -	} else { -		show = SHOW_HV; -		level = 'H'; -		dump_printf(" ...... dso: [hypervisor]\n"); -	} - -	if (show & show_mask) { -		struct symbol *sym = NULL; - -		if (dso) -			sym = dso->find_symbol(dso, ip); - -		if (hist_entry__add(thread, map, dso, sym, ip, level)) { -			fprintf(stderr, -		"problem incrementing symbol count, skipping event\n"); -			return -1; -		} +	if (!al.filtered && perf_session__add_hist_entry(session, &al, 1)) { +		pr_warning("problem incrementing symbol count, " +			   "skipping event\n"); +		return -1;  	} -	total++;  	return 0;  } -static int -process_mmap_event(event_t *event, unsigned long offset, unsigned long head) -{ -	struct thread *thread; -	struct map *map = map__new(&event->mmap, NULL, 0); - -	thread = threads__findnew(event->mmap.pid, &threads, &last_match); - -	dump_printf("%p [%p]: PERF_RECORD_MMAP %d: [%p(%p) @ %p]: %s\n", -		(void *)(offset + head), -		(void *)(long)(event->header.size), -		event->mmap.pid, -		(void *)(long)event->mmap.start, -		(void *)(long)event->mmap.len, -		(void *)(long)event->mmap.pgoff, -		event->mmap.filename); - -	if (thread == NULL || map == NULL) { -		dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); -		return 0; -	} - -	thread__insert_map(thread, map); -	total_mmap++; - -	return 0; -} +struct objdump_line { +	struct list_head node; +	s64		 offset; +	char		 *line; +}; -static int -process_comm_event(event_t *event, unsigned long offset, unsigned long head) +static struct objdump_line *objdump_line__new(s64 offset, char *line)  { -	struct thread *thread; +	struct objdump_line *self = malloc(sizeof(*self)); -	thread = threads__findnew(event->comm.pid, &threads, &last_match); -	dump_printf("%p [%p]: PERF_RECORD_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)) { -		dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); -		return -1; +	if (self != NULL) { +		self->offset = offset; +		self->line = line;  	} -	total_comm++; -	return 0; +	return self;  } -static int -process_fork_event(event_t *event, unsigned long offset, unsigned long head) +static void objdump_line__free(struct objdump_line *self)  { -	struct thread *thread; -	struct thread *parent; - -	thread = threads__findnew(event->fork.pid, &threads, &last_match); -	parent = threads__findnew(event->fork.ppid, &threads, &last_match); -	dump_printf("%p [%p]: PERF_RECORD_FORK: %d:%d\n", -		(void *)(offset + head), -		(void *)(long)(event->header.size), -		event->fork.pid, event->fork.ppid); - -	/* -	 * A thread clone will have the same PID for both -	 * parent and child. -	 */ -	if (thread == parent) -		return 0; - -	if (!thread || !parent || thread__fork(thread, parent)) { -		dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); -		return -1; -	} -	total_fork++; - -	return 0; +	free(self->line); +	free(self);  } -static int -process_event(event_t *event, unsigned long offset, unsigned long head) +static void objdump__add_line(struct list_head *head, struct objdump_line *line)  { -	switch (event->header.type) { -	case PERF_RECORD_SAMPLE: -		return process_sample_event(event, offset, head); - -	case PERF_RECORD_MMAP: -		return process_mmap_event(event, offset, head); - -	case PERF_RECORD_COMM: -		return process_comm_event(event, offset, head); - -	case PERF_RECORD_FORK: -		return process_fork_event(event, offset, head); -	/* -	 * We dont process them right now but they are fine: -	 */ - -	case PERF_RECORD_THROTTLE: -	case PERF_RECORD_UNTHROTTLE: -		return 0; +	list_add_tail(&line->node, head); +} -	default: -		return -1; -	} +static 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 0; +	return NULL;  } -static int -parse_line(FILE *file, struct symbol *sym, u64 start, u64 len) +static int parse_line(FILE *file, struct hist_entry *he, +		      struct list_head *head)  { +	struct symbol *sym = he->sym; +	struct objdump_line *objdump_line;  	char *line = NULL, *tmp, *tmp2; -	static const char *prev_line; -	static const char *prev_color; -	unsigned int offset;  	size_t line_len; -	s64 line_ip; -	int ret; +	s64 line_ip, offset = -1;  	char *c;  	if (getline(&line, &line_len, file) < 0)  		return -1; +  	if (!line)  		return -1; @@ -687,8 +205,6 @@ parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)  		*c = 0;  	line_ip = -1; -	offset = 0; -	ret = -2;  	/*  	 * Strip leading spaces: @@ -710,21 +226,53 @@ parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)  	}  	if (line_ip != -1) { +		u64 start = map__rip_2objdump(he->map, sym->start); +		offset = line_ip - start; +	} + +	objdump_line = objdump_line__new(offset, line); +	if (objdump_line == NULL) { +		free(line); +		return -1; +	} +	objdump__add_line(head, objdump_line); + +	return 0; +} + +static int objdump_line__print(struct objdump_line *self, +			       struct list_head *head, +			       struct hist_entry *he, u64 len) +{ +	struct symbol *sym = he->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_ext *sym_ext = sym->priv; - -		offset = line_ip - start; -		if (offset < len) -			hits = sym->hist[offset]; +		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 (offset < len && sym_ext) { -			path = sym_ext[offset].path; -			percent = sym_ext[offset].percent; -		} else if (sym->hist_sum) -			percent = 100.0 * hits / sym->hist_sum; +		if (sym_ext == NULL && h->sum) +			percent = 100.0 * hits / h->sum;  		color = get_percent_color(percent); @@ -744,12 +292,12 @@ parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)  		color_fprintf(stdout, color, " %7.2f", percent);  		printf(" :	"); -		color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line); +		color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", self->line);  	} else { -		if (!*line) +		if (!*self->line)  			printf("         :\n");  		else -			printf("         :	%s\n", line); +			printf("         :	%s\n", self->line);  	}  	return 0; @@ -777,9 +325,10 @@ static void insert_source_line(struct sym_ext *sym_ext)  	rb_insert_color(&sym_ext->node, &root_sym_ext);  } -static void free_source_line(struct symbol *sym, int len) +static void free_source_line(struct hist_entry *he, int len)  { -	struct sym_ext *sym_ext = sym->priv; +	struct sym_priv *priv = symbol__priv(he->sym); +	struct sym_ext *sym_ext = priv->ext;  	int i;  	if (!sym_ext) @@ -789,26 +338,30 @@ static void free_source_line(struct symbol *sym, int len)  		free(sym_ext[i].path);  	free(sym_ext); -	sym->priv = NULL; +	priv->ext = NULL;  	root_sym_ext = RB_ROOT;  }  /* Get the filename:line for the colored entries */  static void -get_source_line(struct symbol *sym, u64 start, int len, const char *filename) +get_source_line(struct hist_entry *he, int len, const char *filename)  { +	struct symbol *sym = he->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 (!sym->hist_sum) +	if (!h->sum)  		return; -	sym->priv = calloc(len, sizeof(struct sym_ext)); -	if (!sym->priv) +	sym_ext = priv->ext = calloc(len, sizeof(struct sym_ext)); +	if (!priv->ext)  		return; -	sym_ext = sym->priv; +	start = he->map->unmap_ip(he->map, sym->start);  	for (i = 0; i < len; i++) {  		char *path = NULL; @@ -816,7 +369,7 @@ get_source_line(struct symbol *sym, u64 start, int len, const char *filename)  		u64 offset;  		FILE *fp; -		sym_ext[i].percent = 100.0 * sym->hist[i] / sym->hist_sum; +		sym_ext[i].percent = 100.0 * h->ip[i] / h->sum;  		if (sym_ext[i].percent <= 0.5)  			continue; @@ -870,33 +423,48 @@ static void print_summary(const char *filename)  	}  } -static void annotate_sym(struct dso *dso, struct symbol *sym) +static void hist_entry__print_hits(struct hist_entry *self) +{ +	struct symbol *sym = self->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 void annotate_sym(struct hist_entry *he)  { -	const char *filename = dso->name, *d_filename; -	u64 start, end, len; +	struct map *map = he->map; +	struct dso *dso = map->dso; +	struct symbol *sym = he->sym; +	const char *filename = dso->long_name, *d_filename; +	u64 len;  	char command[PATH_MAX*2];  	FILE *file; +	LIST_HEAD(head); +	struct objdump_line *pos, *n;  	if (!filename)  		return; -	if (sym->module) -		filename = sym->module->path; -	else if (dso == kernel_dso) -		filename = vmlinux_name; - -	start = sym->obj_start; -	if (!start) -		start = sym->start; + +	pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, +		 filename, sym->name, map->unmap_ip(map, sym->start), +		 map->unmap_ip(map, sym->end)); +  	if (full_paths)  		d_filename = filename;  	else  		d_filename = basename(filename); -	end = start + sym->end - sym->start + 1;  	len = sym->end - sym->start;  	if (print_line) { -		get_source_line(sym, start, len, filename); +		get_source_line(he, len, filename);  		print_summary(filename);  	} @@ -905,10 +473,13 @@ static void annotate_sym(struct dso *dso, struct symbol *sym)  	printf("------------------------------------------------\n");  	if (verbose >= 2) -		printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name); +		printf("annotating [%p] %30s : [%p] %30s\n", +		       dso, dso->long_name, sym, sym->name);  	sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s", -			(u64)start, (u64)end, filename, filename); +		map__rip_2objdump(map, sym->start), +		map__rip_2objdump(map, sym->end), +		filename, filename);  	if (verbose >= 3)  		printf("doing: %s\n", command); @@ -918,159 +489,88 @@ static void annotate_sym(struct dso *dso, struct symbol *sym)  		return;  	while (!feof(file)) { -		if (parse_line(file, sym, start, len) < 0) +		if (parse_line(file, he, &head) < 0)  			break;  	}  	pclose(file); -	if (print_line) -		free_source_line(sym, len); -} -static void find_annotations(void) -{ -	struct rb_node *nd; -	struct dso *dso; -	int count = 0; - -	list_for_each_entry(dso, &dsos, node) { - -		for (nd = rb_first(&dso->syms); nd; nd = rb_next(nd)) { -			struct symbol *sym = rb_entry(nd, struct symbol, rb_node); +	if (verbose) +		hist_entry__print_hits(he); -			if (sym->hist) { -				annotate_sym(dso, sym); -				count++; -			} -		} +	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 (!count) -		printf(" Error: symbol '%s' not present amongst the samples.\n", sym_hist_filter); +	if (print_line) +		free_source_line(he, len);  } -static int __cmd_annotate(void) +static void perf_session__find_annotations(struct perf_session *self)  { -	int ret, rc = EXIT_FAILURE; -	unsigned long offset = 0; -	unsigned long head = 0; -	struct stat input_stat; -	event_t *event; -	uint32_t size; -	char *buf; - -	register_idle_thread(&threads, &last_match); - -	input = open(input_name, O_RDONLY); -	if (input < 0) { -		perror("failed to open file"); -		exit(-1); -	} - -	ret = fstat(input, &input_stat); -	if (ret < 0) { -		perror("failed to stat file"); -		exit(-1); -	} - -	if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { -		fprintf(stderr, "file: %s not owned by current user or root\n", input_name); -		exit(-1); -	} - -	if (!input_stat.st_size) { -		fprintf(stderr, "zero-sized file, nothing to do!\n"); -		exit(0); -	} - -	if (load_kernel() < 0) { -		perror("failed to load kernel symbols"); -		return EXIT_FAILURE; -	} - -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) { -		unsigned long shift = page_size * (head / page_size); -		int munmap_ret; - -		munmap_ret = munmap(buf, page_size * mmap_window); -		assert(munmap_ret == 0); - -		offset += shift; -		head -= shift; -		goto remap; -	} - -	size = event->header.size; - -	dump_printf("%p [%p]: event: %d\n", -			(void *)(offset + head), -			(void *)(long)event->header.size, -			event->header.type); +	struct rb_node *nd; -	if (!size || process_event(event, offset, head) < 0) { +	for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) { +		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); +		struct sym_priv *priv; -		dump_printf("%p [%p]: skipping unknown header type: %d\n", -			(void *)(offset + head), -			(void *)(long)(event->header.size), -			event->header.type); +		if (he->sym == NULL) +			continue; -		total_unknown++; +		priv = symbol__priv(he->sym); +		if (priv->hist == NULL) +			continue; +		annotate_sym(he);  		/* -		 * assume we lost track of the stream, check alignment, and -		 * increment a single u64 in the hope to catch on again 'soon'. +		 * Since we have a hist_entry per IP for the same symbol, free +		 * he->sym->hist to signal we already processed this symbol.  		 */ - -		if (unlikely(head & 7)) -			head &= ~7ULL; - -		size = 8; +		free(priv->hist); +		priv->hist = NULL;  	} +} -	head += size; +static struct perf_event_ops event_ops = { +	.sample	= process_sample_event, +	.mmap	= event__process_mmap, +	.comm	= event__process_comm, +	.fork	= event__process_task, +}; -	if (offset + head < (unsigned long)input_stat.st_size) -		goto more; +static int __cmd_annotate(void) +{ +	int ret; +	struct perf_session *session; -	rc = EXIT_SUCCESS; -	close(input); +	session = perf_session__new(input_name, O_RDONLY, force); +	if (session == NULL) +		return -ENOMEM; -	dump_printf("      IP events: %10ld\n", total); -	dump_printf("    mmap events: %10ld\n", total_mmap); -	dump_printf("    comm events: %10ld\n", total_comm); -	dump_printf("    fork events: %10ld\n", total_fork); -	dump_printf(" unknown events: %10ld\n", total_unknown); +	ret = perf_session__process_events(session, &event_ops); +	if (ret) +		goto out_delete; -	if (dump_trace) -		return 0; +	if (dump_trace) { +		event__print_totals(); +		goto out_delete; +	} -	if (verbose >= 3) -		threads__fprintf(stdout, &threads); +	if (verbose > 3) +		perf_session__fprintf(session, stdout); -	if (verbose >= 2) +	if (verbose > 2)  		dsos__fprintf(stdout); -	collapse__resort(); -	output__resort(); +	perf_session__collapse_resort(&session->hists); +	perf_session__output_resort(&session->hists, session->event_total[0]); +	perf_session__find_annotations(session); +out_delete: +	perf_session__delete(session); -	find_annotations(); - -	return rc; +	return ret;  }  static const char * const annotate_usage[] = { @@ -1088,8 +588,9 @@ static const struct option options[] = {  		    "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_name, "file", "vmlinux pathname"), -	OPT_BOOLEAN('m', "modules", &modules, +	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,  		    "print matching source lines (may be slow)"), @@ -1098,30 +599,17 @@ static const struct option options[] = {  	OPT_END()  }; -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(annotate_usage, options); -		} -	} - -	free(str); -} -  int cmd_annotate(int argc, const char **argv, const char *prefix __used)  { -	symbol__init(); +	argc = parse_options(argc, argv, options, annotate_usage, 0); -	page_size = getpagesize(); +	symbol_conf.priv_size = sizeof(struct sym_priv); +	symbol_conf.try_vmlinux_path = true; -	argc = parse_options(argc, argv, options, annotate_usage, 0); +	if (symbol__init() < 0) +		return -1; -	setup_sorting(); +	setup_sorting(annotate_usage, options);  	if (argc) {  		/* @@ -1134,10 +622,12 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used)  		sym_hist_filter = argv[0];  	} -	if (!sym_hist_filter) -		usage_with_options(annotate_usage, options); -  	setup_pager(); +	if (field_sep && *field_sep == '.') { +		pr_err("'.' is the only non valid --field-separator argument\n"); +		return -1; +	} +  	return __cmd_annotate();  } | 
