Linux Perf Users
 help / color / mirror / Atom feed
* [PATCH v4 0/3] perf alpha: Decode ProfileMe samples
@ 2026-11-04 18:53 Matt Turner
  2026-11-04 18:53 ` [PATCH v4 1/3] perf alpha: Decode ProfileMe raw samples Matt Turner
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Matt Turner @ 2026-11-04 18:53 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
	Ian Rogers, Adrian Hunter, James Clark
  Cc: Magnus Lindholm, linux-alpha, linux-kernel, linux-perf-users,
	Matt Turner

The Alpha kernel series "alpha/perf: add ProfileMe sampling" adds
ProfileMe sampling to the Alpha cpu PMU. Each ProfileMe sample carries
the profiled instruction's PMPC and its I_STAT and PCTR_CTL status as
PERF_SAMPLE_RAW data, which says whether the instruction retired,
whether it trapped and why, whether it was a mispredicted branch,
stalled before map, or was killed on a wrong path, and what PCTR1
counted while it was in flight:

  https://lore.kernel.org/r/20261104-alpha-profileme-v6-0-63362e9290a3@gmail.com

This series is the perf tool side. Patch 1 decodes the raw data in perf
report -D and perf script -D, as is done for AMD IBS. It copies the new
uapi header from the kernel series into tools/arch, so it should wait
until that header is merged. Patch 2 adds a script that sums the status
by symbol or by instruction, in the style of DCPI. Patch 3 gives perf
mem a ProfileMe event, as the kernel series reports the DTB hits and
misses of loads and stores in the data source.

To try it, with the kernel series applied:

  perf record -R -e cpu/instructions,profileme/ -- ./workload
  tools/perf/python/alpha-profileme.py --pc

I tested this on an AlphaServer ES47 (EV7). Sampling a loop whose
branch tests random data reports that branch mispredicted in 21% of its
samples, and 0% with sorted data. As a normal user, perf mem record on
a loop that loads one word from each page reports the load's DTB hits
and misses.

Signed-off-by: Matt Turner <mattst88@gmail.com>
---
Changes in v4:
- Patches 1 and 2: ignore TRP, and MIS and the trap type with it, for
  an instruction killed before it was mapped, as the kernel series
  does. Thanks to Ian Rogers.
- Patch 2: record cpu/instructions,profileme/ in the example, as a
  precise event now samples only retired instructions, which leaves
  the killed% and invalid% columns at 0. Thanks to Ian Rogers.
- New patch 3: add a perf mem event for Alpha, cpu/cycles,profileme/.
  Suggested by Ian Rogers.
- Link to v3: https://patch.msgid.link/20261102-perf-alpha-profileme-v3-0-4bf6e1dda733@gmail.com

Changes in v3:
- Patch 1: silence -Wpacked around the uapi header, which breaks the
  build where __u64 is 4-byte aligned, such as sh4.
- Patch 2: only read raw data from cpu PMU events, as a tracepoint's
  payload can pass for ProfileMe status. Thanks to Magnus Lindholm.
- Patch 2: print a table for each thing PCTR1 can count, so that mixing
  events no longer averages unrelated values. Thanks to Magnus Lindholm.
- Link to v2: https://patch.msgid.link/20261030-perf-alpha-profileme-v2-0-9ce2f0b0e622@gmail.com

Changes in v2:
- Patch 1: copy the raw data out of the sample and convert it from
  little endian, so that it decodes on strict-alignment and big-endian
  hosts.
- Patches 1 and 2: follow the new 28-byte struct alpha_profileme_sample
  from kernel v2.
- Link to v1: https://patch.msgid.link/20261028-perf-alpha-profileme-v1-0-f46c1938d001@gmail.com

---
Matt Turner (3):
      perf alpha: Decode ProfileMe raw samples
      perf python: Add alpha-profileme.py
      perf alpha: Add perf mem support

 tools/arch/alpha/include/uapi/asm/perf_event.h |  31 +++++
 tools/perf/Documentation/perf-mem.txt          |   3 +
 tools/perf/arch/alpha/Build                    |   2 +-
 tools/perf/arch/alpha/util/Build               |   2 +
 tools/perf/arch/alpha/util/mem-events.c        |  17 +++
 tools/perf/arch/alpha/util/mem-events.h        |   7 +
 tools/perf/arch/alpha/util/pmu.c               |  11 ++
 tools/perf/check-headers.sh                    |   1 +
 tools/perf/python/alpha-profileme.py           | 178 +++++++++++++++++++++++++
 tools/perf/util/Build                          |   1 +
 tools/perf/util/alpha-sample-raw.c             | 156 ++++++++++++++++++++++
 tools/perf/util/sample-raw.c                   |   2 +
 tools/perf/util/sample-raw.h                   |   2 +
 13 files changed, 412 insertions(+), 1 deletion(-)
---
base-commit: 4626d370ae89b8af4d46a45372b269f926ec662d
change-id: 20261028-perf-alpha-profileme-8010299be323

Best regards,
--  
Matt Turner <mattst88@gmail.com>


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v4 1/3] perf alpha: Decode ProfileMe raw samples
  2026-11-04 18:53 [PATCH v4 0/3] perf alpha: Decode ProfileMe samples Matt Turner
@ 2026-11-04 18:53 ` Matt Turner
  2026-11-04 19:04   ` sashiko-bot
  2026-11-04 18:53 ` [PATCH v4 2/3] perf python: Add alpha-profileme.py Matt Turner
  2026-11-04 18:53 ` [PATCH v4 3/3] perf alpha: Add perf mem support Matt Turner
  2 siblings, 1 reply; 7+ messages in thread
From: Matt Turner @ 2026-11-04 18:53 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
	Ian Rogers, Adrian Hunter, James Clark
  Cc: Magnus Lindholm, linux-alpha, linux-kernel, linux-perf-users,
	Matt Turner

The Alpha cpu PMU attaches struct alpha_profileme_sample to each
ProfileMe sample as PERF_SAMPLE_RAW data: the profiled instruction's
PMPC along with the I_STAT and PCTR_CTL status that says what happened
to it.

Decode it in perf report -D and perf script -D the way AMD IBS raw data
is decoded, naming the trap from TRAP_TYPE when TRP is set and from the
PALcode entry offset in PMPC for redirect traps, which TRAP_TYPE does
not report. TRP is ignored for an instruction killed before it was
mapped, as an EV7 leaves it stale there. The payload is copied out of
the sample and converted from little endian, so that it decodes on
strict-alignment and big-endian hosts too.

Copy the uapi header that defines the struct into tools/arch and add it
to check-headers.sh.

Signed-off-by: Matt Turner <mattst88@gmail.com>
Reviewed-by: Magnus Lindholm <linmag7@gmail.com>
Tested-by: Magnus Lindholm <linmag7@gmail.com>
---
 tools/arch/alpha/include/uapi/asm/perf_event.h |  31 +++++
 tools/perf/check-headers.sh                    |   1 +
 tools/perf/util/Build                          |   1 +
 tools/perf/util/alpha-sample-raw.c             | 156 +++++++++++++++++++++++++
 tools/perf/util/sample-raw.c                   |   2 +
 tools/perf/util/sample-raw.h                   |   2 +
 6 files changed, 193 insertions(+)

diff --git a/tools/arch/alpha/include/uapi/asm/perf_event.h b/tools/arch/alpha/include/uapi/asm/perf_event.h
new file mode 100644
index 000000000000..aa0eff5a917c
--- /dev/null
+++ b/tools/arch/alpha/include/uapi/asm/perf_event.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI_ASM_ALPHA_PERF_EVENT_H
+#define _UAPI_ASM_ALPHA_PERF_EVENT_H
+
+#include <linux/types.h>
+
+/*
+ * PERF_SAMPLE_RAW payload of a ProfileMe sample from the cpu PMU.
+ *
+ * pmpc, i_stat and pctr_ctl are the values of the PMPC, I_STAT and
+ * PCTR_CTL registers when the ProfileMe window closed. i_stat holds only
+ * bits 40:30 and pctr_ctl only bits 47:0. See the EV67/EV68 Hardware
+ * Reference Manuals for the field layout.
+ *
+ * In the sample record the payload follows the 32-bit PERF_SAMPLE_RAW
+ * size, so it starts 4 bytes past an 8-byte boundary. The 64-bit fields
+ * are packed after the 32-bit version so that they are 8-byte aligned in
+ * the record and no padding follows the payload.
+ *
+ * Fields may be added to the end in later versions.
+ */
+#define ALPHA_PROFILEME_SAMPLE_VERSION	1
+
+struct alpha_profileme_sample {
+	__u32 version;
+	__u64 pmpc __attribute__((packed));
+	__u64 i_stat __attribute__((packed));
+	__u64 pctr_ctl __attribute__((packed));
+};
+
+#endif /* _UAPI_ASM_ALPHA_PERF_EVENT_H */
diff --git a/tools/perf/check-headers.sh b/tools/perf/check-headers.sh
index 531c0e0e84df..de66bb148754 100755
--- a/tools/perf/check-headers.sh
+++ b/tools/perf/check-headers.sh
@@ -36,6 +36,7 @@ declare -a FILES=(
   "arch/x86/include/asm/msr-index.h"
   "arch/x86/lib/x86-opcode-map.txt"
   "arch/x86/tools/gen-insn-attr-x86.awk"
+  "arch/alpha/include/uapi/asm/perf_event.h"
   "arch/arm/include/uapi/asm/perf_regs.h"
   "arch/arm64/include/uapi/asm/perf_regs.h"
   "arch/loongarch/include/uapi/asm/perf_regs.h"
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 2c1f880c4c47..2ab63bbe4a05 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -77,6 +77,7 @@ perf-util-y += sample.o
 perf-util-y += sample-raw.o
 perf-util-y += s390-sample-raw.o
 perf-util-y += amd-sample-raw.o
+perf-util-y += alpha-sample-raw.o
 perf-util-y += ordered-events.o
 perf-util-y += namespaces.o
 perf-util-y += comm.o
diff --git a/tools/perf/util/alpha-sample-raw.c b/tools/perf/util/alpha-sample-raw.c
new file mode 100644
index 000000000000..6ff0566799ca
--- /dev/null
+++ b/tools/perf/util/alpha-sample-raw.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Alpha specific. Provide textual annotation for ProfileMe raw sample data.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <linux/kernel.h>
+#include <linux/perf_event.h>
+
+/* -Wpacked objects to the packed fields where __u64 is 4-byte aligned. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpacked"
+#pragma GCC diagnostic ignored "-Wattributes"
+#include "../../arch/alpha/include/uapi/asm/perf_event.h"
+#pragma GCC diagnostic pop
+
+#include "evlist.h"
+#include "evsel.h"
+#include "sample-raw.h"
+#include "util/sample.h"
+
+/* I_STAT */
+#define I_STAT_OVR(x)		(((x) >> 30) & 0x7)
+#define I_STAT_ICM(x)		(((x) >> 33) & 0x1)
+#define I_STAT_TRAP_TYPE(x)	(((x) >> 34) & 0xf)
+#define I_STAT_LSO(x)		(((x) >> 38) & 0x1)
+#define I_STAT_TRP(x)		(((x) >> 39) & 0x1)
+#define I_STAT_MIS(x)		(((x) >> 40) & 0x1)
+
+/* PCTR_CTL */
+#define PCTR_CTL_TAK(x)		((x) & 0x1)
+#define PCTR_CTL_VAL(x)		(((x) >> 1) & 0x1)
+#define PCTR_CTL_SL1(x)		(((x) >> 2) & 0x3)
+#define PCTR_CTL_SL0(x)		(((x) >> 4) & 0x1)
+#define PCTR_CTL_PCTR1(x)	(((x) >> 6) & 0xfffff)
+#define PCTR_CTL_KILLED(x)	(((x) >> 26) & 0x1)
+#define PCTR_CTL_STALLED(x)	(((x) >> 27) & 0x1)
+#define PCTR_CTL_PCTR0(x)	(((x) >> 28) & 0xfffff)
+
+/* PMPC */
+#define PMPC_PAL(x)		((x) & 0x1)
+#define PMPC_PAL_OFFSET(x)	((x) & 0x7fff)
+
+static const char * const trap_type_names[16] = {
+	[0]  = "replay",
+	[2]  = "DTB double miss (3 level)",
+	[3]  = "DTB double miss (4 level)",
+	[4]  = "FP disabled",
+	[5]  = "unaligned",
+	[6]  = "DTB single miss",
+	[7]  = "Dstream fault",
+	[8]  = "OPCDEC",
+	[10] = "machine check",
+	[12] = "arithmetic",
+	[14] = "MT_FPCR",
+	[15] = "reset",
+};
+
+/* What PCTR1 counts during the window, by SL1. */
+static const char * const pctr1_names[4] = {
+	"cycles",
+	"retire delay",
+	"bcache misses",
+	"mbox replays",
+};
+
+/* TRP, and MIS and the trap type with it, are stale for a killed insn. */
+static bool trapped(__u64 i_stat, __u64 ctl)
+{
+	return I_STAT_TRP(i_stat) && !PCTR_CTL_KILLED(ctl);
+}
+
+static const char *trap_name(__u64 i_stat, __u64 ctl, __u64 pmpc)
+{
+	const char *name;
+
+	/* Redirect traps leave TRAP_TYPE alone and are named by PMPC. */
+	if (PMPC_PAL(pmpc)) {
+		switch (PMPC_PAL_OFFSET(pmpc)) {
+		case 0x481:
+			return "Istream access violation";
+		case 0x581:
+			return "ITB miss";
+		case 0x681:
+			return "interrupt";
+		default:
+			break;
+		}
+	}
+	if (!trapped(i_stat, ctl) || I_STAT_MIS(i_stat))
+		return "none";
+	name = trap_type_names[I_STAT_TRAP_TYPE(i_stat)];
+	return name ? name : "other (see PMPC)";
+}
+
+/*
+ * Copy the payload out of the sample, since nothing guarantees its
+ * alignment in the buffer, and convert it from the little-endian byte
+ * order it was recorded in.
+ */
+static bool is_profileme_sample(struct evlist *evlist, union perf_event *event,
+				struct perf_sample *sample,
+				struct alpha_profileme_sample *pm)
+{
+	struct evsel *evsel;
+
+	if (sample->raw_size < sizeof(*pm))
+		return false;
+
+	evsel = evlist__event2evsel(evlist, event);
+	if (!evsel)
+		return false;
+	if (evsel->core.attr.type != PERF_TYPE_HARDWARE &&
+	    evsel->core.attr.type != PERF_TYPE_RAW)
+		return false;
+
+	memcpy(pm, sample->raw_data, sizeof(*pm));
+	pm->version = le32_to_cpu(pm->version);
+	pm->pmpc = le64_to_cpu(pm->pmpc);
+	pm->i_stat = le64_to_cpu(pm->i_stat);
+	pm->pctr_ctl = le64_to_cpu(pm->pctr_ctl);
+
+	return pm->version >= ALPHA_PROFILEME_SAMPLE_VERSION;
+}
+
+void evlist__alpha_sample_raw(struct evlist *evlist, union perf_event *event,
+			      struct perf_sample *sample)
+{
+	struct alpha_profileme_sample pm;
+	unsigned long long i_stat, ctl, pmpc;
+
+	if (event->header.type != PERF_RECORD_SAMPLE ||
+	    !is_profileme_sample(evlist, event, sample, &pm))
+		return;
+
+	i_stat = pm.i_stat;
+	ctl = pm.pctr_ctl;
+	pmpc = pm.pmpc;
+
+	printf("ProfileMe PMPC:\t%016llx %s trap %s%s\n",
+	       pmpc, PMPC_PAL(pmpc) ? "PAL" : (pmpc >> 63 ? "kernel" : "user"),
+	       trap_name(i_stat, ctl, pmpc),
+	       trapped(i_stat, ctl) && I_STAT_MIS(i_stat) ? " mispredicted" : "");
+	printf("ProfileMe I_STAT:\t%016llx TRP %d MIS %d LSO %d TrapType %d ICM %d OVR %d\n",
+	       i_stat, (int)I_STAT_TRP(i_stat), (int)I_STAT_MIS(i_stat),
+	       (int)I_STAT_LSO(i_stat), (int)I_STAT_TRAP_TYPE(i_stat),
+	       (int)I_STAT_ICM(i_stat), (int)I_STAT_OVR(i_stat));
+	printf("ProfileMe PCTR_CTL:\t%016llx VAL %d TAK %d Stalled %d Killed %d",
+	       ctl, (int)PCTR_CTL_VAL(ctl), (int)PCTR_CTL_TAK(ctl),
+	       (int)PCTR_CTL_STALLED(ctl), (int)PCTR_CTL_KILLED(ctl));
+	printf(" SL0 %d SL1 %d PCTR0 %d PCTR1 %d (%s)\n",
+	       (int)PCTR_CTL_SL0(ctl), (int)PCTR_CTL_SL1(ctl),
+	       (int)PCTR_CTL_PCTR0(ctl), (int)PCTR_CTL_PCTR1(ctl),
+	       pctr1_names[PCTR_CTL_SL1(ctl)]);
+}
diff --git a/tools/perf/util/sample-raw.c b/tools/perf/util/sample-raw.c
index f5ae9f468983..f765429a481d 100644
--- a/tools/perf/util/sample-raw.c
+++ b/tools/perf/util/sample-raw.c
@@ -19,6 +19,8 @@ void evlist__init_trace_event_sample_raw(struct evlist *evlist, struct perf_env
 
 	if (e_machine == EM_S390) {
 		evlist__set_trace_event_sample_raw(evlist, evlist__s390_sample_raw);
+	} else if (e_machine == EM_ALPHA) {
+		evlist__set_trace_event_sample_raw(evlist, evlist__alpha_sample_raw);
 	} else if (e_machine == EM_X86_64 || e_machine == EM_386) {
 		const char *cpuid = perf_env__cpuid(env);
 
diff --git a/tools/perf/util/sample-raw.h b/tools/perf/util/sample-raw.h
index c8d38c841c8c..8df0bd3d02aa 100644
--- a/tools/perf/util/sample-raw.h
+++ b/tools/perf/util/sample-raw.h
@@ -14,6 +14,8 @@ void evlist__s390_sample_raw(struct evlist *evlist, union perf_event *event,
 bool evlist__has_amd_ibs(struct evlist *evlist);
 void evlist__amd_sample_raw(struct evlist *evlist, union perf_event *event,
 			    struct perf_sample *sample);
+void evlist__alpha_sample_raw(struct evlist *evlist, union perf_event *event,
+			      struct perf_sample *sample);
 void evlist__init_trace_event_sample_raw(struct evlist *evlist, struct perf_env *env);
 
 #endif /* __SAMPLE_RAW_H */

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v4 2/3] perf python: Add alpha-profileme.py
  2026-11-04 18:53 [PATCH v4 0/3] perf alpha: Decode ProfileMe samples Matt Turner
  2026-11-04 18:53 ` [PATCH v4 1/3] perf alpha: Decode ProfileMe raw samples Matt Turner
