[PATCH] perf env: Find correct branch counter info on hybrid

From: kan . liang
Date: Mon Sep 09 2024 - 14:41:45 EST


From: Kan Liang <kan.liang@xxxxxxxxxxxxxxx>

No event is printed in the "Branch Counter" column on hybrid machines.

For example,
$perf record -e "{cpu_core/branch-instructions/pp,cpu_core/branches/}:S"
-j any,counter
$perf report --total-cycles

# Branch counter abbr list:
# cpu_core/branch-instructions/pp = A
# cpu_core/branches/ = B
# '-' No event occurs
# '+' Event occurrences may be lost due to branch counter saturated
#
# Sampled Cycles% Sampled Cycles Avg Cycles% Avg Cycles Branch Counter
# ............... .............. ........... .......... ..............
44.54% 727.1K 0.00% 1 |+ |+ |
36.31% 592.7K 0.00% 2 |+ |+ |
17.83% 291.1K 0.00% 1 |+ |+ |

The branch counter information (br_cntr_width and br_cntr_nr) in the
perf_env is retrieved from the CPU_PMU_CAPS. However, the CPU_PMU_CAPS
is not available on hybrid machines. Without the width information, the
number of occurrences of an event cannot be calculated.

For a hybrid machine, the caps information should be retrieved from the
PMU_CAPS, and stored in the perf_env->pmu_caps.

Add a perf_env__find_br_cntr_info() to return the correct branch counter
information from the corresponding fields.

Fixes: 6f9d8d1de2c6 ("perf script: Add branch counters")
Signed-off-by: Kan Liang <kan.liang@xxxxxxxxxxxxxxx>
---
tools/perf/builtin-script.c | 5 +++--
tools/perf/util/annotate.c | 5 +++--
tools/perf/util/env.c | 15 +++++++++++++++
tools/perf/util/env.h | 3 +++
tools/perf/util/session.c | 6 ++++--
5 files changed, 28 insertions(+), 6 deletions(-)

diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index dbe792b52c5c..a644787fa9e1 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -1241,10 +1241,11 @@ static int ip__fprintf_jump(uint64_t ip, struct branch_entry *en,
}

if (PRINT_FIELD(BRCNTR)) {
- unsigned int width = evsel__env(evsel)->br_cntr_width;
- unsigned int i = 0, j, num, mask = (1L << width) - 1;
struct evsel *pos = evsel__leader(evsel);
+ unsigned int i = 0, j, num, mask, width;

+ perf_env__find_br_cntr_info(evsel__env(evsel), NULL, &width);
+ mask = (1L << width) - 1;
printed += fprintf(fp, "br_cntr: ");
evlist__for_each_entry_from(evsel->evlist, pos) {
if (!(pos->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS))
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 4990c70b1794..c6ebde9d40a8 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -333,14 +333,15 @@ static int symbol__account_br_cntr(struct annotated_branch *branch,
{
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;
+ 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;

diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index a459374d0a1a..1edbccfc3281 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -624,3 +624,18 @@ char *perf_env__find_pmu_cap(struct perf_env *env, const char *pmu_name,
free(cap_eq);
return NULL;
}
+
+void perf_env__find_br_cntr_info(struct perf_env *env,
+ unsigned int *nr,
+ unsigned int *width)
+{
+ if (nr) {
+ *nr = env->cpu_pmu_caps ? env->br_cntr_nr :
+ env->pmu_caps->br_cntr_nr;
+ }
+
+ if (width) {
+ *width = env->cpu_pmu_caps ? env->br_cntr_width :
+ env->pmu_caps->br_cntr_width;
+ }
+}
diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h
index 2a2c37cc40b7..51b36c36019b 100644
--- a/tools/perf/util/env.h
+++ b/tools/perf/util/env.h
@@ -192,4 +192,7 @@ char *perf_env__find_pmu_cap(struct perf_env *env, const char *pmu_name,
const char *cap);

bool perf_env__has_pmu_mapping(struct perf_env *env, const char *pmu_name);
+void perf_env__find_br_cntr_info(struct perf_env *env,
+ unsigned int *nr,
+ unsigned int *width);
#endif /* __PERF_ENV_H */
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index b492300ec959..dbaf07bf6c5f 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -856,7 +856,6 @@ static void branch_stack__printf(struct perf_sample *sample,
struct branch_entry *entries = perf_sample__branch_entries(sample);
bool callstack = evsel__has_branch_callstack(evsel);
u64 *branch_stack_cntr = sample->branch_stack_cntr;
- struct perf_env *env = evsel__env(evsel);
uint64_t i;

if (!callstack) {
@@ -900,8 +899,11 @@ static void branch_stack__printf(struct perf_sample *sample,
}

if (branch_stack_cntr) {
+ unsigned int br_cntr_width, br_cntr_nr;
+
+ perf_env__find_br_cntr_info(evsel__env(evsel), &br_cntr_nr, &br_cntr_width);
printf("... branch stack counters: nr:%" PRIu64 " (counter width: %u max counter nr:%u)\n",
- sample->branch_stack->nr, env->br_cntr_width, env->br_cntr_nr);
+ sample->branch_stack->nr, br_cntr_width, br_cntr_nr);
for (i = 0; i < sample->branch_stack->nr; i++)
printf("..... %2"PRIu64": %016" PRIx64 "\n", i, branch_stack_cntr[i]);
}
--
2.38.1