aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util/annotate.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/annotate.c')
-rw-r--r--tools/perf/util/annotate.c269
1 files changed, 214 insertions, 55 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 9b70ab110ce79f..ac002d907d8180 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, &notes->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;