@ 2026-11-04 18:53 ` Matt Turner
  2026-11-04 19:08   ` sashiko-bot
  2026-11-04 18:53 ` [PATCH v4 3/3] perf alpha: Add perf mem support Matt Turner
  2 siblings, 1 reply; 7+ messages in thread
From: Matt Turner @ 2026-11-04 18:53 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
	Ian Rogers, Adrian Hunter, James Clark
  Cc: Magnus Lindholm, linux-alpha, linux-kernel, linux-perf-users,
	Matt Turner

perf report shows where Alpha ProfileMe samples land but cannot
aggregate the raw status attached to them.

Add a standalone script, using the perf Python module, that summarizes
it by symbol or, with --pc, by instruction, in the style of DCPI: the
fraction of samples killed on a wrong path, not retired, mispredicted,
taken or stalled, the mean PCTR1 window count, and trap counts by type.
Mispredict and trap types are only counted when I_STAT[TRP] is set
and the instruction was not killed before it was mapped, since they are
stale otherwise. Redirect traps are counted by their
PALcode entry offset in PMPC.

Raw data is only read from cpu PMU events, as a tracepoint's payload can
pass for ProfileMe status. Rows are kept apart by PCTR_CTL[SL1], as a
mean over two things PCTR1 can count means nothing.

On an EV7, sampling a loop whose branch tests random data reports that
branch mispredicted in 21% of its samples, and 0% with sorted data.

Signed-off-by: Matt Turner <mattst88@gmail.com>
---
 tools/perf/python/alpha-profileme.py | 178 +++++++++++++++++++++++++++++++++++
 1 file changed, 178 insertions(+)

diff --git a/tools/perf/python/alpha-profileme.py b/tools/perf/python/alpha-profileme.py
new file mode 100755
index 000000000000..da374d206187
--- /dev/null
+++ b/tools/perf/python/alpha-profileme.py
@@ -0,0 +1,178 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+"""Summarize Alpha ProfileMe samples.
+
+Aggregates the ProfileMe status that the Alpha cpu PMU attaches to each
+sample as PERF_SAMPLE_RAW data, by symbol or, with --pc, by instruction.
+
+Record with raw data, e.g.:
+  perf record -R -e cpu/instructions,profileme/ -- ./workload
+A precise event such as instructions:p samples only retired instructions,
+so its killed% and invalid% columns read 0.
+  alpha-profileme.py [-i perf.data] [--pc] [--top N]
+
+Columns:
+  samples   ProfileMe samples attributed to the row
+  killed%   profiled instruction killed before it was mapped (PM_KILLED_BM)
+  invalid%  instruction did not trap and did not retire valid (VAL clear)
+  mispr%    conditional branch mispredicted (TRP and MIS)
+  taken%    TAK set; only meaningful for rows that are conditional branches
+  stall%    instruction stalled between fetch and map (PM_STALLED)
+  pctr1     mean PCTR1 count during the window; what it counts depends on
+            the event: inum retire delay for cycles:p, window cycles for
+            instructions:p, Bcache misses for bcache-misses:p and Mbox
+            replay traps for cpu/mbox-replay/p
+  traps     trap counts by type, only for samples with TRP set
+
+TRP, and MIS and the trap type with it, are stale for a killed instruction,
+so mispr%, invalid% and traps ignore them there.
+
+Samples from events that set PCTR1 to count different things are printed
+in separate tables, as their means cannot be compared.
+"""
+from __future__ import annotations
+
+import argparse
+from collections import defaultdict
+import struct
+import sys
+from typing import Optional
+import perf
+
+TRAP_TYPES = {
+    0: "replay", 2: "dtb2-3l", 3: "dtb2-4l", 4: "fen", 5: "unalign",
+    6: "dtb1", 7: "dfault", 8: "opcdec", 10: "mchk", 12: "arith",
+    14: "mt_fpcr", 15: "reset",
+}
+REDIRECTS = {0x481: "iacv", 0x581: "itbmiss", 0x681: "intr"}
+PCTR1_NAMES = ("cycles", "retire delay", "bcache misses", "mbox replays")
+SAMPLE = struct.Struct("<IQQQ")
+
+
+class Row:
+    """ProfileMe status totals for one symbol or instruction."""
+    def __init__(self) -> None:
+        self.samples = 0
+        self.killed = 0
+        self.invalid = 0
+        self.mispredict = 0
+        self.taken = 0
+        self.stalled = 0
+        self.pctr1 = 0
+        self.traps: dict[str, int] = defaultdict(int)
+
+
+def pct(n: int, d: int) -> float:
+    """Return n as a percentage of d."""
+    return 100.0 * n / d if d else 0.0
+
+
+class ProfileMe:
+    """Aggregates ProfileMe raw sample data."""
+    def __init__(self, by_pc: bool, top: int) -> None:
+        self.by_pc = by_pc
+        self.top = top
+        # Keyed by PCTR_CTL[SL1], which sets what PCTR1 counts.
+        self.tables: dict[int, dict[str, Row]] = defaultdict(lambda: defaultdict(Row))
+        self.skipped = 0
+        self.session: Optional[perf.session] = None
+
+    def process_event(self, sample: perf.sample_event) -> None:
+        """Add one sample's ProfileMe status to its row."""
+        raw = sample.raw_buf
+        # Raw data from another PMU or a tracepoint is not ProfileMe status.
+        if (sample.evsel.type not in (perf.TYPE_HARDWARE, perf.TYPE_RAW) or
+                not raw or len(raw) < SAMPLE.size):
+            self.skipped += 1
+            return
+        version, pmpc, i_stat, ctl = SAMPLE.unpack_from(raw)
+        if version < 1:
+            self.skipped += 1
+            return
+
+        symbol = sample.symbol or "[unknown]"
+        if self.by_pc:
+            if pmpc & 1:
+                key = f"PAL+{pmpc & 0x7fff:#x}"
+            else:
+                key = f"{pmpc & ~3:#018x} {symbol}"
+        else:
+            key = f"{sample.dso or '[unknown]'} {symbol}"
+
+        r = self.tables[(ctl >> 2) & 3][key]
+        r.samples += 1
+        killed = (ctl >> 26) & 1
+        trp = (i_stat >> 39) & 1 and not killed
+        if killed:
+            r.killed += 1
+        if not trp and not (ctl >> 1) & 1:
+            r.invalid += 1
+        if ctl & 1:
+            r.taken += 1
+        if (ctl >> 27) & 1:
+            r.stalled += 1
+        r.pctr1 += (ctl >> 6) & 0xfffff
+
+        redirect = REDIRECTS.get(pmpc & 0x7fff) if pmpc & 1 else None
+        if redirect:
+            r.traps[redirect] += 1
+        elif trp:
+            if (i_stat >> 40) & 1:
+                r.mispredict += 1
+            else:
+                tt = (i_stat >> 34) & 0xf
+                r.traps[TRAP_TYPES.get(tt, "other")] += 1
+
+    def print_totals(self) -> None:
+        """Print the busiest rows of each table."""
+        total = sum(r.samples for rows in self.tables.values() for r in rows.values())
+        print(f"{total} ProfileMe samples, {self.skipped} without ProfileMe data")
+        for sl1, rows in sorted(self.tables.items()):
+            print(f"\npctr1 counts {PCTR1_NAMES[sl1]}:")
+            self.print_table(rows)
+
+    def print_table(self, rows: dict[str, Row]) -> None:
+        """Print the busiest rows of one table."""
+        print(f"{'samples':>8} {'killed%':>7} {'invalid%':>8} {'mispr%':>6} "
+              f"{'taken%':>7} {'stall%':>6} {'pctr1':>7}  {'location':<40} traps")
+        ordered = sorted(rows.items(), key=lambda kv: kv[1].samples, reverse=True)
+        for key, r in ordered[:self.top]:
+            traps = " ".join(f"{k}={v}" for k, v in
+                             sorted(r.traps.items(), key=lambda kv: -kv[1]))
+            print(f"{r.samples:8d} {pct(r.killed, r.samples):7.1f} "
+                  f"{pct(r.invalid, r.samples):8.1f} "
+                  f"{pct(r.mispredict, r.samples):6.1f} "
+                  f"{pct(r.taken, r.samples):7.1f} "
+                  f"{pct(r.stalled, r.samples):6.1f} "
+                  f"{r.pctr1 / r.samples:7.1f}  {key:<40} {traps}")
+
+    def run(self, input_file: str) -> None:
+        """Run the session."""
+        self.session = perf.session(perf.data(input_file), sample=self.process_event)
+        try:
+            self.session.process_events()
+        finally:
+            # Break the reference cycle between perf.session and self.process_event
+            # because perf.session lacks cyclic GC support (tp_traverse).
+            self.session = None
+        self.print_totals()
+
+
+def main() -> None:
+    """Main function."""
+    parser = argparse.ArgumentParser(description="Summarize Alpha ProfileMe samples")
+    parser.add_argument("-i", "--input", default="perf.data", help="Input file")
+    parser.add_argument("--pc", action="store_true", help="aggregate by instruction")
+    parser.add_argument("--top", type=int, default=30, help="rows to print")
+    args = parser.parse_args()
+
+    analyzer = ProfileMe(args.pc, args.top)
+    try:
+        analyzer.run(args.input)
+    except IOError as e:
+        print(e, file=sys.stderr)
+        sys.exit(1)
+
+
+if __name__ == "__main__":
+    main()

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v4 3/3] perf alpha: Add perf mem support
  2026-11-04 18:53 [PATCH v4 0/3] perf alpha: Decode ProfileMe samples Matt Turner
  2026-11-04 18:53 ` [PATCH v4 1/3] perf alpha: Decode ProfileMe raw samples Matt Turner
  2026-11-04 18:53 ` [PATCH v4 2/3] perf python: Add alpha-profileme.py Matt Turner
@ 2026-11-04 18:53 ` Matt Turner
  2026-11-04 19:14   ` sashiko-bot
  2 siblings, 1 reply; 7+ messages in thread
From: Matt Turner @ 2026-11-04 18:53 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
	Ian Rogers, Adrian Hunter, James Clark
  Cc: Magnus Lindholm, linux-alpha, linux-kernel, linux-perf-users,
	Matt Turner

The Alpha kernel series "alpha/perf: add ProfileMe sampling" reports the
data source of ProfileMe samples of loads and stores: whether they hit or
missed the DTB, and whether they were loads, stores or prefetches.

Give the Alpha core PMU a mem-ldst event, cpu/cycles,profileme/, as AMD
does with IBS Op. cycles:p is not used, as precise events sample only
retired instructions, and a load or store that misses the DTB is aborted
and refetched. The profileme format bit only exists on the EV67 and
later, which have ProfileMe.

Suggested-by: Ian Rogers <irogers@google.com>
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
 tools/perf/Documentation/perf-mem.txt   |  3 +++
 tools/perf/arch/alpha/Build             |  2 +-
 tools/perf/arch/alpha/util/Build        |  2 ++
 tools/perf/arch/alpha/util/mem-events.c | 17 +++++++++++++++++
 tools/perf/arch/alpha/util/mem-events.h |  7 +++++++
 tools/perf/arch/alpha/util/pmu.c        | 11 +++++++++++
 6 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/tools/perf/Documentation/perf-mem.txt b/tools/perf/Documentation/perf-mem.txt
index fe51c5e3333d..cb439940ce90 100644
--- a/tools/perf/Documentation/perf-mem.txt
+++ b/tools/perf/Documentation/perf-mem.txt
@@ -34,6 +34,9 @@ be sampled.
 
 On AMD this use IBS Op PMU to sample load-store operations.
 
+On Alpha this uses ProfileMe, on the EV67 and later, to sample all
+instructions. Loads and stores report whether they hit or missed the DTB.
+
 COMMON OPTIONS
 --------------
 -f::
diff --git a/tools/perf/arch/alpha/Build b/tools/perf/arch/alpha/Build
index 1bb8bf6d7fd4..e63eabc2c8f4 100644
--- a/tools/perf/arch/alpha/Build
+++ b/tools/perf/arch/alpha/Build
@@ -1 +1 @@
-# empty
+perf-util-y += util/
diff --git a/tools/perf/arch/alpha/util/Build b/tools/perf/arch/alpha/util/Build
new file mode 100644
index 000000000000..8d924b229470
--- /dev/null
+++ b/tools/perf/arch/alpha/util/Build
@@ -0,0 +1,2 @@
+perf-util-y += mem-events.o
+perf-util-y += pmu.o
diff --git a/tools/perf/arch/alpha/util/mem-events.c b/tools/perf/arch/alpha/util/mem-events.c
new file mode 100644
index 000000000000..d3cbca1993b4
--- /dev/null
+++ b/tools/perf/arch/alpha/util/mem-events.c
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "util/map_symbol.h"
+#include "util/mem-events.h"
+#include "mem-events.h"
+
+#define E(t, n, s, l, a) { .tag = t, .name = n, .event_name = s, .ldlat = l, .aux_event = a }
+
+/*
+ * ProfileMe samples loads and stores alike, and reports DTB hits and misses
+ * in the data source. A precise event would sample only retired
+ * instructions, which leaves out the DTB misses, so set the profileme bit.
+ */
+struct perf_mem_event perf_mem_events_alpha[PERF_MEM_EVENTS__MAX] = {
+	E(NULL,		NULL,			NULL,		false,	0),
+	E(NULL,		NULL,			NULL,		false,	0),
+	E("mem-ldst",	"%s/cycles,profileme/",	"cycles",	false,	0),
+};
diff --git a/tools/perf/arch/alpha/util/mem-events.h b/tools/perf/arch/alpha/util/mem-events.h
new file mode 100644
index 000000000000..311448cf1218
--- /dev/null
+++ b/tools/perf/arch/alpha/util/mem-events.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ALPHA_MEM_EVENTS_H
+#define _ALPHA_MEM_EVENTS_H
+
+extern struct perf_mem_event perf_mem_events_alpha[PERF_MEM_EVENTS__MAX];
+
+#endif /* _ALPHA_MEM_EVENTS_H */
diff --git a/tools/perf/arch/alpha/util/pmu.c b/tools/perf/arch/alpha/util/pmu.c
new file mode 100644
index 000000000000..d19dfc979b08
--- /dev/null
+++ b/tools/perf/arch/alpha/util/pmu.c
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "../../../util/pmu.h"
+#include "mem-events.h"
+
+void perf_pmu__arch_init(struct perf_pmu *pmu)
+{
+	/* ProfileMe is only on the EV67 and later. */
+	if (pmu->is_core && perf_pmu__has_format(pmu, "profileme"))
+		pmu->mem_events = perf_mem_events_alpha;
+}

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v4 1/3] perf alpha: Decode ProfileMe raw samples
  2026-11-04 18:53 ` [PATCH v4 1/3] perf alpha: Decode ProfileMe raw samples Matt Turner
