[6/6] perf tools: Support PERF_SAMPLE_BRANCH_EVENT_IDS

Message ID 20230410204352.1098067-6-kan.liang@linux.intel.com
State New
Headers
Series [1/6] perf/x86/intel: Add Grand Ridge and Sierra Forest |

Commit Message

Liang, Kan April 10, 2023, 8:43 p.m. UTC
  From: Kan Liang <kan.liang@linux.intel.com>

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@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
---
 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(-)
  

Patch

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;
 }