[PATCH 6/6] perf tools: Support PERF_SAMPLE_BRANCH_EVENT_IDS

From: kan . liang
Date: Mon Apr 10 2023 - 16:44:21 EST


From: Kan Liang <kan.liang@xxxxxxxxxxxxxxx>

Support new sample type PERF_SAMPLE_BRANCH_EVENT_IDS.

It's used with the branch event feature together. If the legacy kernel
doesn't support either of them, switching off them together.

The sampling event may not be the event logged by a branch. Apply the
PERF_SAMPLE_BRANCH_EVENT_IDS for all events if the branch events logging
feature is detected.

Reviewed-by: Andi Kleen <ak@xxxxxxxxxxxxxxx>
Signed-off-by: Kan Liang <kan.liang@xxxxxxxxxxxxxxx>
---
tools/perf/util/branch.h | 5 +++++
tools/perf/util/evsel.c | 22 ++++++++++++++++++++--
tools/perf/util/perf_event_attr_fprintf.c | 2 +-
tools/perf/util/record.c | 13 +++++++++++++
tools/perf/util/sample.h | 1 +
tools/perf/util/session.c | 17 +++++++++++++++++
tools/perf/util/synthetic-events.c | 12 ++++++++++++
7 files changed, 69 insertions(+), 3 deletions(-)

diff --git a/tools/perf/util/branch.h b/tools/perf/util/branch.h
index 5feb79ccd698..761b686e7730 100644
--- a/tools/perf/util/branch.h
+++ b/tools/perf/util/branch.h
@@ -51,6 +51,11 @@ struct branch_stack {
struct branch_entry entries[];
};

+struct branch_event_ids {
+ u64 nr;
+ u64 ids[];
+};
+
/*
* The hw_idx is only available when PERF_SAMPLE_BRANCH_HW_INDEX is applied.
* Otherwise, the output format of a sample with branch stack is
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 1888552f41f9..91bd989c8491 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -1850,8 +1850,10 @@ static int __evsel__prepare_open(struct evsel *evsel, struct perf_cpu_map *cpus,

static void evsel__disable_missing_features(struct evsel *evsel)
{
- if (perf_missing_features.branch_event)
+ if (perf_missing_features.branch_event) {
evsel->core.attr.branch_sample_type &= ~PERF_SAMPLE_BRANCH_EVENT;
+ evsel__reset_sample_bit(evsel, BRANCH_EVENT_IDS);
+ }
if (perf_missing_features.read_lost)
evsel->core.attr.read_format &= ~PERF_FORMAT_LOST;
if (perf_missing_features.weight_struct) {
@@ -1906,7 +1908,8 @@ bool evsel__detect_missing_features(struct evsel *evsel)
* perf_event_attr interface.
*/
if (!perf_missing_features.branch_event &&
- (evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_EVENT)) {
+ ((evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_EVENT) ||
+ (evsel->core.attr.sample_type & PERF_SAMPLE_BRANCH_EVENT_IDS))) {
perf_missing_features.branch_event = true;
pr_debug2("switching off branch event support\n");
return true;
@@ -2710,6 +2713,21 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
array = (void *)array + sz;
}

+ if (type & PERF_SAMPLE_BRANCH_EVENT_IDS) {
+ const u64 max_branch_nr = UINT64_MAX / sizeof(u64);
+
+ OVERFLOW_CHECK_u64(array);
+ data->branch_event_ids = (struct branch_event_ids *)array++;
+
+ if (data->branch_event_ids->nr > max_branch_nr)
+ return -EFAULT;
+
+ sz = data->branch_event_ids->nr * sizeof(u64);
+
+ OVERFLOW_CHECK(array, sz, max_size);
+ array = (void *)array + sz;
+ }
+
return 0;
}

