Show the branch type statistics at the end of perf report --stdio.

For example:
perf report --stdio

 JCC forward:  34.0%
JCC backward:   3.6%
         JMP:   0.0%
     IND_JMP:   6.5%
        CALL:  26.6%
    IND_CALL:   0.0%
         RET:  29.3%
  FAR_BRANCH:   0.0%

Signed-off-by: Jin Yao <[email protected]>
---
 tools/perf/builtin-report.c | 140 ++++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/hist.c      |   5 +-
 2 files changed, 141 insertions(+), 4 deletions(-)

diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index c18158b..fb26513 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -43,6 +43,17 @@
 #include <linux/bitmap.h>
 #include <linux/stringify.h>
 
+struct branch_type_stat {
+       u64     jcc_fwd;
+       u64     jcc_bwd;
+       u64     jmp;
+       u64     ind_jmp;
+       u64     call;
+       u64     ind_call;
+       u64     ret;
+       u64     far_branch;
+};
+
 struct report {
        struct perf_tool        tool;
        struct perf_session     *session;
@@ -66,6 +77,7 @@ struct report {
        u64                     queue_size;
        int                     socket_filter;
        DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
+       struct branch_type_stat brtype_stat;
 };
 
 static int report__config(const char *var, const char *value, void *cb)
@@ -144,6 +156,66 @@ static int hist_iter__report_callback(struct 
hist_entry_iter *iter,
        return err;
 }
 
+static void branch_type_count(struct report *rep, struct branch_info *bi)
+{
+       struct branch_type_stat *stat = &rep->brtype_stat;
+       struct branch_flags *flags = &bi->flags;
+
+       switch (flags->type) {
+       case PERF_BR_JCC_FWD:
+               stat->jcc_fwd++;
+               break;
+
+       case PERF_BR_JCC_BWD:
+               stat->jcc_bwd++;
+               break;
+
+       case PERF_BR_JMP:
+               stat->jmp++;
+               break;
+
+       case PERF_BR_IND_JMP:
+               stat->ind_jmp++;
+               break;
+
+       case PERF_BR_CALL:
+               stat->call++;
+               break;
+
+       case PERF_BR_IND_CALL:
+               stat->ind_call++;
+               break;
+
+       case PERF_BR_RET:
+               stat->ret++;
+               break;
+
+       case PERF_BR_FAR_BRANCH:
+               stat->far_branch++;
+               break;
+
+       default:
+               break;
+       }
+}
+
+static int hist_iter__branch_callback(struct hist_entry_iter *iter,
+                                     struct addr_location *al __maybe_unused,
+                                     bool single __maybe_unused,
+                                     void *arg)
+{
+       struct hist_entry *he = iter->he;
+       struct report *rep = arg;
+       struct branch_info *bi;
+
+       if (sort__mode == SORT_MODE__BRANCH) {
+               bi = he->branch_info;
+               branch_type_count(rep, bi);
+       }
+
+       return 0;
+}
+
 static int process_sample_event(struct perf_tool *tool,
                                union perf_event *event,
                                struct perf_sample *sample,
@@ -182,6 +254,8 @@ static int process_sample_event(struct perf_tool *tool,
                 */
                if (!sample->branch_stack)
                        goto out_put;
+
+               iter.add_entry_cb = hist_iter__branch_callback;
                iter.ops = &hist_iter_branch;
        } else if (rep->mem_mode) {
                iter.ops = &hist_iter_mem;
@@ -369,6 +443,67 @@ static size_t hists__fprintf_nr_sample_events(struct hists 
*hists, struct report
        return ret + fprintf(fp, "\n#\n");
 }
 
+static void branch_type_stat_display(FILE *fp, struct branch_type_stat *stat)
+{
+       u64 total = 0;
+
+       total += stat->jcc_fwd;
+       total += stat->jcc_bwd;
+       total += stat->jmp;
+       total += stat->ind_jmp;
+       total += stat->call;
+       total += stat->ind_call;
+       total += stat->ret;
+       total += stat->far_branch;
+
+       if (total == 0)
+               return;
+
+       fprintf(fp, "\n#");
+       fprintf(fp, "\n# Branch Statistics:");
+       fprintf(fp, "\n#");
+
+       if (stat->jcc_fwd > 0)
+               fprintf(fp, "\n%12s: %5.1f%%",
+                       "JCC forward",
+                       100.0 * (double)stat->jcc_fwd / (double)total);
+
+       if (stat->jcc_bwd > 0)
+               fprintf(fp, "\n%12s: %5.1f%%",
+                       "JCC backward",
+                       100.0 * (double)stat->jcc_bwd / (double)total);
+
+       if (stat->jmp > 0)
+               fprintf(fp, "\n%12s: %5.1f%%",
+                       "JMP",
+                       100.0 * (double)stat->jmp / (double)total);
+
+       if (stat->ind_jmp > 0)
+               fprintf(fp, "\n%12s: %5.1f%%",
+                       "IND_JMP",
+                       100.0 * (double)stat->ind_jmp / (double)total);
+
+       if (stat->call > 0)
+               fprintf(fp, "\n%12s: %5.1f%%",
+                       "CALL",
+                       100.0 * (double)stat->call / (double)total);
+
+       if (stat->ind_call > 0)
+               fprintf(fp, "\n%12s: %5.1f%%",
+                       "IND_CALL",
+                       100.0 * (double)stat->ind_call / (double)total);
+
+       if (stat->ret > 0)
+               fprintf(fp, "\n%12s: %5.1f%%",
+                       "RET",
+                       100.0 * (double)stat->ret / (double)total);
+
+       if (stat->far_branch > 0)
+               fprintf(fp, "\n%12s: %5.1f%%",
+                       "FAR_BRANCH",
+                       100.0 * (double)stat->far_branch / (double)total);
+}
+
 static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
                                         struct report *rep,
                                         const char *help)
@@ -404,6 +539,9 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist 
*evlist,
                perf_read_values_destroy(&rep->show_threads_values);
        }
 
+       if (sort__mode == SORT_MODE__BRANCH)
+               branch_type_stat_display(stdout, &rep->brtype_stat);
+
        return 0;
 }
 
@@ -936,6 +1074,8 @@ int cmd_report(int argc, const char **argv)
        if (has_br_stack && branch_call_mode)
                symbol_conf.show_branchflag_count = true;
 
+       memset(&report.brtype_stat, 0, sizeof(struct branch_type_stat));
+
        /*
         * Branch mode is a tristate:
         * -1 means default, so decide based on the file having branch data.
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 61bf304..c8aee25 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -745,12 +745,9 @@ iter_prepare_branch_entry(struct hist_entry_iter *iter, 
struct addr_location *al
 }
 
 static int
-iter_add_single_branch_entry(struct hist_entry_iter *iter,
+iter_add_single_branch_entry(struct hist_entry_iter *iter __maybe_unused,
                             struct addr_location *al __maybe_unused)
 {
-       /* to avoid calling callback function */
-       iter->he = NULL;
-
        return 0;
 }
 
-- 
2.7.4

Reply via email to