[RFC 11/11] perf annotate: Show hazard data in tui mode

From: Ravi Bangoria
Date: Mon Mar 02 2020 - 00:25:10 EST


Enable perf report->annotate tui mode to show hazard information. By
default they are hidden, but user can unhide them by pressing hot key
'S'. Sample o/p:

â Disassembly of section .text:
â
â 0000000010001cf8 <compare>:
â compare():
â return NULL;
â }
â
â static int
â compare(const void *p1, const void *p2)
â {
33.23 â std r31,-8(r1)
â {haz_stage: LSU, haz_reason: ERAT Miss, stall_stage: LSU, stall_reason: Store, icache: L1 hit}
â {haz_stage: LSU, haz_reason: ERAT Miss, stall_stage: LSU, stall_reason: Store, icache: L1 hit}
â {haz_stage: LSU, haz_reason: Load Hit Store, stall_stage: LSU, stall_reason: -, icache: L3 hit}
â {haz_stage: LSU, haz_reason: ERAT Miss, stall_stage: -, stall_reason: -, icache: L1 hit}
â {haz_stage: LSU, haz_reason: ERAT Miss, stall_stage: LSU, stall_reason: Store, icache: L1 hit}
â {haz_stage: LSU, haz_reason: ERAT Miss, stall_stage: LSU, stall_reason: Store, icache: L1 hit}
0.84 â stdu r1,-64(r1)
â {haz_stage: LSU, haz_reason: ERAT Miss, stall_stage: -, stall_reason: -, icache: L1 hit}
0.24 â mr r31,r1
â {haz_stage: -, haz_reason: -, stall_stage: -, stall_reason: -, icache: L1 hit}
21.18 â std r3,32(r31)
â {haz_stage: LSU, haz_reason: ERAT Miss, stall_stage: LSU, stall_reason: Store, icache: L1 hit}
â {haz_stage: LSU, haz_reason: ERAT Miss, stall_stage: LSU, stall_reason: Store, icache: L1 hit}
â {haz_stage: LSU, haz_reason: ERAT Miss, stall_stage: LSU, stall_reason: Store, icache: L1 hit}

Signed-off-by: Ravi Bangoria <ravi.bangoria@xxxxxxxxxxxxx>
---
tools/perf/builtin-annotate.c | 5 ++
tools/perf/ui/browsers/annotate.c | 124 ++++++++++++++++++++++++++----
tools/perf/util/annotate.c | 51 +++++++++++-
tools/perf/util/annotate.h | 18 ++++-
4 files changed, 178 insertions(+), 20 deletions(-)

diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 78552a9428a6..a51313a6b019 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -472,6 +472,7 @@ static const char * const annotate_usage[] = {

int cmd_annotate(int argc, const char **argv)
{
+ bool annotate_haz = false;
struct perf_annotate annotate = {
.tool = {
.sample = process_sample_event,
@@ -531,6 +532,8 @@ int cmd_annotate(int argc, const char **argv)
symbol__config_symfs),
OPT_BOOLEAN(0, "source", &annotate.opts.annotate_src,
"Interleave source code with assembly code (default)"),
+ OPT_BOOLEAN(0, "hazard", &annotate_haz,
+ "Interleave CPU pileline hazard/stall data with assembly code"),
OPT_BOOLEAN(0, "asm-raw", &annotate.opts.show_asm_raw,
"Display raw encoding of assembly instructions (default)"),
OPT_STRING('M', "disassembler-style", &annotate.opts.disassembler_style, "disassembler style",
@@ -583,6 +586,8 @@ int cmd_annotate(int argc, const char **argv)
if (annotate_check_args(&annotate.opts) < 0)
return -EINVAL;

+ annotate.opts.hide_haz_data = !annotate_haz;
+
if (symbol_conf.show_nr_samples && annotate.use_gtk) {
pr_err("--show-nr-samples is not available in --gtk mode at this time\n");
return ret;
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index 2e4db8216b3b..b04d825cee50 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -190,9 +190,15 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)
return;
}

- if (notes->options->hide_src_code) {
+ if (notes->options->hide_src_code && notes->options->hide_haz_data) {
from = cursor->al.idx_asm;
to = target->idx_asm;
+ } else if (!notes->options->hide_src_code && notes->options->hide_haz_data) {
+ from = cursor->al.idx_asm + cursor->al.idx_src + 1;
+ to = target->idx_asm + target->idx_src + 1;
+ } else if (notes->options->hide_src_code && !notes->options->hide_haz_data) {
+ from = cursor->al.idx_asm + cursor->al.idx_haz + 1;
+ to = target->idx_asm + target->idx_haz + 1;
} else {
from = (u64)cursor->al.idx;
to = (u64)target->idx;
@@ -293,8 +299,13 @@ static void annotate_browser__set_rb_top(struct annotate_browser *browser,
struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
u32 idx = pos->idx;

- if (notes->options->hide_src_code)
+ if (notes->options->hide_src_code && notes->options->hide_haz_data)
idx = pos->idx_asm;
+ else if (!notes->options->hide_src_code && notes->options->hide_haz_data)
+ idx = pos->idx_asm + pos->idx_src + 1;
+ else if (notes->options->hide_src_code && !notes->options->hide_haz_data)
+ idx = pos->idx_asm + pos->idx_haz + 1;
+
annotate_browser__set_top(browser, pos, idx);
browser->curr_hot = nd;
}
@@ -348,44 +359,117 @@ static bool annotate_browser__toggle_source(struct annotate_browser *browser)
struct annotation *notes = browser__annotation(&browser->b);
struct annotation_line *al;
off_t offset = browser->b.index - browser->b.top_idx;
+ u32 curr_idx;

browser->b.seek(&browser->b, offset, SEEK_CUR);
al = list_entry(browser->b.top, struct annotation_line, node);

if (notes->options->hide_src_code) {
- if (al->idx_asm < offset)
- offset = al->idx;
+ if (notes->options->hide_haz_data) {
+ curr_idx = al->idx_asm + al->idx_src + 1;
+ browser->b.nr_entries = notes->nr_asm_entries +
+ notes->nr_src_entries;
+ } else {
+ curr_idx = al->idx;
+ browser->b.nr_entries = notes->nr_entries;
+ }

- browser->b.nr_entries = notes->nr_entries;
notes->options->hide_src_code = false;
browser->b.seek(&browser->b, -offset, SEEK_CUR);
- browser->b.top_idx = al->idx - offset;
- browser->b.index = al->idx;
+ browser->b.top_idx = curr_idx - offset;
+ browser->b.index = curr_idx;
} else {
- if (al->idx_asm < 0) {
+ if (al->type != AL_TYPE_ASM) {
ui_helpline__puts("Only available for assembly lines.");
browser->b.seek(&browser->b, -offset, SEEK_CUR);
return false;
}

- if (al->idx_asm < offset)
- offset = al->idx_asm;
+ if (notes->options->hide_haz_data) {
+ curr_idx = al->idx_asm;
+ browser->b.nr_entries = notes->nr_asm_entries;
+ } else {
+ curr_idx = al->idx_asm + al->idx_haz + 1;
+ browser->b.nr_entries = notes->nr_asm_entries +
+ notes->nr_haz_entries;
+ }
+
+ if (curr_idx < offset)
+ offset = curr_idx;

- browser->b.nr_entries = notes->nr_asm_entries;
notes->options->hide_src_code = true;
browser->b.seek(&browser->b, -offset, SEEK_CUR);
- browser->b.top_idx = al->idx_asm - offset;
- browser->b.index = al->idx_asm;
+ browser->b.top_idx = curr_idx - offset;
+ browser->b.index = curr_idx;
+ }
+
+ return true;
+}
+
+static bool annotate_browser__toggle_hazard(struct annotate_browser *browser)
+{
+ struct annotation *notes = browser__annotation(&browser->b);
+ struct annotation_line *al;
+ off_t offset = browser->b.index - browser->b.top_idx;
+ u32 curr_idx;
+
+ browser->b.seek(&browser->b, offset, SEEK_CUR);
+ al = list_entry(browser->b.top, struct annotation_line, node);
+
+ if (notes->options->hide_haz_data) {
+ if (notes->options->hide_src_code) {
+ curr_idx = al->idx_asm + al->idx_haz + 1;
+ browser->b.nr_entries = notes->nr_asm_entries +
+ notes->nr_haz_entries;
+ } else {
+ curr_idx = al->idx;
+ browser->b.nr_entries = notes->nr_entries;
+ }
+
+ notes->options->hide_haz_data = false;
+ browser->b.seek(&browser->b, -offset, SEEK_CUR);
+ browser->b.top_idx = curr_idx - offset;
+ browser->b.index = curr_idx;
+ } else {
+ if (al->type != AL_TYPE_ASM) {
+ ui_helpline__puts("Only available for assembly lines.");
+ browser->b.seek(&browser->b, -offset, SEEK_CUR);
+ return false;
+ }
+
+ if (notes->options->hide_src_code) {
+ curr_idx = al->idx_asm;
+ browser->b.nr_entries = notes->nr_asm_entries;
+ } else {
+ curr_idx = al->idx_asm + al->idx_src + 1;
+ browser->b.nr_entries = notes->nr_asm_entries +
+ notes->nr_src_entries;
+ }
+
+ if (curr_idx < offset)
+ offset = curr_idx;
+
+ notes->options->hide_haz_data = true;
+ browser->b.seek(&browser->b, -offset, SEEK_CUR);
+ browser->b.top_idx = curr_idx - offset;
+ browser->b.index = curr_idx;
}

return true;
}

-static void ui_browser__init_asm_mode(struct ui_browser *browser)
+static void ui_browser__hide_src_code(struct ui_browser *browser)
+{
+ struct annotation *notes = browser__annotation(browser);
+ ui_browser__reset_index(browser);
+ browser->nr_entries -= notes->nr_src_entries;
+}
+
+static void ui_browser__hide_haz_data(struct ui_browser *browser)
{
struct annotation *notes = browser__annotation(browser);
ui_browser__reset_index(browser);
- browser->nr_entries = notes->nr_asm_entries;
+ browser->nr_entries -= notes->nr_haz_entries;
}

#define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
@@ -743,6 +827,7 @@ static int annotate_browser__run(struct annotate_browser *browser,
"o Toggle disassembler output/simplified view\n"
"O Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
"s Toggle source code view\n"
+ "S Toggle pipeline hazard/stall view\n"
"t Circulate percent, total period, samples view\n"
"c Show min/max cycle\n"
"/ Search string\n"
@@ -767,6 +852,10 @@ static int annotate_browser__run(struct annotate_browser *browser,
if (annotate_browser__toggle_source(browser))
ui_helpline__puts(help);
continue;
+ case 'S':
+ if (annotate_browser__toggle_hazard(browser))
+ ui_helpline__puts(help);
+ continue;
case 'o':
notes->options->use_offset = !notes->options->use_offset;
annotation__update_column_widths(notes);
@@ -932,7 +1021,10 @@ int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
browser.b.width += 18; /* Percentage */

if (notes->options->hide_src_code)
- ui_browser__init_asm_mode(&browser.b);
+ ui_browser__hide_src_code(&browser.b);
+
+ if (notes->options->hide_haz_data)
+ ui_browser__hide_haz_data(&browser.b);

ret = annotate_browser__run(&browser, evsel, hbt);

diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 766934b0f36d..d782ee193345 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -66,6 +66,7 @@ struct annotation_options annotation__default_options = {
.annotate_src = true,
.offset_level = ANNOTATION__OFFSET_JUMP_TARGETS,
.percent_type = PERCENT_PERIOD_LOCAL,
+ .hide_haz_data = true,
};

static regex_t file_lineno;
@@ -1268,7 +1269,7 @@ static struct disasm_line *disasm_line__new(struct annotate_args *args)
struct disasm_line *dl = NULL;
int nr = 1;

- if (perf_evsel__is_group_event(args->evsel))
+ if (args->type != AL_TYPE_HAZ && perf_evsel__is_group_event(args->evsel))
nr = args->evsel->core.nr_members;

dl = zalloc(disasm_line_size(nr));
@@ -1509,6 +1510,7 @@ annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start
} else if (max_lines && printed >= max_lines)
return 1;
else {
+ /* TODO: Hazard specific changes */
int width = symbol_conf.show_total_period ? 12 : 8;

if (queue)
@@ -1526,6 +1528,34 @@ annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start
return 0;
}

+static int annotation_add_hazard_detail(struct annotation *notes, s64 offset)
+{
+ int ret;
+ struct hazard_hist *hh;
+ struct disasm_line *dl;
+ struct annotate_args args = {
+ .type = AL_TYPE_HAZ,
+ .offset = -1,
+ };
+
+ list_for_each_entry(hh, &notes->src->haz_hist[offset].list, list) {
+ ret = asprintf(&(args.line), " {haz_stage: %s, "
+ "haz_reason: %s, stall_stage: %s, stall_reason: %s, "
+ "icache: %s}", hh->haz_stage, hh->haz_reason,
+ hh->stall_stage, hh->stall_reason, hh->icache);
+ if (ret == -1)
+ return -ENOMEM;
+
+ dl = disasm_line__new(&args);
+ free(args.line);
+ if (!dl)
+ return -ENOMEM;
+
+ annotation_line__add(&dl->al, &notes->src->source);
+ }
+ return 0;
+}
+
/*
* symbol__parse_objdump_line() parses objdump output (with -d --no-show-raw)
* which looks like following
@@ -1608,6 +1638,10 @@ static int symbol__parse_objdump_line(struct symbol *sym,

annotation_line__add(&dl->al, &notes->src->source);

+ if (offset != -1 && notes->src->haz_hist &&
+ !list_empty(&notes->src->haz_hist[offset].list))
+ return annotation_add_hazard_detail(notes, offset);
+
return 0;
}

@@ -2701,6 +2735,8 @@ static void annotation__set_offsets(struct annotation *notes, s64 size)
notes->max_line_len = 0;
notes->nr_entries = 0;
notes->nr_asm_entries = 0;
+ notes->nr_src_entries = 0;
+ notes->nr_haz_entries = 0;

list_for_each_entry(al, &notes->src->source, node) {
size_t line_len = strlen(al->line);
@@ -2710,6 +2746,8 @@ static void annotation__set_offsets(struct annotation *notes, s64 size)
al->idx = notes->nr_entries++;
if (al->type == AL_TYPE_ASM) {
al->idx_asm = notes->nr_asm_entries++;
+ al->idx_src = notes->nr_src_entries - 1;
+ al->idx_haz = notes->nr_haz_entries - 1;
/*
* FIXME: short term bandaid to cope with assembly
* routines that comes with labels in the same column
@@ -2719,8 +2757,15 @@ static void annotation__set_offsets(struct annotation *notes, s64 size)
*/
if (al->offset < size)
notes->offsets[al->offset] = al;
- } else
+ } else if (al->type == AL_TYPE_SRC) {
al->idx_asm = -1;
+ al->idx_src = notes->nr_src_entries++;
+ al->idx_haz = notes->nr_haz_entries - 1;
+ } else if (al->type == AL_TYPE_HAZ) {
+ al->idx_asm = -1;
+ al->idx_haz = notes->nr_haz_entries++;
+ al->idx_src = notes->nr_src_entries - 1;
+ }
}
}

@@ -3051,6 +3096,8 @@ static void __annotation_line__write(struct annotation_line *al, struct annotati
printed = scnprintf(bf, sizeof(bf), "%-*s ", notes->widths.addr, " ");
obj__printf(obj, bf);
obj__printf(obj, "%-*s", width - printed - pcnt_width - cycles_width + 1, al->line);
+ } else if (al->type == AL_TYPE_HAZ) {
+ obj__printf(obj, "%-*s", width - pcnt_width - cycles_width, al->line);
} else {
u64 addr = al->offset;
int color = -1;
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index a3803f89b8fc..3a082c13bdad 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -85,7 +85,8 @@ struct annotation_options {
show_nr_jumps,
show_minmax_cycle,
show_asm_raw,
- annotate_src;
+ annotate_src,
+ hide_haz_data;
u8 offset_level;
int min_pcnt;
int max_lines;
@@ -131,8 +132,16 @@ struct annotation_data {
enum annotation_line_type {
AL_TYPE_ASM = 0,
AL_TYPE_SRC,
+ AL_TYPE_HAZ,
};

+/*
+ * @idx_asm, @idx_src and @idx_haz starts from -1 and increments in
+ * chronological order. For _all_ non-assembly lines, @idx_asm is -1.
+ * OTOH, for non-source line, @idx_src contains prev value. Similarly
+ * for non-hazard lines @idx_haz also contains prev value.
+ * And @idx = @idx_asm + (@idx_src + 1) + (@idx_haz + 1);
+ */
struct annotation_line {
struct list_head node;
struct rb_node rb_node;
@@ -148,6 +157,8 @@ struct annotation_line {
char *path;
u32 idx;
int idx_asm;
+ int idx_src;
+ int idx_haz;
int data_nr;
struct annotation_data data[0];
};
@@ -298,6 +309,8 @@ struct annotation {
int max_jump_sources;
int nr_entries;
int nr_asm_entries;
+ int nr_src_entries;
+ int nr_haz_entries;
u16 max_line_len;
struct {
u8 addr;
@@ -326,7 +339,8 @@ static inline int annotation__pcnt_width(struct annotation *notes)

static inline bool annotation_line__filter(struct annotation_line *al, struct annotation *notes)
{
- return notes->options->hide_src_code && al->type == AL_TYPE_SRC;
+ return ((notes->options->hide_src_code && al->type == AL_TYPE_SRC) ||
+ (notes->options->hide_haz_data && al->type == AL_TYPE_HAZ));
}

void annotation__update_column_widths(struct annotation *notes);
--
2.21.1