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.bango...@linux.ibm.com>
---
 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

Reply via email to