]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
perf annotate: Save branch counters for each block
authorKan Liang <kan.liang@linux.intel.com>
Tue, 13 Aug 2024 16:02:03 +0000 (09:02 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Wed, 14 Aug 2024 13:20:40 +0000 (10:20 -0300)
When annotating a basic block, it's useful to display the occurrences
of other events in the block.

The branch counter feature is only available for newer Intel platforms.

So a dedicated option to display the branch counters is not introduced.

Reuse the existing --total-cycles option, which triggers the annotation
of a basic block and displays the cycle-related annotation.

When the branch counters information is available, the branch counters
are automatically appended after all the cycle-related annotation.

Accounting the branch counters as well when accounting the cycles in
hist__account_cycles().

In 'struct annotated_branch', introduce a br_cntr array to save the
accumulation of each branch counter.

In a sample, all the branch counters for a branch are saved in a u64
space.

Because the saturation of a branch counter is small, e.g., for Intel
Sierra Forest, the saturation is only 3.

Add ANNOTATION__BR_CNTR_SATURATED_FLAG to indicate if a branch counter
once saturated. That can be used to indicate a potential event lost
because of the saturation.

Reviewed-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: https://lore.kernel.org/r/20240813160208.2493643-5-kan.liang@linux.intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/builtin-annotate.c
tools/perf/builtin-diff.c
tools/perf/builtin-report.c
tools/perf/builtin-top.c
tools/perf/util/annotate.c
tools/perf/util/annotate.h
tools/perf/util/branch.h
tools/perf/util/hist.c
tools/perf/util/hist.h
tools/perf/util/machine.c

index d6f6ba5a569db4b9173c4776e7af91c81564996e..9b4a8a379b5b3c852862c7cf910924308aade136 100644 (file)
@@ -221,7 +221,8 @@ static int process_branch_callback(struct evsel *evsel,
        if (a.map != NULL)
                dso__set_hit(map__dso(a.map));
 
-       hist__account_cycles(sample->branch_stack, al, sample, false, NULL);
+       hist__account_cycles(sample->branch_stack, al, sample, false,
+                            NULL, evsel);
 
        ret = hist_entry_iter__add(&iter, &a, PERF_MAX_STACK_DEPTH, ann);
 out:
index 28c5208fcdc9b8caebbc73db3e86cced76d81316..6983b5373372a3b36c9c09cf149aecb49f4b2ce2 100644 (file)
@@ -431,8 +431,8 @@ static int diff__process_sample_event(const struct perf_tool *tool,
                        goto out;
                }
 
-               hist__account_cycles(sample->branch_stack, &al, sample, false,
-                                    NULL);
+               hist__account_cycles(sample->branch_stack, &al, sample,
+                                    false, NULL, evsel);
                break;
 
        case COMPUTE_STREAM:
index 04b9a5c1bc7e3411b809a72bd9dc736a3ddc8414..ab450644e5b36182b00990cacce8c6dfd8377fbf 100644 (file)
@@ -328,7 +328,7 @@ static int process_sample_event(const struct perf_tool *tool,
        if (ui__has_annotation() || rep->symbol_ipc || rep->total_cycles_mode) {
                hist__account_cycles(sample->branch_stack, &al, sample,
                                     rep->nonany_branch_mode,
-                                    &rep->total_cycles);
+                                    &rep->total_cycles, evsel);
        }
 
        ret = hist_entry_iter__add(&iter, &al, rep->max_stack, rep);
index 1cdac4e190e96f5e62687787e338dcad317dc92c..881b861c35eebd8d799afc30e3adf472666912f8 100644 (file)
@@ -735,8 +735,8 @@ static int hist_iter__top_callback(struct hist_entry_iter *iter,
                perf_top__record_precise_ip(top, iter->he, iter->sample, evsel, al->addr);
 
        hist__account_cycles(iter->sample->branch_stack, al, iter->sample,
-                    !(top->record_opts.branch_stack & PERF_SAMPLE_BRANCH_ANY),
-                    NULL);
+                            !(top->record_opts.branch_stack & PERF_SAMPLE_BRANCH_ANY),
+                            NULL, evsel);
        return 0;
 }
 
index a87d2e97e10d1f016f46c0dfa37dee59993a4904..c269758d47c89b1d1e7fbba06ce71579a480e724 100644 (file)
@@ -266,22 +266,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)
@@ -316,16 +324,44 @@ static int symbol__inc_addr_samples(struct map_symbol *ms,
        return src ? __symbol__inc_addr_samples(ms, src, evsel->core.idx, addr, sample) : 0;
 }
 
-static int symbol__account_cycles(u64 addr, u64 start,
-                                 struct symbol *sym, unsigned cycles)
+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 width = evsel__env(evsel)->br_cntr_width;
+       unsigned int off = offset * evsel->evlist->nr_br_cntr;
+       unsigned int i, mask = (1L << width) - 1;
+       u64 *branch_br_cntr = branch->br_cntr;
+
+       if (!br_cntr || !branch_br_cntr)
+               return 0;
+
+       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, 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;
@@ -337,15 +373,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;
@@ -371,7 +414,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;
@@ -412,6 +455,7 @@ static void annotated_branch__delete(struct annotated_branch *branch)
 {
        if (branch) {
                zfree(&branch->cycles_hist);
+               free(branch->br_cntr);
                free(branch);
        }
 }