@ 2026-11-04 19:04   ` sashiko-bot
  0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-11-04 19:04 UTC (permalink / raw)
  To: Matt Turner; +Cc: linux-perf-users

> The Alpha cpu PMU attaches struct alpha_profileme_sample to each
> ProfileMe sample as PERF_SAMPLE_RAW data: the profiled instruction's
> PMPC along with the I_STAT and PCTR_CTL status that says what happened
> to it.
> 
> Decode it in perf report -D and perf script -D the way AMD IBS raw data
> is decoded, naming the trap from TRAP_TYPE when TRP is set and from the
> PALcode entry offset in PMPC for redirect traps, which TRAP_TYPE does
> not report. TRP is ignored for an instruction killed before it was
> mapped, as an EV7 leaves it stale there. The payload is copied out of
> the sample and converted from little endian, so that it decodes on
> strict-alignment and big-endian hosts too.
> 
> Copy the uapi header that defines the struct into tools/arch and add it
> to check-headers.sh.
> 
> Signed-off-by: Matt Turner <mattst88@gmail.com>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261104-perf-alpha-profileme-v4-0-1771ea02950e@gmail.com?part=1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4 2/3] perf python: Add alpha-profileme.py
  2026-11-04 18:53 ` [PATCH v4 2/3] perf python: Add alpha-profileme.py Matt Turner
