summaryrefslogtreecommitdiff
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.c873
1 files changed, 698 insertions, 175 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 1451caf25e77..cc7764455faf 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -25,6 +25,7 @@
#include "srcline.h"
#include "units.h"
#include "debug.h"
+#include "debuginfo.h"
#include "annotate.h"
#include "annotate-data.h"
#include "evsel.h"
@@ -40,6 +41,7 @@
#include "namespaces.h"
#include "thread.h"
#include "hashmap.h"
+#include "strbuf.h"
#include <regex.h>
#include <linux/bitops.h>
#include <linux/kernel.h>
@@ -47,6 +49,7 @@
#include <linux/zalloc.h>
#include <subcmd/parse-options.h>
#include <subcmd/run-command.h>
+#include <math.h>
/* FIXME: For the HE_COLORSET */
#include "ui/browser.h"
@@ -84,6 +87,8 @@ struct annotated_data_type canary_type = {
},
};
+#define NO_TYPE ((struct annotated_data_type *)-1UL)
+
/* symbol histogram: key = offset << 16 | evsel->core.idx */
static size_t sym_hist_hash(long key, void *ctx __maybe_unused)
{
@@ -206,7 +211,7 @@ static int __symbol__account_cycles(struct cyc_hist *ch,
}
static int __symbol__inc_addr_samples(struct map_symbol *ms,
- struct annotated_source *src, int evidx, u64 addr,
+ struct annotated_source *src, struct evsel *evsel, u64 addr,
struct perf_sample *sample)
{
struct symbol *sym = ms->sym;
@@ -225,14 +230,14 @@ static int __symbol__inc_addr_samples(struct map_symbol *ms,
}
offset = addr - sym->start;
- h = annotated_source__histogram(src, evidx);
+ h = annotated_source__histogram(src, evsel);
if (h == NULL) {
pr_debug("%s(%d): ENOMEM! sym->name=%s, start=%#" PRIx64 ", addr=%#" PRIx64 ", end=%#" PRIx64 ", func: %d\n",
__func__, __LINE__, sym->name, sym->start, addr, sym->end, sym->type == STT_FUNC);
return -ENOMEM;
}
- hash_key = offset << 16 | evidx;
+ hash_key = offset << 16 | evsel->core.idx;
if (!hashmap__find(src->samples, hash_key, &entry)) {
entry = zalloc(sizeof(*entry));
if (entry == NULL)
@@ -249,7 +254,7 @@ static int __symbol__inc_addr_samples(struct map_symbol *ms,
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,
+ sym->start, sym->name, addr, addr - sym->start, evsel->core.idx,
entry->nr_samples, entry->period);
return 0;
}
@@ -265,22 +270,30 @@ struct annotated_branch *annotation__get_branch(struct annotation *notes)
return notes->branch;
}
-static struct cyc_hist *symbol__cycles_hist(struct symbol *sym)
+static struct annotated_branch *symbol__find_branch_hist(struct symbol *sym,
+ unsigned int br_cntr_nr)
{
struct annotation *notes = symbol__annotation(sym);
struct annotated_branch *branch;
+ const size_t size = symbol__size(sym);
branch = annotation__get_branch(notes);
if (branch == NULL)
return NULL;
if (branch->cycles_hist == NULL) {
- const size_t size = symbol__size(sym);
-
branch->cycles_hist = calloc(size, sizeof(struct cyc_hist));
+ if (!branch->cycles_hist)
+ return NULL;
+ }
+
+ if (br_cntr_nr && branch->br_cntr == NULL) {
+ branch->br_cntr = calloc(br_cntr_nr * size, sizeof(u64));
+ if (!branch->br_cntr)
+ return NULL;
}
- return branch->cycles_hist;
+ return branch;
}
struct annotated_source *symbol__hists(struct symbol *sym, int nr_hists)
@@ -312,19 +325,48 @@ static int symbol__inc_addr_samples(struct map_symbol *ms,
if (sym == NULL)
return 0;
src = symbol__hists(sym, evsel->evlist->core.nr_entries);
- return src ? __symbol__inc_addr_samples(ms, src, evsel->core.idx, addr, sample) : 0;
+ return src ? __symbol__inc_addr_samples(ms, src, evsel, addr, sample) : 0;
+}
+
+static int symbol__account_br_cntr(struct annotated_branch *branch,
+ struct evsel *evsel,
+ unsigned offset,
+ u64 br_cntr)
+{
+ unsigned int br_cntr_nr = evsel__leader(evsel)->br_cntr_nr;
+ unsigned int base = evsel__leader(evsel)->br_cntr_idx;
+ unsigned int off = offset * evsel->evlist->nr_br_cntr;
+ u64 *branch_br_cntr = branch->br_cntr;
+ unsigned int i, mask, width;
+
+ if (!br_cntr || !branch_br_cntr)
+ return 0;
+
+ perf_env__find_br_cntr_info(evsel__env(evsel), NULL, &width);
+ mask = (1L << width) - 1;
+ for (i = 0; i < br_cntr_nr; i++) {
+ u64 cntr = (br_cntr >> i * width) & mask;
+
+ branch_br_cntr[off + i + base] += cntr;
+ if (cntr == mask)
+ branch_br_cntr[off + i + base] |= ANNOTATION__BR_CNTR_SATURATED_FLAG;
+ }
+
+ return 0;
}
-static int symbol__account_cycles(u64 addr, u64 start,
- struct symbol *sym, unsigned cycles)
+static int symbol__account_cycles(u64 addr, u64 start, struct symbol *sym,
+ unsigned cycles, struct evsel *evsel,
+ u64 br_cntr)
{
- struct cyc_hist *cycles_hist;
+ struct annotated_branch *branch;
unsigned offset;
+ int ret;
if (sym == NULL)
return 0;
- cycles_hist = symbol__cycles_hist(sym);
- if (cycles_hist == NULL)
+ branch = symbol__find_branch_hist(sym, evsel->evlist->nr_br_cntr);
+ if (!branch)
return -ENOMEM;
if (addr < sym->start || addr >= sym->end)
return -ERANGE;
@@ -336,15 +378,22 @@ static int symbol__account_cycles(u64 addr, u64 start,
start = 0;
}
offset = addr - sym->start;
- return __symbol__account_cycles(cycles_hist,
+ ret = __symbol__account_cycles(branch->cycles_hist,
start ? start - sym->start : 0,
offset, cycles,
!!start);
+
+ if (ret)
+ return ret;
+
+ return symbol__account_br_cntr(branch, evsel, offset, br_cntr);
}
int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,
struct addr_map_symbol *start,
- unsigned cycles)
+ unsigned cycles,
+ struct evsel *evsel,
+ u64 br_cntr)
{
u64 saddr = 0;
int err;
@@ -370,7 +419,7 @@ int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,
start ? start->addr : 0,
ams->ms.sym ? ams->ms.sym->start + map__start(ams->ms.map) : 0,
saddr);
- err = symbol__account_cycles(ams->al_addr, saddr, ams->ms.sym, cycles);
+ err = symbol__account_cycles(ams->al_addr, saddr, ams->ms.sym, cycles, evsel, br_cntr);
if (err)
pr_debug2("account_cycles failed %d\n", err);
return err;
@@ -411,6 +460,7 @@ static void annotated_branch__delete(struct annotated_branch *branch)
{
if (branch) {
zfree(&branch->cycles_hist);
+ free(branch->br_cntr);
free(branch);
}
}
@@ -454,8 +504,10 @@ static void annotation__count_and_fill(struct annotation *notes, u64 start, u64
}
}
-static int annotation__compute_ipc(struct annotation *notes, size_t size)
+static int annotation__compute_ipc(struct annotation *notes, size_t size,
+ struct evsel *evsel)
{
+ unsigned int br_cntr_nr = evsel->evlist->nr_br_cntr;
int err = 0;
s64 offset;
@@ -490,6 +542,20 @@ static int annotation__compute_ipc(struct annotation *notes, size_t size)
al->cycles->max = ch->cycles_max;
al->cycles->min = ch->cycles_min;
}
+ if (al && notes->branch->br_cntr) {
+ if (!al->br_cntr) {
+ al->br_cntr = calloc(br_cntr_nr, sizeof(u64));
+ if (!al->br_cntr) {
+ err = ENOMEM;
+ break;
+ }
+ }
+ al->num_aggr = ch->num_aggr;
+ al->br_cntr_nr = br_cntr_nr;
+ al->evsel = evsel;
+ memcpy(al->br_cntr, &notes->branch->br_cntr[offset * br_cntr_nr],
+ br_cntr_nr * sizeof(u64));
+ }
}
}
@@ -501,8 +567,10 @@ static int annotation__compute_ipc(struct annotation *notes, size_t size)
struct annotation_line *al;
al = annotated_source__get_line(notes->src, offset);
- if (al)
+ if (al) {
zfree(&al->cycles);
+ zfree(&al->br_cntr);
+ }
}
}
}
@@ -692,20 +760,38 @@ static int disasm_line__print(struct disasm_line *dl, u64 start, int addr_fmt_wi
return 0;
}
+static struct annotated_data_type *
+__hist_entry__get_data_type(struct hist_entry *he, struct arch *arch,
+ struct debuginfo *dbg, struct disasm_line *dl,
+ int *type_offset);
+
+static bool needs_type_info(struct annotated_data_type *data_type)
+{
+ if (data_type == NULL || data_type == NO_TYPE)
+ return false;
+
+ if (verbose)
+ return true;
+
+ return (data_type != &stackop_type) && (data_type != &canary_type);
+}
+
static int
-annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start,
- struct evsel *evsel, u64 len, int min_pcnt, int printed,
- int max_lines, struct annotation_line *queue, int addr_fmt_width,
- int percent_type)
+annotation_line__print(struct annotation_line *al, struct annotation_print_data *apd,
+ struct annotation_options *opts, int printed,
+ struct annotation_line *queue)
{
+ struct symbol *sym = apd->he->ms.sym;
struct disasm_line *dl = container_of(al, struct disasm_line, al);
+ struct annotation *notes = symbol__annotation(sym);
static const char *prev_line;
+ int max_lines = opts->max_lines;
+ int percent_type = opts->percent_type;
if (al->offset != -1) {
double max_percent = 0.0;
int i, nr_percent = 1;
const char *color;
- struct annotation *notes = symbol__annotation(sym);
for (i = 0; i < al->data_nr; i++) {
double percent;
@@ -720,19 +806,23 @@ annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start
if (al->data_nr > nr_percent)
nr_percent = al->data_nr;
- if (max_percent < min_pcnt)
+ if (max_percent < opts->min_pcnt)
return -1;
if (max_lines && printed >= max_lines)
return 1;
if (queue != NULL) {
+ struct annotation_options queue_opts = {
+ .max_lines = 1,
+ .percent_type = percent_type,
+ };
+
list_for_each_entry_from(queue, &notes->src->source, node) {
if (queue == al)
break;
- annotation_line__print(queue, sym, start, evsel, len,
- 0, 0, 1, NULL, addr_fmt_width,
- percent_type);
+ annotation_line__print(queue, apd, &queue_opts,
+ /*printed=*/0, /*queue=*/NULL);
}
}
@@ -757,7 +847,31 @@ annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start
printf(" : ");
- disasm_line__print(dl, start, addr_fmt_width);
+ disasm_line__print(dl, notes->src->start, apd->addr_fmt_width);
+
+ if (opts->code_with_type && apd->dbg) {
+ struct annotated_data_type *data_type;
+ int offset = 0;
+
+ data_type = __hist_entry__get_data_type(apd->he, apd->arch,
+ apd->dbg, dl, &offset);
+ if (needs_type_info(data_type)) {
+ char buf[4096];
+
+ printf("\t\t# data-type: %s",
+ data_type->self.type_name);
+
+ if (data_type != &stackop_type &&
+ data_type != &canary_type)
+ printf(" +%#x", offset);
+
+ if (annotated_data_type__get_member_name(data_type,
+ buf,
+ sizeof(buf),
+ offset))
+ printf(" (%s)", buf);
+ }
+ }
/*
* Also color the filename and line if needed, with
@@ -775,18 +889,16 @@ annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start
} else if (max_lines && printed >= max_lines)
return 1;
else {
- int width = symbol_conf.show_total_period ? 12 : 8;
+ int width = annotation__pcnt_width(notes);
if (queue)
return -1;
- if (evsel__is_group_event(evsel))
- width *= evsel->core.nr_members;
-
if (!*al->line)
printf(" %*s:\n", width, " ");
else
- printf(" %*s: %-*d %s\n", width, " ", addr_fmt_width, al->line_nr, al->line);
+ printf(" %*s: %-*d %s\n", width, " ", apd->addr_fmt_width,
+ al->line_nr, al->line);
}
return 0;
@@ -798,15 +910,14 @@ static void calc_percent(struct annotation *notes,
s64 offset, s64 end)
{
struct hists *hists = evsel__hists(evsel);
- int evidx = evsel->core.idx;
- struct sym_hist *sym_hist = annotation__histogram(notes, evidx);
+ struct sym_hist *sym_hist = annotation__histogram(notes, evsel);
unsigned int hits = 0;
u64 period = 0;
while (offset < end) {
struct sym_hist_entry *entry;
- entry = annotated_source__hist_entry(notes->src, evidx, offset);
+ entry = annotated_source__hist_entry(notes->src, evsel, offset);
if (entry) {
hits += entry->nr_samples;
period += entry->period;
@@ -851,6 +962,10 @@ static void annotation__calc_percent(struct annotation *notes,
BUG_ON(i >= al->data_nr);
+ if (symbol_conf.skip_empty &&
+ evsel__hists(evsel)->stats.nr_samples == 0)
+ continue;
+
data = &al->data[i++];
calc_percent(notes, evsel, data, al->offset, end);
@@ -865,7 +980,7 @@ void symbol__calc_percent(struct symbol *sym, struct evsel *evsel)
annotation__calc_percent(notes, evsel, symbol__size(sym));
}
-static int evsel__get_arch(struct evsel *evsel, struct arch **parch)
+int evsel__get_arch(struct evsel *evsel, struct arch **parch)
{
struct perf_env *env = evsel__env(evsel);
const char *arch_name = perf_env__arch(env);
@@ -900,14 +1015,13 @@ int symbol__annotate(struct map_symbol *ms, struct evsel *evsel,
struct symbol *sym = ms->sym;
struct annotation *notes = symbol__annotation(sym);
struct annotate_args args = {
- .evsel = evsel,
.options = &annotate_opts,
};
struct arch *arch = NULL;
- int err;
+ int err, nr;
err = evsel__get_arch(evsel, &arch);
- if (err < 0)
+ if (err)
return err;
if (parch)
@@ -925,6 +1039,19 @@ int symbol__annotate(struct map_symbol *ms, struct evsel *evsel,
return -1;
}
+ nr = 0;
+ if (evsel__is_group_event(evsel)) {
+ struct evsel *pos;
+
+ for_each_group_evsel(pos, evsel) {
+ if (symbol_conf.skip_empty &&
+ evsel__hists(pos)->stats.nr_samples == 0)
+ continue;
+ nr++;
+ }
+ }
+ notes->src->nr_events = nr ? nr : 1;
+
if (annotate_opts.full_addr)
notes->src->start = map__objdump_2mem(ms->map, ms->sym->start);
else
@@ -1060,15 +1187,14 @@ 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, evidx);
+ struct sym_hist *h = annotation__histogram(notes, evsel);
u64 len = symbol__size(sym), offset;
for (offset = 0; offset < len; ++offset) {
struct sym_hist_entry *entry;
- entry = annotated_source__hist_entry(notes->src, evidx, offset);
+ entry = annotated_source__hist_entry(notes->src, evsel, offset);
if (entry && entry->nr_samples != 0)
printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2,
sym->start + offset, entry->nr_samples);
@@ -1089,8 +1215,9 @@ static int annotated_source__addr_fmt_width(struct list_head *lines, u64 start)
return 0;
}
-int symbol__annotate_printf(struct map_symbol *ms, struct evsel *evsel)
+int hist_entry__annotate_printf(struct hist_entry *he, struct evsel *evsel)
{
+ struct map_symbol *ms = &he->ms;
struct map *map = ms->map;
struct symbol *sym = ms->sym;
struct dso *dso = map__dso(map);
@@ -1098,15 +1225,17 @@ int symbol__annotate_printf(struct map_symbol *ms, struct evsel *evsel)
const char *d_filename;
const char *evsel_name = evsel__name(evsel);
struct annotation *notes = symbol__annotation(sym);
- struct sym_hist *h = annotation__histogram(notes, evsel->core.idx);
+ struct sym_hist *h = annotation__histogram(notes, evsel);
struct annotation_line *pos, *queue = NULL;
struct annotation_options *opts = &annotate_opts;
- u64 start = map__rip_2objdump(map, sym->start);
- int printed = 2, queue_len = 0, addr_fmt_width;
+ struct annotation_print_data apd = {
+ .he = he,
+ .evsel = evsel,
+ };
+ int printed = 2, queue_len = 0;
int more = 0;
bool context = opts->context;
- u64 len;
- int width = symbol_conf.show_total_period ? 12 : 8;
+ int width = annotation__pcnt_width(notes);
int graph_dotted_len;
char buf[512];
@@ -1119,10 +1248,7 @@ int symbol__annotate_printf(struct map_symbol *ms, struct evsel *evsel)
else
d_filename = basename(filename);
- len = symbol__size(sym);
-
if (evsel__is_group_event(evsel)) {
- width *= evsel->core.nr_members;
evsel__group_desc(evsel, buf, sizeof(buf));
evsel_name = buf;
}
@@ -1140,7 +1266,10 @@ int symbol__annotate_printf(struct map_symbol *ms, struct evsel *evsel)
if (verbose > 0)
symbol__annotate_hits(sym, evsel);
- addr_fmt_width = annotated_source__addr_fmt_width(&notes->src->source, start);
+ apd.addr_fmt_width = annotated_source__addr_fmt_width(&notes->src->source,
+ notes->src->start);
+ evsel__get_arch(evsel, &apd.arch);
+ apd.dbg = dso__debuginfo(dso);
list_for_each_entry(pos, &notes->src->source, node) {
int err;
@@ -1150,9 +1279,7 @@ int symbol__annotate_printf(struct map_symbol *ms, struct evsel *evsel)
queue_len = 0;
}
- err = annotation_line__print(pos, sym, start, evsel, len,
- opts->min_pcnt, printed, opts->max_lines,
- queue, addr_fmt_width, opts->percent_type);
+ err = annotation_line__print(pos, &apd, opts, printed, queue);
switch (err) {
case 0:
@@ -1183,6 +1310,7 @@ int symbol__annotate_printf(struct map_symbol *ms, struct evsel *evsel)
}
}
+ debuginfo__delete(apd.dbg);
free(filename);
return more;
@@ -1229,7 +1357,8 @@ static void FILE__write_graph(void *fp, int graph)
fputs(s, fp);
}
-static int symbol__annotate_fprintf2(struct symbol *sym, FILE *fp)
+static int symbol__annotate_fprintf2(struct symbol *sym, FILE *fp,
+ struct annotation_print_data *apd)
{
struct annotation *notes = symbol__annotation(sym);
struct annotation_write_ops wops = {
@@ -1243,24 +1372,37 @@ static int symbol__annotate_fprintf2(struct symbol *sym, FILE *fp)
};
struct annotation_line *al;
+ if (annotate_opts.code_with_type) {
+ evsel__get_arch(apd->evsel, &apd->arch);
+ apd->dbg = dso__debuginfo(map__dso(apd->he->ms.map));
+ }
+
list_for_each_entry(al, &notes->src->source, node) {
if (annotation_line__filter(al))
continue;
- annotation_line__write(al, notes, &wops);
+ annotation_line__write(al, notes, &wops, apd);
fputc('\n', fp);
wops.first_line = false;
}
+ if (annotate_opts.code_with_type)
+ debuginfo__delete(apd->dbg);
+
return 0;
}
-int map_symbol__annotation_dump(struct map_symbol *ms, struct evsel *evsel)
+int map_symbol__annotation_dump(struct map_symbol *ms, struct evsel *evsel,
+ struct hist_entry *he)
{
const char *ev_name = evsel__name(evsel);
char buf[1024];
char *filename;
int err = -1;
FILE *fp;
+ struct annotation_print_data apd = {
+ .he = he,
+ .evsel = evsel,
+ };
if (asprintf(&filename, "%s.annotation", ms->sym->name) < 0)
return -1;
@@ -1276,7 +1418,7 @@ int map_symbol__annotation_dump(struct map_symbol *ms, struct evsel *evsel)
fprintf(fp, "%s() %s\nEvent: %s\n\n",
ms->sym->name, dso__long_name(map__dso(ms->map)), ev_name);
- symbol__annotate_fprintf2(ms->sym, fp);
+ symbol__annotate_fprintf2(ms->sym, fp, &apd);
fclose(fp);
err = 0;
@@ -1285,18 +1427,18 @@ out_free_filename:
return err;
}
-void symbol__annotate_zero_histogram(struct symbol *sym, int evidx)
+void symbol__annotate_zero_histogram(struct symbol *sym, struct evsel *evsel)
{
struct annotation *notes = symbol__annotation(sym);
- struct sym_hist *h = annotation__histogram(notes, evidx);
+ struct sym_hist *h = annotation__histogram(notes, evsel);
memset(h, 0, sizeof(*notes->src->histograms) * notes->src->nr_histograms);
}
-void symbol__annotate_decay_histogram(struct symbol *sym, int evidx)
+void symbol__annotate_decay_histogram(struct symbol *sym, struct evsel *evsel)
{
struct annotation *notes = symbol__annotation(sym);
- struct sym_hist *h = annotation__histogram(notes, evidx);
+ struct sym_hist *h = annotation__histogram(notes, evsel);
struct annotation_line *al;
h->nr_samples = 0;
@@ -1306,7 +1448,7 @@ void symbol__annotate_decay_histogram(struct symbol *sym, int evidx)
if (al->offset == -1)
continue;
- entry = annotated_source__hist_entry(notes->src, evidx, al->offset);
+ entry = annotated_source__hist_entry(notes->src, evsel, al->offset);
if (entry == NULL)
continue;
@@ -1323,6 +1465,7 @@ void annotated_source__purge(struct annotated_source *as)
list_del_init(&al->node);
disasm_line__free(disasm_line(al));
}
+ as->tried_source = false;
}
static size_t disasm_line__fprintf(struct disasm_line *dl, FILE *fp)
@@ -1520,12 +1663,17 @@ static void symbol__calc_lines(struct map_symbol *ms, struct rb_root *root)
annotation__calc_lines(notes, ms, root);
}
-int symbol__tty_annotate2(struct map_symbol *ms, struct evsel *evsel)
+int hist_entry__tty_annotate2(struct hist_entry *he, struct evsel *evsel)
{
+ struct map_symbol *ms = &he->ms;
struct dso *dso = map__dso(ms->map);
struct symbol *sym = ms->sym;
struct rb_root source_line = RB_ROOT;
struct hists *hists = evsel__hists(evsel);
+ struct annotation_print_data apd = {
+ .he = he,
+ .evsel = evsel,
+ };
char buf[1024];
int err;
@@ -1548,15 +1696,16 @@ int symbol__tty_annotate2(struct map_symbol *ms, struct evsel *evsel)
hists__scnprintf_title(hists, buf, sizeof(buf));
fprintf(stdout, "%s, [percent: %s]\n%s() %s\n",
buf, percent_type_str(annotate_opts.percent_type), sym->name, dso__long_name(dso));
- symbol__annotate_fprintf2(sym, stdout);
+ symbol__annotate_fprintf2(sym, stdout, &apd);
annotated_source__purge(symbol__annotation(sym)->src);
return 0;
}
-int symbol__tty_annotate(struct map_symbol *ms, struct evsel *evsel)
+int hist_entry__tty_annotate(struct hist_entry *he, struct evsel *evsel)
{
+ struct map_symbol *ms = &he->ms;
struct dso *dso = map__dso(ms->map);
struct symbol *sym = ms->sym;
struct rb_root source_line = RB_ROOT;
@@ -1580,7 +1729,7 @@ int symbol__tty_annotate(struct map_symbol *ms, struct evsel *evsel)
print_summary(&source_line, dso__long_name(dso));
}
- symbol__annotate_printf(ms, evsel);
+ hist_entry__annotate_printf(he, evsel);
annotated_source__purge(symbol__annotation(sym)->src);
@@ -1594,13 +1743,12 @@ bool ui__has_annotation(void)
static double annotation_line__max_percent(struct annotation_line *al,
- struct annotation *notes,
unsigned int percent_type)
{
double percent_max = 0.0;
int i;
- for (i = 0; i < notes->src->nr_events; i++) {
+ for (i = 0; i < al->data_nr; i++) {
double percent;
percent = annotation_data__percent(&al->data[i],
@@ -1613,7 +1761,7 @@ static double annotation_line__max_percent(struct annotation_line *al,
return percent_max;
}
-static void disasm_line__write(struct disasm_line *dl, struct annotation *notes,
+static int disasm_line__write(struct disasm_line *dl, struct annotation *notes,
void *obj, char *bf, size_t size,
void (*obj__printf)(void *obj, const char *fmt, ...),
void (*obj__write_graph)(void *obj, int graph))
@@ -1641,8 +1789,8 @@ call_like:
obj__printf(obj, " ");
}
- disasm_line__scnprintf(dl, bf, size, !annotate_opts.use_offset,
- notes->src->widths.max_ins_name);
+ return disasm_line__scnprintf(dl, bf, size, !annotate_opts.use_offset,
+ notes->src->widths.max_ins_name) + 2;
}
static void ipc_coverage_string(char *bf, int size, struct annotation *notes)
@@ -1662,24 +1810,225 @@ static void ipc_coverage_string(char *bf, int size, struct annotation *notes)
ipc, coverage);
}
-static void __annotation_line__write(struct annotation_line *al, struct annotation *notes,
- bool first_line, bool current_entry, bool change_color, int width,
- void *obj, unsigned int percent_type,
- int (*obj__set_color)(void *obj, int color),
- void (*obj__set_percent_color)(void *obj, double percent, bool current),
- int (*obj__set_jumps_percent_color)(void *obj, int nr, bool current),
- void (*obj__printf)(void *obj, const char *fmt, ...),
- void (*obj__write_graph)(void *obj, int graph))
+int annotation_br_cntr_abbr_list(char **str, struct evsel *evsel, bool header)
+{
+ struct evsel *pos;
+ struct strbuf sb;
+
+ if (evsel->evlist->nr_br_cntr <= 0)
+ return -ENOTSUP;
+
+ strbuf_init(&sb, /*hint=*/ 0);
+
+ if (header && strbuf_addf(&sb, "# Branch counter abbr list:\n"))
+ goto err;
+
+ evlist__for_each_entry(evsel->evlist, pos) {
+ if (!(pos->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS))
+ continue;
+ if (header && strbuf_addf(&sb, "#"))
+ goto err;
+
+ if (strbuf_addf(&sb, " %s = %s\n", pos->name, pos->abbr_name))
+ goto err;
+ }
+
+ if (header && strbuf_addf(&sb, "#"))
+ goto err;
+ if (strbuf_addf(&sb, " '-' No event occurs\n"))
+ goto err;
+
+ if (header && strbuf_addf(&sb, "#"))
+ goto err;
+ if (strbuf_addf(&sb, " '+' Event occurrences may be lost due to branch counter saturated\n"))
+ goto err;
+
+ *str = strbuf_detach(&sb, NULL);
+
+ return 0;
+err:
+ strbuf_release(&sb);
+ return -ENOMEM;
+}
+
+/* Assume the branch counter saturated at 3 */
+#define ANNOTATION_BR_CNTR_SATURATION 3
+
+int annotation_br_cntr_entry(char **str, int br_cntr_nr,
+ u64 *br_cntr, int num_aggr,
+ struct evsel *evsel)
+{
+ struct evsel *pos = evsel ? evlist__first(evsel->evlist) : NULL;
+ bool saturated = false;
+ int i, j, avg, used;
+ struct strbuf sb;
+
+ strbuf_init(&sb, /*hint=*/ 0);
+ for (i = 0; i < br_cntr_nr; i++) {
+ used = 0;
+ avg = ceil((double)(br_cntr[i] & ~ANNOTATION__BR_CNTR_SATURATED_FLAG) /
+ (double)num_aggr);
+
+ /*
+ * A histogram with the abbr name is displayed by default.
+ * With -v, the exact number of branch counter is displayed.
+ */
+ if (verbose) {
+ evlist__for_each_entry_from(evsel->evlist, pos) {
+ if ((pos->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS) &&
+ (pos->br_cntr_idx == i))
+ break;
+ }
+ if (strbuf_addstr(&sb, pos->abbr_name))
+ goto err;
+
+ if (!br_cntr[i]) {
+ if (strbuf_addstr(&sb, "=-"))
+ goto err;
+ } else {
+ if (strbuf_addf(&sb, "=%d", avg))
+ goto err;
+ }
+ if (br_cntr[i] & ANNOTATION__BR_CNTR_SATURATED_FLAG) {
+ if (strbuf_addch(&sb, '+'))
+ goto err;
+ } else {
+ if (strbuf_addch(&sb, ' '))
+ goto err;
+ }
+
+ if ((i < br_cntr_nr - 1) && strbuf_addch(&sb, ','))
+ goto err;
+ continue;
+ }
+
+ if (strbuf_addch(&sb, '|'))
+ goto err;
+
+ if (!br_cntr[i]) {
+ if (strbuf_addch(&sb, '-'))
+ goto err;
+ used++;
+ } else {
+ evlist__for_each_entry_from(evsel->evlist, pos) {
+ if ((pos->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS) &&
+ (pos->br_cntr_idx == i))
+ break;
+ }
+ if (br_cntr[i] & ANNOTATION__BR_CNTR_SATURATED_FLAG)
+ saturated = true;
+
+ for (j = 0; j < avg; j++, used++) {
+ /* Print + if the number of logged events > 3 */
+ if (j >= ANNOTATION_BR_CNTR_SATURATION) {
+ saturated = true;
+ break;
+ }
+ if (strbuf_addstr(&sb, pos->abbr_name))
+ goto err;
+ }
+
+ if (saturated) {
+ if (strbuf_addch(&sb, '+'))
+ goto err;
+ used++;
+ }
+ pos = list_next_entry(pos, core.node);
+ }
+
+ for (j = used; j < ANNOTATION_BR_CNTR_SATURATION + 1; j++) {
+ if (strbuf_addch(&sb, ' '))
+ goto err;
+ }
+ }
+
+ if (!verbose && strbuf_addch(&sb, br_cntr_nr ? '|' : ' '))
+ goto err;
+
+ *str = strbuf_detach(&sb, NULL);
+
+ return 0;
+err:
+ strbuf_release(&sb);
+ return -ENOMEM;
+}
+
+struct type_hash_entry {
+ struct annotated_data_type *type;
+ int offset;
+};
+static int disasm_line__snprint_type_info(struct disasm_line *dl,
+ char *buf, int len,
+ struct annotation_print_data *apd)
{
- double percent_max = annotation_line__max_percent(al, notes, percent_type);
- int pcnt_width = annotation__pcnt_width(notes),
- cycles_width = annotation__cycles_width(notes);
+ struct annotated_data_type *data_type = NULL;
+ struct type_hash_entry *entry = NULL;
+ char member[256];
+ int offset = 0;
+ int printed;
+
+ scnprintf(buf, len, " ");
+
+ if (!annotate_opts.code_with_type || apd->dbg == NULL)
+ return 1;
+
+ if (apd->type_hash) {
+ hashmap__find(apd->type_hash, dl->al.offset, &entry);
+ if (entry != NULL) {
+ data_type = entry->type;
+ offset = entry->offset;
+ }
+ }
+
+ if (data_type == NULL)
+ data_type = __hist_entry__get_data_type(apd->he, apd->arch, apd->dbg, dl, &offset);
+
+ if (apd->type_hash && entry == NULL) {
+ entry = malloc(sizeof(*entry));
+ if (entry != NULL) {
+ entry->type = data_type;
+ entry->offset = offset;
+ hashmap__add(apd->type_hash, dl->al.offset, entry);
+ }
+ }
+
+ if (!needs_type_info(data_type))
+ return 1;
+
+ printed = scnprintf(buf, len, "\t\t# data-type: %s", data_type->self.type_name);
+
+ if (data_type != &stackop_type && data_type != &canary_type && len > printed)
+ printed += scnprintf(buf + printed, len - printed, " +%#x", offset);
+
+ if (annotated_data_type__get_member_name(data_type, member, sizeof(member), offset) &&
+ len > printed) {
+ printed += scnprintf(buf + printed, len - printed, " (%s)", member);
+ }
+ return printed;
+}
+
+void annotation_line__write(struct annotation_line *al, struct annotation *notes,
+ const struct annotation_write_ops *wops,
+ struct annotation_print_data *apd)
+{
+ bool current_entry = wops->current_entry;
+ bool change_color = wops->change_color;
+ double percent_max = annotation_line__max_percent(al, annotate_opts.percent_type);
+ int width = wops->width;
+ int pcnt_width = annotation__pcnt_width(notes);
+ int cycles_width = annotation__cycles_width(notes);
bool show_title = false;
char bf[256];
int printed;
-
- if (first_line && (al->offset == -1 || percent_max == 0.0)) {
+ void *obj = wops->obj;
+ int (*obj__set_color)(void *obj, int color) = wops->set_color;
+ void (*obj__set_percent_color)(void *obj, double percent, bool current) = wops->set_percent_color;
+ int (*obj__set_jumps_percent_color)(void *obj, int nr, bool current) = wops->set_jumps_percent_color;
+ void (*obj__printf)(void *obj, const char *fmt, ...) = wops->printf;
+ void (*obj__write_graph)(void *obj, int graph) = wops->write_graph;
+
+ if (wops->first_line && (al->offset == -1 || percent_max == 0.0)) {
if (notes->branch && al->cycles) {
if (al->cycles->ipc == 0.0 && al->cycles->avg == 0)
show_title = true;
@@ -1690,19 +2039,20 @@ static void __annotation_line__write(struct annotation_line *al, struct annotati
if (al->offset != -1 && percent_max != 0.0) {
int i;
- for (i = 0; i < notes->src->nr_events; i++) {
+ for (i = 0; i < al->data_nr; i++) {
double percent;
- percent = annotation_data__percent(&al->data[i], percent_type);
+ percent = annotation_data__percent(&al->data[i],
+ annotate_opts.percent_type);
obj__set_percent_color(obj, percent, current_entry);
if (symbol_conf.show_total_period) {
obj__printf(obj, "%11" PRIu64 " ", al->data[i].he.period);
} else if (symbol_conf.show_nr_samples) {
- obj__printf(obj, "%6" PRIu64 " ",
+ obj__printf(obj, "%7" PRIu64 " ",
al->data[i].he.nr_samples);
} else {
- obj__printf(obj, "%6.2f ", percent);
+ obj__printf(obj, "%7.2f ", percent);
}
}
} else {
@@ -1716,6 +2066,7 @@ static void __annotation_line__write(struct annotation_line *al, struct annotati
symbol_conf.show_nr_samples ? "Samples" : "Percent");
}
}
+ width -= pcnt_width;
if (notes->branch) {
if (al->cycles && al->cycles->ipc)
@@ -1758,16 +2109,34 @@ static void __annotation_line__write(struct annotation_line *al, struct annotati
"Cycle(min/max)");
}
+ if (annotate_opts.show_br_cntr) {
+ if (show_title) {
+ obj__printf(obj, "%*s ",
+ ANNOTATION__BR_CNTR_WIDTH,
+ "Branch Counter");
+ } else {
+ char *buf;
+
+ if (!annotation_br_cntr_entry(&buf, al->br_cntr_nr, al->br_cntr,
+ al->num_aggr, al->evsel)) {
+ obj__printf(obj, "%*s ", ANNOTATION__BR_CNTR_WIDTH, buf);
+ free(buf);
+ }
+ }
+ }
+
if (show_title && !*al->line) {
ipc_coverage_string(bf, sizeof(bf), notes);
obj__printf(obj, "%*s", ANNOTATION__AVG_IPC_WIDTH, bf);
}
}
+ width -= cycles_width;
obj__printf(obj, " ");
+ width -= 1;
if (!*al->line)
- obj__printf(obj, "%-*s", width - pcnt_width - cycles_width, " ");
+ obj__printf(obj, "%-*s", width, " ");
else if (al->offset == -1) {
if (al->line_nr && annotate_opts.show_linenr)
printed = scnprintf(bf, sizeof(bf), "%-*d ",
@@ -1776,7 +2145,8 @@ static void __annotation_line__write(struct annotation_line *al, struct annotati
printed = scnprintf(bf, sizeof(bf), "%-*s ",
notes->src->widths.addr, " ");
obj__printf(obj, bf);
- obj__printf(obj, "%-*s", width - printed - pcnt_width - cycles_width + 1, al->line);
+ width -= printed;
+ obj__printf(obj, "%-*s", width, al->line);
} else {
u64 addr = al->offset;
int color = -1;
@@ -1819,22 +2189,18 @@ print_addr:
if (change_color)
obj__set_color(obj, color);
- disasm_line__write(disasm_line(al), notes, obj, bf, sizeof(bf), obj__printf, obj__write_graph);
+ width -= printed;
- obj__printf(obj, "%-*s", width - pcnt_width - cycles_width - 3 - printed, bf);
- }
+ printed = disasm_line__write(disasm_line(al), notes, obj, bf, sizeof(bf),
+ obj__printf, obj__write_graph);
-}
+ obj__printf(obj, "%s", bf);
+ width -= printed;
+
+ disasm_line__snprint_type_info(disasm_line(al), bf, sizeof(bf), apd);
+ obj__printf(obj, "%-*s", width, bf);
+ }
-void annotation_line__write(struct annotation_line *al, struct annotation *notes,
- struct annotation_write_ops *wops)
-{
- __annotation_line__write(al, notes, wops->first_line, wops->current_entry,
- wops->change_color, wops->width, wops->obj,
- annotate_opts.percent_type,
- wops->set_color, wops->set_percent_color,
- wops->set_jumps_percent_color, wops->printf,
- wops->write_graph);
}
int symbol__annotate2(struct map_symbol *ms, struct evsel *evsel,
@@ -1843,10 +2209,7 @@ int symbol__annotate2(struct map_symbol *ms, struct evsel *evsel,
struct symbol *sym = ms->sym;
struct annotation *notes = symbol__annotation(sym);
size_t size = symbol__size(sym);
- int nr_pcnt = 1, err;
-
- if (evsel__is_group_event(evsel))
- nr_pcnt = evsel->core.nr_members;
+ int err;
err = symbol__annotate(ms, evsel, parch);
if (err)
@@ -1857,19 +2220,68 @@ int symbol__annotate2(struct map_symbol *ms, struct evsel *evsel,
annotation__set_index(notes);
annotation__mark_jump_targets(notes, sym);
- err = annotation__compute_ipc(notes, size);
+ err = annotation__compute_ipc(notes, size, evsel);
if (err)
return err;
annotation__init_column_widths(notes, sym);
- notes->src->nr_events = nr_pcnt;
-
annotation__update_column_widths(notes);
sym->annotate2 = 1;
return 0;
}
+const char * const perf_disassembler__strs[] = {
+ [PERF_DISASM_UNKNOWN] = "unknown",
+ [PERF_DISASM_LLVM] = "llvm",
+ [PERF_DISASM_CAPSTONE] = "capstone",
+ [PERF_DISASM_OBJDUMP] = "objdump",
+};
+
+
+static void annotation_options__add_disassembler(struct annotation_options *options,
+ enum perf_disassembler dis)
+{
+ for (u8 i = 0; i < ARRAY_SIZE(options->disassemblers); i++) {
+ if (options->disassemblers[i] == dis) {
+ /* Disassembler is already present then don't add again. */
+ return;
+ }
+ if (options->disassemblers[i] == PERF_DISASM_UNKNOWN) {
+ /* Found a free slot. */
+ options->disassemblers[i] = dis;
+ return;
+ }
+ }
+ pr_err("Failed to add disassembler %d\n", dis);
+}
+
+static int annotation_options__add_disassemblers_str(struct annotation_options *options,
+ const char *str)
+{
+ while (str && *str != '\0') {
+ const char *comma = strchr(str, ',');
+ int len = comma ? comma - str : (int)strlen(str);
+ bool match = false;
+
+ for (u8 i = 0; i < ARRAY_SIZE(perf_disassembler__strs); i++) {
+ const char *dis_str = perf_disassembler__strs[i];
+
+ if (len == (int)strlen(dis_str) && !strncmp(str, dis_str, len)) {
+ annotation_options__add_disassembler(options, i);
+ match = true;
+ break;
+ }
+ }
+ if (!match) {
+ pr_err("Invalid disassembler '%.*s'\n", len, str);
+ return -1;
+ }
+ str = comma ? comma + 1 : NULL;
+ }
+ return 0;
+}
+
static int annotation__config(const char *var, const char *value, void *data)
{
struct annotation_options *opt = data;
@@ -1884,6 +2296,11 @@ static int annotation__config(const char *var, const char *value, void *data)
opt->offset_level = ANNOTATION__MAX_OFFSET_LEVEL;
else if (opt->offset_level < ANNOTATION__MIN_OFFSET_LEVEL)
opt->offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
+ } else if (!strcmp(var, "annotate.disassemblers")) {
+ int err = annotation_options__add_disassemblers_str(opt, value);
+
+ if (err)
+ return err;
} else if (!strcmp(var, "annotate.hide_src_code")) {
opt->hide_src_code = perf_config_bool("hide_src_code", value);
} else if (!strcmp(var, "annotate.jump_arrows")) {
@@ -1941,6 +2358,8 @@ void annotation_options__init(void)
opt->annotate_src = true;
opt->offset_level = ANNOTATION__OFFSET_JUMP_TARGETS;
opt->percent_type = PERCENT_PERIOD_LOCAL;
+ opt->hide_src_code = true;
+ opt->hide_src_code_on_title = true;
}
void annotation_options__exit(void)
@@ -1949,9 +2368,25 @@ void annotation_options__exit(void)
zfree(&annotate_opts.objdump_path);
}
+static void annotation_options__default_init_disassemblers(struct annotation_options *options)
+{
+ if (options->disassemblers[0] != PERF_DISASM_UNKNOWN) {
+ /* Already initialized. */
+ return;
+ }
+#ifdef HAVE_LIBLLVM_SUPPORT
+ annotation_options__add_disassembler(options, PERF_DISASM_LLVM);
+#endif
+#ifdef HAVE_LIBCAPSTONE_SUPPORT
+ annotation_options__add_disassembler(options, PERF_DISASM_CAPSTONE);
+#endif
+ annotation_options__add_disassembler(options, PERF_DISASM_OBJDUMP);
+}
+
void annotation_config__init(void)
{
perf_config(annotation__config, &annotate_opts);
+ annotation_options__default_init_disassemblers(&annotate_opts);
}
static unsigned int parse_percent_type(char *str1, char *str2)
@@ -2060,7 +2495,7 @@ static int extract_reg_offset(struct arch *arch, const char *str,
if (regname == NULL)
return -1;
- op_loc->reg1 = get_dwarf_regnum(regname, 0);
+ op_loc->reg1 = get_dwarf_regnum(regname, arch->e_machine, arch->e_flags);
free(regname);
/* Get the second register */
@@ -2073,7 +2508,7 @@ static int extract_reg_offset(struct arch *arch, const char *str,
if (regname == NULL)
return -1;
- op_loc->reg2 = get_dwarf_regnum(regname, 0);
+ op_loc->reg2 = get_dwarf_regnum(regname, arch->e_machine, arch->e_flags);
free(regname);
}
return 0;
@@ -2123,20 +2558,33 @@ 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;
+ bool mem_ref = ops->source.mem_ref;
if (i == INSN_OP_TARGET) {
insn_str = ops->target.raw;
multi_regs = ops->target.multi_regs;
+ mem_ref = ops->target.mem_ref;
}
/* Invalidate the register by default */
op_loc->reg1 = -1;
op_loc->reg2 = -1;
- if (insn_str == NULL)
- continue;
+ if (insn_str == NULL) {
+ if (!arch__is(arch, "powerpc"))
+ continue;
+ }
- if (strchr(insn_str, arch->objdump.memory_ref_char)) {
+ /*
+ * For powerpc, call get_powerpc_regs function which extracts the
+ * required fields for op_loc, ie reg1, reg2, offset from the
+ * raw instruction.
+ */
+ if (arch__is(arch, "powerpc")) {
+ op_loc->mem_ref = mem_ref;
+ op_loc->multi_regs = multi_regs;
+ get_powerpc_regs(dl->raw.raw_insn, !i, op_loc);
+ } else 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);
@@ -2160,7 +2608,7 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl,
return -1;
if (*s == arch->objdump.register_char)
- op_loc->reg1 = get_dwarf_regnum(s, 0);
+ op_loc->reg1 = get_dwarf_regnum(s, arch->e_machine, arch->e_flags);
else if (*s == arch->objdump.imm_char) {
op_loc->offset = strtol(s + 1, &p, 0);
if (p && p != s + 1)
@@ -2216,7 +2664,7 @@ static struct annotated_item_stat *annotate_data_stat(struct list_head *head,
return NULL;
istat->name = strdup(name);
- if (istat->name == NULL) {
+ if ((istat->name == NULL) || (!strlen(istat->name))) {
free(istat);
return NULL;
}
@@ -2230,6 +2678,7 @@ 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, "call", 4) ||
!strncmp(dl->ins.name, "ret", 3))
return true;
}
@@ -2249,6 +2698,20 @@ static bool is_stack_canary(struct arch *arch, struct annotated_op_loc *loc)
return false;
}
+/**
+ * Returns true if the instruction has a memory operand without
+ * performing a load/store
+ */
+static bool is_address_gen_insn(struct arch *arch, struct disasm_line *dl)
+{
+ if (arch__is(arch, "x86")) {
+ if (!strncmp(dl->ins.name, "lea", 3))
+ return true;
+ }
+
+ return false;
+}
+
static struct disasm_line *
annotation__prev_asm_line(struct annotation *notes, struct disasm_line *curr)
{
@@ -2313,92 +2776,70 @@ u64 annotate_calc_pcrel(struct map_symbol *ms, u64 ip, int offset,
return map__rip_2objdump(ms->map, addr);
}
-/**
- * hist_entry__get_data_type - find data type for given hist entry
- * @he: hist entry
- *
- * This function first annotates the instruction at @he->ip and extracts
- * register and offset info from it. Then it searches the DWARF debug
- * info to get a variable and type information using the address, register,
- * and offset.
- */
-struct annotated_data_type *hist_entry__get_data_type(struct hist_entry *he)
+static struct debuginfo_cache {
+ struct dso *dso;
+ struct debuginfo *dbg;
+} di_cache;
+
+void debuginfo_cache__delete(void)
+{
+ dso__put(di_cache.dso);
+ di_cache.dso = NULL;
+
+ debuginfo__delete(di_cache.dbg);
+ di_cache.dbg = NULL;
+}
+
+static struct annotated_data_type *
+__hist_entry__get_data_type(struct hist_entry *he, struct arch *arch,
+ struct debuginfo *dbg, struct disasm_line *dl,
+ int *type_offset)
{
struct map_symbol *ms = &he->ms;
- struct evsel *evsel = hists_to_evsel(he->hists);
- struct arch *arch;
- struct disasm_line *dl;
struct annotated_insn_loc loc;
struct annotated_op_loc *op_loc;
struct annotated_data_type *mem_type;
struct annotated_item_stat *istat;
- u64 ip = he->ip;
int i;
- ann_data_stat.total++;
-
- if (ms->map == NULL || ms->sym == NULL) {
- ann_data_stat.no_sym++;
- return NULL;
- }
-
- if (!symbol_conf.init_annotation) {
- ann_data_stat.no_sym++;
- return NULL;
- }
-
- /* Make sure it has the disasm of the function */
- if (symbol__annotate(ms, evsel, &arch) < 0) {
- ann_data_stat.no_insn++;
- return NULL;
- }
-
- /*
- * Get a disasm to extract the location from the insn.
- * This is too slow...
- */
- dl = find_disasm_line(ms->sym, ip, /*allow_update=*/true);
- if (dl == NULL) {
- ann_data_stat.no_insn++;
- return NULL;
- }
-
-retry:
istat = annotate_data_stat(&ann_insn_stat, dl->ins.name);
if (istat == NULL) {
ann_data_stat.no_insn++;
- return NULL;
+ return NO_TYPE;
}
if (annotate_get_insn_location(arch, dl, &loc) < 0) {
ann_data_stat.no_insn_ops++;
istat->bad++;
- return NULL;
+ return NO_TYPE;
}
if (is_stack_operation(arch, dl)) {
istat->good++;
- he->mem_type_off = 0;
+ *type_offset = 0;
return &stackop_type;
}
+ if (is_address_gen_insn(arch, dl)) {
+ istat->bad++;
+ ann_data_stat.no_mem_ops++;
+ return NO_TYPE;
+ }
+
for_each_insn_op_loc(&loc, i, op_loc) {
struct data_loc_info dloc = {
.arch = arch,
.thread = he->thread,
.ms = ms,
- /* Recalculate IP for LOCK prefix or insn fusion */
.ip = ms->sym->start + dl->al.offset,
.cpumode = he->cpumode,
.op = op_loc,
+ .di = dbg,
};
if (!op_loc->mem_ref && op_loc->segment == INSN_SEG_NONE)
continue;
- /* Recalculate IP because of LOCK prefix or insn fusion */
- ip = ms->sym->start + dl->al.offset;
-
/* PC-relative addressing */
if (op_loc->reg1 == DWARF_REG_PC) {
dloc.var_addr = annotate_calc_pcrel(ms, dloc.ip,
@@ -2416,7 +2857,7 @@ retry:
if (mem_type == NULL && is_stack_canary(arch, op_loc)) {
istat->good++;
- he->mem_type_off = 0;
+ *type_offset = 0;
return &canary_type;
}
@@ -2426,15 +2867,95 @@ retry:
istat->bad++;
if (symbol_conf.annotate_data_sample) {
+ struct evsel *evsel = hists_to_evsel(he->hists);
+
annotated_data_type__update_samples(mem_type, evsel,
dloc.type_offset,
he->stat.nr_events,
he->stat.period);
}
- he->mem_type_off = dloc.type_offset;
- return mem_type;
+ *type_offset = dloc.type_offset;
+ return mem_type ?: NO_TYPE;
+ }
+
+ /* retry with a fused instruction */
+ return NULL;
+}
+
+/**
+ * hist_entry__get_data_type - find data type for given hist entry
+ * @he: hist entry
+ *
+ * This function first annotates the instruction at @he->ip and extracts
+ * register and offset info from it. Then it searches the DWARF debug
+ * info to get a variable and type information using the address, register,
+ * and offset.
+ */
+struct annotated_data_type *hist_entry__get_data_type(struct hist_entry *he)
+{
+ struct map_symbol *ms = &he->ms;
+ struct evsel *evsel = hists_to_evsel(he->hists);
+ struct arch *arch;
+ struct disasm_line *dl;
+ struct annotated_data_type *mem_type;
+ struct annotated_item_stat *istat;
+ u64 ip = he->ip;
+
+ ann_data_stat.total++;
+
+ if (ms->map == NULL || ms->sym == NULL) {
+ ann_data_stat.no_sym++;
+ return NULL;
+ }
+
+ if (!symbol_conf.init_annotation) {
+ ann_data_stat.no_sym++;
+ return NULL;
+ }
+
+ /*
+ * di_cache holds a pair of values, but code below assumes
+ * di_cache.dso can be compared/updated and di_cache.dbg can be
+ * read/updated independently from each other. That assumption only
+ * holds in single threaded code.
+ */
+ assert(perf_singlethreaded);
+
+ if (map__dso(ms->map) != di_cache.dso) {
+ dso__put(di_cache.dso);
+ di_cache.dso = dso__get(map__dso(ms->map));
+
+ debuginfo__delete(di_cache.dbg);
+ di_cache.dbg = dso__debuginfo(di_cache.dso);
+ }
+
+ if (di_cache.dbg == NULL) {
+ ann_data_stat.no_dbginfo++;
+ return NULL;
}
+ /* Make sure it has the disasm of the function */
+ if (symbol__annotate(ms, evsel, &arch) < 0) {
+ ann_data_stat.no_insn++;
+ return NULL;
+ }
+
+ /*
+ * Get a disasm to extract the location from the insn.
+ * This is too slow...
+ */
+ dl = find_disasm_line(ms->sym, ip, /*allow_update=*/true);
+ if (dl == NULL) {
+ ann_data_stat.no_insn++;
+ return NULL;
+ }
+
+retry:
+ mem_type = __hist_entry__get_data_type(he, arch, di_cache.dbg, dl,
+ &he->mem_type_off);
+ if (mem_type)
+ return mem_type == NO_TYPE ? NULL : mem_type;
+
/*
* Some instructions can be fused and the actual memory access came
* from the previous instruction.
@@ -2453,7 +2974,9 @@ retry:
}
ann_data_stat.no_mem_ops++;
- istat->bad++;
+ istat = annotate_data_stat(&ann_insn_stat, dl->ins.name);
+ if (istat)
+ istat->bad++;
return NULL;
}