diff --git a/tools/perf/util/perf_event_attr_fprintf.c b/tools/perf/util/perf_event_attr_fprintf.c
index 96f0aafc962d..5eadcdaba12e 100644
--- a/tools/perf/util/perf_event_attr_fprintf.c
+++ b/tools/perf/util/perf_event_attr_fprintf.c
@@ -36,7 +36,7 @@ static void __p_sample_type(char *buf, size_t size, u64 value)
bit_name(IDENTIFIER), bit_name(REGS_INTR), bit_name(DATA_SRC),
bit_name(WEIGHT), bit_name(PHYS_ADDR), bit_name(AUX),
bit_name(CGROUP), bit_name(DATA_PAGE_SIZE), bit_name(CODE_PAGE_SIZE),
- bit_name(WEIGHT_STRUCT),
+ bit_name(WEIGHT_STRUCT), bit_name(BRANCH_EVENT_IDS),
{ .name = NULL, }
};
#undef bit_name
diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c
index 9eb5c6a08999..640ba5243209 100644
--- a/tools/perf/util/record.c
+++ b/tools/perf/util/record.c
@@ -98,6 +98,7 @@ void evlist__config(struct evlist *evlist, struct record_opts *opts, struct call
bool use_sample_identifier = false;
bool use_comm_exec;
bool sample_id = opts->sample_id;
+ bool has_branch_events = false;

if (perf_cpu_map__cpu(evlist->core.user_requested_cpus, 0).cpu < 0)
opts->no_inherit = true;
@@ -108,6 +109,8 @@ void evlist__config(struct evlist *evlist, struct record_opts *opts, struct call
evsel__config(evsel, opts, callchain);
if (evsel->tracking && use_comm_exec)
evsel->core.attr.comm_exec = 1;
+ if (evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_EVENT)
+ has_branch_events = true;
}

/* Configure leader sampling here now that the sample type is known */
@@ -139,6 +142,16 @@ void evlist__config(struct evlist *evlist, struct record_opts *opts, struct call
evsel__set_sample_id(evsel, use_sample_identifier);
}

+ if (has_branch_events) {
+ /*
+ * The sampling event may not be the event logged by a
+ * branch. Apply the BRANCH_EVENT_IDS for all events if
+ * the branch events logging feature is detected.
+ */
+ evlist__for_each_entry(evlist, evsel)
+ evsel__set_sample_bit(evsel, BRANCH_EVENT_IDS);
+ }
+
evlist__set_id_pos(evlist);
}

diff --git a/tools/perf/util/sample.h b/tools/perf/util/sample.h
index 33b08e0ac746..b0979571c8af 100644
--- a/tools/perf/util/sample.h
+++ b/tools/perf/util/sample.h
@@ -101,6 +101,7 @@ struct perf_sample {
void *raw_data;
struct ip_callchain *callchain;
struct branch_stack *branch_stack;
+ struct branch_event_ids *branch_event_ids;
struct regs_dump user_regs;
struct regs_dump intr_regs;
struct stack_dump user_stack;
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index ce6d9349ec42..cc53a4ddfe6d 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -1203,6 +1203,20 @@ static void branch_stack__printf(struct perf_sample *sample, bool callstack)
}
}

+static void branch_event_ids__printf(struct branch_event_ids *br_event)
+{
+ u64 i;
+
+ printf("%s: nr:%" PRIu64 "\n", "... branch event IDs", br_event->nr);
+
+ for (i = 0; i < br_event->nr; i++) {
+ if (br_event->ids[i] != -1ULL)
+ printf("..... %2"PRIu64": %016" PRIx64 "\n", i, br_event->ids[i]);
+ else
+ printf("..... %2"PRIu64": N/A\n", i);
+ }
+}
+
static void regs_dump__printf(u64 mask, u64 *regs, const char *arch)
{
unsigned rid, i = 0;
@@ -1364,6 +1378,9 @@ static void dump_sample(struct evsel *evsel, union perf_event *event,
if (evsel__has_br_stack(evsel))
branch_stack__printf(sample, evsel__has_branch_callstack(evsel));

+ if (sample_type & PERF_SAMPLE_BRANCH_EVENT_IDS)
+ branch_event_ids__printf(sample->branch_event_ids);
+
if (sample_type & PERF_SAMPLE_REGS_USER)
regs_user__printf(sample, arch);

diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c
index 9ab9308ee80c..f4c47979e7c1 100644
--- a/tools/perf/util/synthetic-events.c
+++ b/tools/perf/util/synthetic-events.c
@@ -1543,6 +1543,11 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
result += sample->aux_sample.size;
}

+ if (type & PERF_SAMPLE_BRANCH_EVENT_IDS) {
+ result += sizeof(u64);
+ result += sample->branch_event_ids->nr * sizeof(u64);
+ }
+
return result;
}

@@ -1757,6 +1762,13 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_fo
array = (void *)array + sz;
}

+ if (type & PERF_SAMPLE_BRANCH_EVENT_IDS) {
+ sz = sizeof(u64);
+ sz += sample->branch_event_ids->nr * sizeof(u64);
+ memcpy(array, sample->branch_event_ids, sz);
+ array = (void *)array + sz;
+ }
+
return 0;
}

--
2.35.1