[PATCH v1 30/41] perf pmu: Probe precise_ip levels

From: Ian Rogers

Date: Tue Nov 03 2026 - 18:55:03 EST


Extend perf_pmu__probe() to probe supported precise_ip levels (1..3) and
populate probed.precise_mask and probed.max_precise.

Some PMUs that do not implement hardware precise sampling (such as
software, breakpoint, or AUX trace PMUs) silently ignore attr.precise_ip
even on non-sampling events (sample_period == 0), whereas PMUs that
genuinely validate precise_ip either reject precise_ip > 0 on counting
events or are sampling-only PMUs. Check for this before probing levels
1..3 so non-precise PMUs are not misreported as supporting precise_ip,
while still detecting higher precision levels when core events are
forwarded to hardware sampling units (such as AMD IBS).

Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx>
---
tools/perf/util/pmu.c | 50 +++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/pmu.h | 4 ++++
2 files changed, 54 insertions(+)

diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 164ed926184e..1c55265be246 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -3453,6 +3453,56 @@ static void __perf_pmu__probe(struct perf_pmu *pmu, int pmu_cpu)
if (pmu->probed.can_sample)
probe_sample_types(pmu, &base_attr, pid, cpu, flags);

+ /*
+ * 3. Precise sampling support (:p, :pp, :ppp).
+ * PMUs that do not validate attr.precise_ip (such as software,
+ * breakpoint, or intel_pt) silently accept precise_ip > 0 even when
+ * sample_period == 0. A PMU that genuinely implements precise_ip
+ * either rejects precise_ip > 0 on non-sampling events or is a
+ * sampling-only PMU (such as AMD ibs_op).
+ * Note: On AMD, core events like cycles:P are forwarded to IBS and
+ * support higher precision than advertised in sysfs caps/max_precise.
+ */
+ if (pmu->probed.can_sample && !pmu->auxtrace) {
+ bool ignores_precise_ip = false;
+
+ if (pmu->probed.can_count) {
+ struct perf_event_attr c_attr = base_attr;
+ int fd;
+
+ c_attr.sample_period = 0;
+ c_attr.freq = 0;
+ c_attr.sample_type = 0;
+ c_attr.precise_ip = 1;
+ fd = sys_perf_event_open(&c_attr, pid, cpu, -1, flags);
+ if (fd >= 0) {
+ close(fd);
+ ignores_precise_ip = true;
+ }
+ }
+
+ if (!ignores_precise_ip) {
+ for (int level = 1; level <= 3; level++) {
+ struct perf_event_attr p_attr = base_attr;
+ int fd;
+
+ p_attr.sample_period = 10000;
+ p_attr.sample_type = PERF_SAMPLE_IP;
+ p_attr.precise_ip = level;
+ fd = sys_perf_event_open(&p_attr, pid, cpu, -1, flags);
+ if (fd < 0 && p_attr.exclude_kernel && p_attr.config2 == 0) {
+ p_attr.config2 = 1;
+ fd = sys_perf_event_open(&p_attr, pid, cpu, -1, flags);
+ }
+ if (fd >= 0) {
+ close(fd);
+ pmu->probed.precise_mask |= (1u << level);
+ pmu->probed.max_precise = level;
+ }
+ }
+ }
+ }
+
if (keepalive_fd >= 0)
close(keepalive_fd);
pmu->probed.checked = true;
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 658485faa05d..fea65e314547 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -238,6 +238,10 @@ struct perf_pmu {
char *probe_event;
/** @probed.open_err: errno if open_supported is false. */
int open_err;
+ /** @probed.max_precise: Maximum precise_ip level (0..3) supported. */
+ int max_precise;
+ /** @probed.precise_mask: Bitmask of supported precise_ip levels (1..3). */
+ unsigned int precise_mask;
/**
* @probed.sample_types: Bitmask of supported
* enum perf_event_sample_format flags.
--
2.56.0.385.gac671c819a-goog