@ 2026-11-04 19:08   ` sashiko-bot
  0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-11-04 19:08 UTC (permalink / raw)
  To: Matt Turner; +Cc: linux-perf-users

> perf report shows where Alpha ProfileMe samples land but cannot
> aggregate the raw status attached to them.
> 
> Add a standalone script, using the perf Python module, that summarizes
> it by symbol or, with --pc, by instruction, in the style of DCPI: the
> fraction of samples killed on a wrong path, not retired, mispredicted,
> taken or stalled, the mean PCTR1 window count, and trap counts by type.
> Mispredict and trap types are only counted when I_STAT[TRP] is set
> and the instruction was not killed before it was mapped, since they are
> stale otherwise. Redirect traps are counted by their
> PALcode entry offset in PMPC.
> 
> Raw data is only read from cpu PMU events, as a tracepoint's payload can
> pass for ProfileMe status. Rows are kept apart by PCTR_CTL[SL1], as a
> mean over two things PCTR1 can count means nothing.
> 
> On an EV7, sampling a loop whose branch tests random data reports that
> branch mispredicted in 21% of its samples, and 0% with sorted data.
> 
> Signed-off-by: Matt Turner <mattst88@gmail.com>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261104-perf-alpha-profileme-v4-0-1771ea02950e@gmail.com?part=2


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4 3/3] perf alpha: Add perf mem support
  2026-11-04 18:53 ` [PATCH v4 3/3] perf alpha: Add perf mem support Matt Turner