index 0e52558baa01f89773da0182bd0d0532bce92454..6b3023f3b18f406cc92ad8e80f24066bb875049c 100644 (file)
@@ -14,6 +14,7 @@
 #include "spark.h"
 #include "hashmap.h"
 #include "disasm.h"
+#include "branch.h"
 
 struct hist_browser_timer;
 struct hist_entry;
@@ -288,6 +289,9 @@ struct annotated_source {
 struct annotation_line *annotated_source__get_line(struct annotated_source *src,
                                                   s64 offset);
 
+/* A branch counter once saturated */
+#define ANNOTATION__BR_CNTR_SATURATED_FLAG     (1ULL << 63)
+
 /**
  * struct annotated_branch - basic block and IPC information for a symbol.
  *
@@ -297,6 +301,7 @@ struct annotation_line *annotated_source__get_line(struct annotated_source *src,
  * @cover_insn: Number of distinct, actually executed instructions.
  * @cycles_hist: Array of cyc_hist for each instruction.
  * @max_coverage: Maximum number of covered basic block (used for block-range).
+ * @br_cntr: Array of the occurrences of events (branch counters) during a block.
  *
  * This struct is used by two different codes when the sample has branch stack
  * and cycles information.  annotation__compute_ipc() calculates average IPC
@@ -313,6 +318,7 @@ struct annotated_branch {
        unsigned int            cover_insn;
        struct cyc_hist         *cycles_hist;
        u64                     max_coverage;
+       u64                     *br_cntr;
 };
 
 struct LOCKABLE annotation {
@@ -383,7 +389,9 @@ struct annotated_branch *annotation__get_branch(struct annotation *notes);
 
 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);
 
 int hist_entry__inc_addr_samples(struct hist_entry *he, struct perf_sample *sample,
                                 struct evsel *evsel, u64 addr);
index 87704d713ff64454efcda339862a3b165b037b9d..b80c12c74bbbe66d76d47be1c53beba61566adfe 100644 (file)
@@ -34,6 +34,7 @@ struct branch_info {
        struct addr_map_symbol from;
        struct addr_map_symbol to;
        struct branch_flags    flags;
+       u64                    branch_stack_cntr;
        char                   *srcline_from;
        char                   *srcline_to;
 };
index c8c1b511f8a7475ac039f10800ab214eed48e313..0f554febf9a1782a2fb24cff1d57876e2f7b2c97 100644 (file)
@@ -2677,7 +2677,7 @@ int hists__unlink(struct hists *hists)
 
 void hist__account_cycles(struct branch_stack *bs, struct addr_location *al,
                          struct perf_sample *sample, bool nonany_branch_mode,
-                         u64 *total_cycles)
+                         u64 *total_cycles, struct evsel *evsel)
 {
        struct branch_info *bi;
        struct branch_entry *entries = perf_sample__branch_entries(sample);
@@ -2701,7 +2701,8 @@ void hist__account_cycles(struct branch_stack *bs, struct addr_location *al,
                        for (int i = bs->nr - 1; i >= 0; i--) {
                                addr_map_symbol__account_cycles(&bi[i].from,
                                        nonany_branch_mode ? NULL : prev,
-                                       bi[i].flags.cycles);
+                                       bi[i].flags.cycles, evsel,
+                                       bi[i].branch_stack_cntr);
                                prev = &bi[i].to;
 
                                if (total_cycles)
index 5273f5c370507b94512dc67662e857ef6b2e1ce4..30c13fc8cbe4ff717c8ffcc20f3deefba405c474 100644 (file)
@@ -742,7 +742,7 @@ unsigned int hists__overhead_width(struct hists *hists);
 
 void hist__account_cycles(struct branch_stack *bs, struct addr_location *al,
                          struct perf_sample *sample, bool nonany_branch_mode,
-                         u64 *total_cycles);
+                         u64 *total_cycles, struct evsel *evsel);
 
 struct option;
 int parse_filter_percentage(const struct option *opt, const char *arg, int unset);
index 706be5e4a076176fcc42b607fe55ef262bc4515d..c08774d06d141c3d0514aaf1c171203369fb52b6 100644 (file)
@@ -2141,6 +2141,7 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
        unsigned int i;
        const struct branch_stack *bs = sample->branch_stack;
        struct branch_entry *entries = perf_sample__branch_entries(sample);
+       u64 *branch_stack_cntr = sample->branch_stack_cntr;
        struct branch_info *bi = calloc(bs->nr, sizeof(struct branch_info));
 
        if (!bi)
@@ -2150,6 +2151,8 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
                ip__resolve_ams(al->thread, &bi[i].to, entries[i].to);
                ip__resolve_ams(al->thread, &bi[i].from, entries[i].from);
                bi[i].flags = entries[i].flags;
+               if (branch_stack_cntr)
+                       bi[i].branch_stack_cntr  = branch_stack_cntr[i];
        }
        return bi;
 }