diff options
Diffstat (limited to 'tools/perf/util/annotate.c')
| -rw-r--r-- | tools/perf/util/annotate.c | 269 | 
1 files changed, 214 insertions, 55 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 9b70ab110ce7..ac002d907d81 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -37,6 +37,8 @@  #include "util/sharded_mutex.h"  #include "arch/common.h"  #include "namespaces.h" +#include "thread.h" +#include "hashmap.h"  #include <regex.h>  #include <linux/bitops.h>  #include <linux/kernel.h> @@ -107,6 +109,14 @@ static struct ins_ops ret_ops;  struct annotated_data_stat ann_data_stat;  LIST_HEAD(ann_insn_stat); +/* Pseudo data types */ +struct annotated_data_type stackop_type = { +	.self = { +		.type_name = (char *)"(stack operation)", +		.children = LIST_HEAD_INIT(stackop_type.self.children), +	}, +}; +  static int arch__grow_instructions(struct arch *arch)  {  	struct ins *new_instructions; @@ -854,6 +864,17 @@ bool arch__is(struct arch *arch, const char *name)  	return !strcmp(arch->name, name);  } +/* symbol histogram: key = offset << 16 | evsel->core.idx */ +static size_t sym_hist_hash(long key, void *ctx __maybe_unused) +{ +	return (key >> 16) + (key & 0xffff); +} + +static bool sym_hist_equal(long key1, long key2, void *ctx __maybe_unused) +{ +	return key1 == key2; +} +  static struct annotated_source *annotated_source__new(void)  {  	struct annotated_source *src = zalloc(sizeof(*src)); @@ -868,38 +889,25 @@ static __maybe_unused void annotated_source__delete(struct annotated_source *src  {  	if (src == NULL)  		return; + +	hashmap__free(src->samples);  	zfree(&src->histograms);  	free(src);  }  static int annotated_source__alloc_histograms(struct annotated_source *src, -					      size_t size, int nr_hists) +					      int nr_hists)  { -	size_t sizeof_sym_hist; - -	/* -	 * Add buffer of one element for zero length symbol. -	 * When sample is taken from first instruction of -	 * zero length symbol, perf still resolves it and -	 * shows symbol name in perf report and allows to -	 * annotate it. -	 */ -	if (size == 0) -		size = 1; +	src->nr_histograms   = nr_hists; +	src->histograms	     = calloc(nr_hists, sizeof(*src->histograms)); -	/* Check for overflow when calculating sizeof_sym_hist */ -	if (size > (SIZE_MAX - sizeof(struct sym_hist)) / sizeof(struct sym_hist_entry)) +	if (src->histograms == NULL)  		return -1; -	sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(struct sym_hist_entry)); - -	/* Check for overflow in zalloc argument */ -	if (sizeof_sym_hist > SIZE_MAX / nr_hists) -		return -1; +	src->samples = hashmap__new(sym_hist_hash, sym_hist_equal, NULL); +	if (src->samples == NULL) +		zfree(&src->histograms); -	src->sizeof_sym_hist = sizeof_sym_hist; -	src->nr_histograms   = nr_hists; -	src->histograms	     = calloc(nr_hists, sizeof_sym_hist) ;  	return src->histograms ? 0 : -1;  } @@ -910,7 +918,8 @@ void symbol__annotate_zero_histograms(struct symbol *sym)  	annotation__lock(notes);  	if (notes->src != NULL) {  		memset(notes->src->histograms, 0, -		       notes->src->nr_histograms * notes->src->sizeof_sym_hist); +		       notes->src->nr_histograms * sizeof(*notes->src->histograms)); +		hashmap__clear(notes->src->samples);  	}  	if (notes->branch && notes->branch->cycles_hist) {  		memset(notes->branch->cycles_hist, 0, @@ -974,8 +983,10 @@ static int __symbol__inc_addr_samples(struct map_symbol *ms,  				      struct perf_sample *sample)  {  	struct symbol *sym = ms->sym; -	unsigned offset; +	long hash_key; +	u64 offset;  	struct sym_hist *h; +	struct sym_hist_entry *entry;  	pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map__unmap_ip(ms->map, addr)); @@ -993,15 +1004,26 @@ static int __symbol__inc_addr_samples(struct map_symbol *ms,  			 __func__, __LINE__, sym->name, sym->start, addr, sym->end, sym->type == STT_FUNC);  		return -ENOMEM;  	} + +	hash_key = offset << 16 | evidx; +	if (!hashmap__find(src->samples, hash_key, &entry)) { +		entry = zalloc(sizeof(*entry)); +		if (entry == NULL) +			return -ENOMEM; + +		if (hashmap__add(src->samples, hash_key, entry) < 0) +			return -ENOMEM; +	} +  	h->nr_samples++; -	h->addr[offset].nr_samples++;  	h->period += sample->period; -	h->addr[offset].period += sample->period; +	entry->nr_samples++; +	entry->period += sample->period;  	pr_debug3("%#" PRIx64 " %s: period++ [addr: %#" PRIx64 ", %#" PRIx64  		  ", evidx=%d] => nr_samples: %" PRIu64 ", period: %" PRIu64 "\n",  		  sym->start, sym->name, addr, addr - sym->start, evidx, -		  h->addr[offset].nr_samples, h->addr[offset].period); +		  entry->nr_samples, entry->period);  	return 0;  } @@ -1047,8 +1069,7 @@ struct annotated_source *symbol__hists(struct symbol *sym, int nr_hists)  	if (notes->src->histograms == NULL) {  alloc_histograms: -		annotated_source__alloc_histograms(notes->src, symbol__size(sym), -						   nr_hists); +		annotated_source__alloc_histograms(notes->src, nr_hists);  	}  	return notes->src; @@ -2321,17 +2342,25 @@ out_remove_tmp:  	return err;  } -static void calc_percent(struct sym_hist *sym_hist, -			 struct hists *hists, +static void calc_percent(struct annotation *notes, +			 struct evsel *evsel,  			 struct annotation_data *data,  			 s64 offset, s64 end)  { +	struct hists *hists = evsel__hists(evsel); +	int evidx = evsel->core.idx; +	struct sym_hist *sym_hist = annotation__histogram(notes, evidx);  	unsigned int hits = 0;  	u64 period = 0;  	while (offset < end) { -		hits   += sym_hist->addr[offset].nr_samples; -		period += sym_hist->addr[offset].period; +		struct sym_hist_entry *entry; + +		entry = annotated_source__hist_entry(notes->src, evidx, offset); +		if (entry) { +			hits   += entry->nr_samples; +			period += entry->period; +		}  		++offset;  	} @@ -2368,16 +2397,13 @@ static void annotation__calc_percent(struct annotation *notes,  		end  = next ? next->offset : len;  		for_each_group_evsel(evsel, leader) { -			struct hists *hists = evsel__hists(evsel);  			struct annotation_data *data; -			struct sym_hist *sym_hist;  			BUG_ON(i >= al->data_nr); -			sym_hist = annotation__histogram(notes, evsel->core.idx);  			data = &al->data[i++]; -			calc_percent(sym_hist, hists, data, al->offset, end); +			calc_percent(notes, evsel, data, al->offset, end);  		}  	}  } @@ -2572,14 +2598,19 @@ static void print_summary(struct rb_root *root, const char *filename)  static void symbol__annotate_hits(struct symbol *sym, struct evsel *evsel)  { +	int evidx = evsel->core.idx;  	struct annotation *notes = symbol__annotation(sym); -	struct sym_hist *h = annotation__histogram(notes, evsel->core.idx); +	struct sym_hist *h = annotation__histogram(notes, evidx);  	u64 len = symbol__size(sym), offset; -	for (offset = 0; offset < len; ++offset) -		if (h->addr[offset].nr_samples != 0) +	for (offset = 0; offset < len; ++offset) { +		struct sym_hist_entry *entry; + +		entry = annotated_source__hist_entry(notes->src, evidx, offset); +		if (entry && entry->nr_samples != 0)  			printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2, -			       sym->start + offset, h->addr[offset].nr_samples); +			       sym->start + offset, entry->nr_samples); +	}  	printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->nr_samples", h->nr_samples);  } @@ -2797,7 +2828,7 @@ 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->src->sizeof_sym_hist); +	memset(h, 0, sizeof(*notes->src->histograms) * notes->src->nr_histograms);  }  void symbol__annotate_decay_histogram(struct symbol *sym, int evidx) @@ -2808,8 +2839,14 @@ void symbol__annotate_decay_histogram(struct symbol *sym, int evidx)  	h->nr_samples = 0;  	for (offset = 0; offset < len; ++offset) { -		h->addr[offset].nr_samples = h->addr[offset].nr_samples * 7 / 8; -		h->nr_samples += h->addr[offset].nr_samples; +		struct sym_hist_entry *entry; + +		entry = annotated_source__hist_entry(notes->src, evidx, offset); +		if (entry == NULL) +			continue; + +		entry->nr_samples = entry->nr_samples * 7 / 8; +		h->nr_samples += entry->nr_samples;  	}  } @@ -3563,8 +3600,22 @@ static int extract_reg_offset(struct arch *arch, const char *str,  	if (regname == NULL)  		return -1; -	op_loc->reg = get_dwarf_regnum(regname, 0); +	op_loc->reg1 = get_dwarf_regnum(regname, 0);  	free(regname); + +	/* Get the second register */ +	if (op_loc->multi_regs) { +		p = strchr(p + 1, arch->objdump.register_char); +		if (p == NULL) +			return -1; + +		regname = strdup(p); +		if (regname == NULL) +			return -1; + +		op_loc->reg2 = get_dwarf_regnum(regname, 0); +		free(regname); +	}  	return 0;  } @@ -3577,14 +3628,20 @@ static int extract_reg_offset(struct arch *arch, const char *str,   * Get detailed location info (register and offset) in the instruction.   * It needs both source and target operand and whether it accesses a   * memory location.  The offset field is meaningful only when the - * corresponding mem flag is set. + * corresponding mem flag is set.  The reg2 field is meaningful only + * when multi_regs flag is set.   *   * Some examples on x86:   * - *   mov  (%rax), %rcx   # src_reg = rax, src_mem = 1, src_offset = 0 - *                       # dst_reg = rcx, dst_mem = 0 + *   mov  (%rax), %rcx   # src_reg1 = rax, src_mem = 1, src_offset = 0 + *                       # dst_reg1 = rcx, dst_mem = 0 + * + *   mov  0x18, %r8      # src_reg1 = -1, src_mem = 0 + *                       # dst_reg1 = r8, dst_mem = 0   * - *   mov  0x18, %r8      # src_reg = -1, dst_reg = r8 + *   mov  %rsi, 8(%rbx,%rcx,4)  # src_reg1 = rsi, src_mem = 0, dst_multi_regs = 0 + *                              # dst_reg1 = rbx, dst_reg2 = rcx, dst_mem = 1 + *                              # dst_multi_regs = 1, dst_offset = 8   */  int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl,  			       struct annotated_insn_loc *loc) @@ -3605,24 +3662,29 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl,  	for_each_insn_op_loc(loc, i, op_loc) {  		const char *insn_str = ops->source.raw; +		bool multi_regs = ops->source.multi_regs; -		if (i == INSN_OP_TARGET) +		if (i == INSN_OP_TARGET) {  			insn_str = ops->target.raw; +			multi_regs = ops->target.multi_regs; +		}  		/* Invalidate the register by default */ -		op_loc->reg = -1; +		op_loc->reg1 = -1; +		op_loc->reg2 = -1;  		if (insn_str == NULL)  			continue;  		if (strchr(insn_str, arch->objdump.memory_ref_char)) {  			op_loc->mem_ref = true; +			op_loc->multi_regs = multi_regs;  			extract_reg_offset(arch, insn_str, op_loc);  		} else {  			char *s = strdup(insn_str);  			if (s) { -				op_loc->reg = get_dwarf_regnum(s, 0); +				op_loc->reg1 = get_dwarf_regnum(s, 0);  				free(s);  			}  		} @@ -3660,8 +3722,17 @@ static struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip)  	notes = symbol__annotation(sym);  	list_for_each_entry(dl, ¬es->src->source, al.node) { -		if (sym->start + dl->al.offset == ip) +		if (sym->start + dl->al.offset == ip) { +			/* +			 * llvm-objdump places "lock" in a separate line and +			 * in that case, we want to get the next line. +			 */ +			if (!strcmp(dl->ins.name, "lock") && *dl->ops.raw == '\0') { +				ip++; +				continue; +			}  			return dl; +		}  	}  	return NULL;  } @@ -3690,6 +3761,42 @@ static struct annotated_item_stat *annotate_data_stat(struct list_head *head,  	return istat;  } +static bool is_stack_operation(struct arch *arch, struct disasm_line *dl) +{ +	if (arch__is(arch, "x86")) { +		if (!strncmp(dl->ins.name, "push", 4) || +		    !strncmp(dl->ins.name, "pop", 3) || +		    !strncmp(dl->ins.name, "ret", 3)) +			return true; +	} + +	return false; +} + +u64 annotate_calc_pcrel(struct map_symbol *ms, u64 ip, int offset, +			struct disasm_line *dl) +{ +	struct annotation *notes; +	struct disasm_line *next; +	u64 addr; + +	notes = symbol__annotation(ms->sym); +	/* +	 * PC-relative addressing starts from the next instruction address +	 * But the IP is for the current instruction.  Since disasm_line +	 * doesn't have the instruction size, calculate it using the next +	 * disasm_line.  If it's the last one, we can use symbol's end +	 * address directly. +	 */ +	if (&dl->al.node == notes->src->source.prev) +		addr = ms->sym->end + offset; +	else { +		next = list_next_entry(dl, al.node); +		addr = ip + (next->al.offset - dl->al.offset) + offset; +	} +	return map__rip_2objdump(ms->map, addr); +} +  /**   * hist_entry__get_data_type - find data type for given hist entry   * @he: hist entry @@ -3709,7 +3816,9 @@ struct annotated_data_type *hist_entry__get_data_type(struct hist_entry *he)  	struct annotated_op_loc *op_loc;  	struct annotated_data_type *mem_type;  	struct annotated_item_stat *istat; -	u64 ip = he->ip; +	u64 ip = he->ip, addr = 0; +	const char *var_name = NULL; +	int var_offset;  	int i;  	ann_data_stat.total++; @@ -3742,6 +3851,7 @@ struct annotated_data_type *hist_entry__get_data_type(struct hist_entry *he)  		return NULL;  	} +retry:  	istat = annotate_data_stat(&ann_insn_stat, dl->ins.name);  	if (istat == NULL) {  		ann_data_stat.no_insn++; @@ -3754,16 +3864,51 @@ struct annotated_data_type *hist_entry__get_data_type(struct hist_entry *he)  		return NULL;  	} +	if (is_stack_operation(arch, dl)) { +		istat->good++; +		he->mem_type_off = 0; +		return &stackop_type; +	} +  	for_each_insn_op_loc(&loc, i, op_loc) {  		if (!op_loc->mem_ref)  			continue; -		mem_type = find_data_type(ms, ip, op_loc->reg, op_loc->offset); +		/* Recalculate IP because of LOCK prefix or insn fusion */ +		ip = ms->sym->start + dl->al.offset; + +		var_offset = op_loc->offset; + +		/* PC-relative addressing */ +		if (op_loc->reg1 == DWARF_REG_PC) { +			struct addr_location al; +			struct symbol *var; +			u64 map_addr; + +			addr = annotate_calc_pcrel(ms, ip, op_loc->offset, dl); +			/* Kernel symbols might be relocated */ +			map_addr = addr + map__reloc(ms->map); + +			addr_location__init(&al); +			var = thread__find_symbol_fb(he->thread, he->cpumode, +						     map_addr, &al); +			if (var) { +				var_name = var->name; +				/* Calculate type offset from the start of variable */ +				var_offset = map_addr - map__unmap_ip(al.map, var->start); +			} +			addr_location__exit(&al); +		} + +		mem_type = find_data_type(ms, ip, op_loc, addr, var_name);  		if (mem_type)  			istat->good++;  		else  			istat->bad++; +		if (mem_type && var_name) +			op_loc->offset = var_offset; +  		if (symbol_conf.annotate_data_sample) {  			annotated_data_type__update_samples(mem_type, evsel,  							    op_loc->offset, @@ -3774,6 +3919,20 @@ struct annotated_data_type *hist_entry__get_data_type(struct hist_entry *he)  		return mem_type;  	} +	/* +	 * Some instructions can be fused and the actual memory access came +	 * from the previous instruction. +	 */ +	if (dl->al.offset > 0) { +		struct disasm_line *prev_dl; + +		prev_dl = list_prev_entry(dl, al.node); +		if (ins__is_fused(arch, prev_dl->ins.name, dl->ins.name)) { +			dl = prev_dl; +			goto retry; +		} +	} +  	ann_data_stat.no_mem_ops++;  	istat->bad++;  	return NULL;  | 
