* [PATCH v3 0/2] perf alpha: Decode ProfileMe samples
@ 2026-11-03 2:19 Matt Turner
2026-11-03 2:19 ` [PATCH v3 1/2] perf alpha: Decode ProfileMe raw samples Matt Turner
2026-11-03 2:19 ` [PATCH v3 2/2] perf python: Add alpha-profileme.py Matt Turner
0 siblings, 2 replies; 6+ messages in thread
From: Matt Turner @ 2026-11-03 2:19 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/20261101-alpha-profileme-v3-0-92d511b5a044@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.
To try it, with the kernel series applied:
perf record -R -e instructions:p -- ./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.
Signed-off-by: Matt Turner <mattst88@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
To: Peter Zijlstra <peterz@infradead.org>
To: Ingo Molnar <mingo@redhat.com>
To: Arnaldo Carvalho de Melo <acme@kernel.org>
To: Namhyung Kim <namhyung@kernel.org>
To: Mark Rutland <mark.rutland@arm.com>
To: Alexander Shishkin <alexander.shishkin@linux.intel.com>
To: Jiri Olsa <jolsa@kernel.org>
To: Ian Rogers <irogers@google.com>
To: Adrian Hunter <adrian.hunter@intel.com>
To: James Clark <james.clark@linaro.org>
Cc: linux-kernel@vger.kernel.org
Cc: linux-perf-users@vger.kernel.org
---
Matt Turner (2):
perf alpha: Decode ProfileMe raw samples
perf python: Add alpha-profileme.py
tools/arch/alpha/include/uapi/asm/perf_event.h | 31 +++++
tools/perf/check-headers.sh | 1 +
tools/perf/python/alpha-profileme.py | 172 +++++++++++++++++++++++++
tools/perf/util/Build | 1 +
tools/perf/util/alpha-sample-raw.c | 150 +++++++++++++++++++++
tools/perf/util/sample-raw.c | 2 +
tools/perf/util/sample-raw.h | 2 +
7 files changed, 359 insertions(+)
---
base-commit: 4626d370ae89b8af4d46a45372b269f926ec662d
change-id: 20261028-perf-alpha-profileme-8010299be323
Best regards,
--
Matt Turner <mattst88@gmail.com>
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v3 1/2] perf alpha: Decode ProfileMe raw samples 2026-11-03 2:19 [PATCH v3 0/2] perf alpha: Decode ProfileMe samples Matt Turner @ 2026-11-03 2:19 ` Matt Turner 2026-11-03 2:26 ` sashiko-bot 2026-11-03 2:19 ` [PATCH v3 2/2] perf python: Add alpha-profileme.py Matt Turner 1 sibling, 1 reply; 6+ messages in thread From: Matt Turner @ 2026-11-03 2:19 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. 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 | 150 +++++++++++++++++++++++++ tools/perf/util/sample-raw.c | 2 + tools/perf/util/sample-raw.h | 2 + 6 files changed, 187 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..e8751224748f --- /dev/null +++ b/tools/perf/util/alpha-sample-raw.c @@ -0,0 +1,150 @@ +// 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", +}; + +static const char *trap_name(__u64 i_stat, __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 (!I_STAT_TRP(i_stat) || 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, pmpc), + I_STAT_TRP(i_stat) && 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] 6+ messages in thread
* Re: [PATCH v3 1/2] perf alpha: Decode ProfileMe raw samples 2026-11-03 2:19 ` [PATCH v3 1/2] perf alpha: Decode ProfileMe raw samples Matt Turner @ 2026-11-03 2:26 ` sashiko-bot 0 siblings, 0 replies; 6+ messages in thread From: sashiko-bot @ 2026-11-03 2:26 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. 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/20261102-perf-alpha-profileme-v3-0-4bf6e1dda733@gmail.com?part=1 ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v3 2/2] perf python: Add alpha-profileme.py 2026-11-03 2:19 [PATCH v3 0/2] perf alpha: Decode ProfileMe samples Matt Turner 2026-11-03 2:19 ` [PATCH v3 1/2] perf alpha: Decode ProfileMe raw samples Matt Turner @ 2026-11-03 2:19 ` Matt Turner 2026-11-03 2:27 ` sashiko-bot 2026-11-03 15:12 ` Magnus Lindholm 1 sibling, 2 replies; 6+ messages in thread From: Matt Turner @ 2026-11-03 2:19 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, 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 | 172 +++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/tools/perf/python/alpha-profileme.py b/tools/perf/python/alpha-profileme.py new file mode 100755 index 000000000000..366b288c6e1b --- /dev/null +++ b/tools/perf/python/alpha-profileme.py @@ -0,0 +1,172 @@ +#!/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 instructions:p -- ./workload + 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 + +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 + trp = (i_stat >> 39) & 1 + if (ctl >> 26) & 1: + 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] 6+ messages in thread
* Re: [PATCH v3 2/2] perf python: Add alpha-profileme.py 2026-11-03 2:19 ` [PATCH v3 2/2] perf python: Add alpha-profileme.py Matt Turner @ 2026-11-03 2:27 ` sashiko-bot 2026-11-03 15:12 ` Magnus Lindholm 1 sibling, 0 replies; 6+ messages in thread From: sashiko-bot @ 2026-11-03 2:27 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, > 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/20261102-perf-alpha-profileme-v3-0-4bf6e1dda733@gmail.com?part=2 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3 2/2] perf python: Add alpha-profileme.py 2026-11-03 2:19 ` [PATCH v3 2/2] perf python: Add alpha-profileme.py Matt Turner 2026-11-03 2:27 ` sashiko-bot @ 2026-11-03 15:12 ` Magnus Lindholm 1 sibling, 0 replies; 6+ messages in thread From: Magnus Lindholm @ 2026-11-03 15:12 UTC (permalink / raw) To: Matt Turner Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark, linux-alpha, linux-kernel, linux-perf-users Hi Matt, On Tue, Nov 3, 2026 at 3:19 AM Matt Turner <mattst88@gmail.com> wrote: > > 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, > 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 | 172 +++++++++++++++++++++++++++++++++++ > 1 file changed, 172 insertions(+) > > diff --git a/tools/perf/python/alpha-profileme.py b/tools/perf/python/alpha-profileme.py > new file mode 100755 > index 000000000000..366b288c6e1b > --- /dev/null > +++ b/tools/perf/python/alpha-profileme.py > @@ -0,0 +1,172 @@ > +#!/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 instructions:p -- ./workload > + 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 > + > +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 > + trp = (i_stat >> 39) & 1 > + if (ctl >> 26) & 1: > + 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 > I tested alpha-profileme.py with fresh ES40 recordings. It kept window cycles and retire delay in separate tables, and it skipped the unrelated tracepoint payload in my test. I also decoded the ES40 recordings successfully on x86-64. Reviewed-by: Magnus Lindholm <linmag7@gmail.com> Tested-by: Magnus Lindholm <linmag7@gmail.com> ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-11-03 15:12 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-11-03 2:19 [PATCH v3 0/2] perf alpha: Decode ProfileMe samples Matt Turner 2026-11-03 2:19 ` [PATCH v3 1/2] perf alpha: Decode ProfileMe raw samples Matt Turner 2026-11-03 2:26 ` sashiko-bot 2026-11-03 2:19 ` [PATCH v3 2/2] perf python: Add alpha-profileme.py Matt Turner 2026-11-03 2:27 ` sashiko-bot 2026-11-03 15:12 ` Magnus Lindholm
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.