@ 2026-11-04 19:14   ` sashiko-bot
  0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-11-04 19:14 UTC (permalink / raw)
  To: Matt Turner; +Cc: linux-perf-users

> The Alpha kernel series "alpha/perf: add ProfileMe sampling" reports the
> data source of ProfileMe samples of loads and stores: whether they hit or
> missed the DTB, and whether they were loads, stores or prefetches.
> 
> Give the Alpha core PMU a mem-ldst event, cpu/cycles,profileme/, as AMD
> does with IBS Op. cycles:p is not used, as precise events sample only
> retired instructions, and a load or store that misses the DTB is aborted
> and refetched. The profileme format bit only exists on the EV67 and
> later, which have ProfileMe.
> 
> Suggested-by: Ian Rogers <irogers@google.com>
> Assisted-by: Claude:claude-opus-5-5
> Signed-off-by: Matt Turner <mattst88@gmail.com>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261104-perf-alpha-profileme-v4-0-1771ea02950e@gmail.com?part=3


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-11-04 19:14 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-04 18:53 [PATCH v4 0/3] perf alpha: Decode ProfileMe samples Matt Turner
2026-11-04 18:53 ` [PATCH v4 1/3] perf alpha: Decode ProfileMe raw samples Matt Turner
2026-11-04 19:04   ` sashiko-bot
2026-11-04 18:53 ` [PATCH v4 2/3] perf python: Add alpha-profileme.py Matt Turner
2026-11-04 19:08   ` sashiko-bot
2026-11-04 18:53 ` [PATCH v4 3/3] perf alpha: Add perf mem support Matt Turner
2026-11-04 19:14   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox