Re: [PATCH] perf regs: Add alpha support
From: Magnus Lindholm
Date: Mon Oct 12 2026 - 13:45:09 EST
Hi Matt,
On Sun, Oct 11, 2026 at 10:37 PM Matt Turner <mattst88@xxxxxxxxx> wrote:
>
> Add the alpha perf register tables so that -I/--user-regs and
> --call-graph dwarf work.
>
> r9-r15 cannot be sampled and are left out of the mask. Perf register
> numbers 0-30 are the DWARF numbers. The pc is DWARF 64, the column GCC
> uses as the alternate return address column on alpha, so libdw's
> frame_nregs is 65.
>
> Assisted-by: Claude:claude-opus-5-5
> Signed-off-by: Matt Turner <mattst88@xxxxxxxxx>
> ---
> This depends on the kernel side, "alpha: add HAVE_PERF_REGS and
> HAVE_PERF_USER_STACK_DUMP support", posted as part of:
>
> https://lore.kernel.org/r/20261011-alpha-perf-regs-v3-v1-0-3354d135d972@xxxxxxxxx
>
> --call-graph dwarf also needs libdw alpha unwinding support (abi_cfi
> and frame_nregs), which I am sending to elfutils-devel separately.
> Without it, libdw rejects the thread and perf reports the error;
> -I/--user-regs work regardless.
> ---
> tools/arch/alpha/include/uapi/asm/perf_regs.h | 45 ++++++++++++++++++++++
> tools/perf/arch/alpha/include/perf_regs.h | 16 ++++++++
> tools/perf/check-headers.sh | 1 +
> tools/perf/util/dwarf-regs-arch/Build | 1 +
> tools/perf/util/dwarf-regs-arch/dwarf-regs-alpha.c | 14 +++++++
> tools/perf/util/dwarf-regs.c | 5 +++
> tools/perf/util/include/dwarf-regs.h | 2 +
> tools/perf/util/perf-regs-arch/Build | 1 +
> tools/perf/util/perf-regs-arch/perf_regs_alpha.c | 34 ++++++++++++++++
> tools/perf/util/perf_regs.c | 13 +++++++
> tools/perf/util/perf_regs.h | 5 +++
> 11 files changed, 137 insertions(+)
>
> diff --git a/tools/arch/alpha/include/uapi/asm/perf_regs.h b/tools/arch/alpha/include/uapi/asm/perf_regs.h
> new file mode 100644
> index 000000000000..da96a31b12fb
> --- /dev/null
> +++ b/tools/arch/alpha/include/uapi/asm/perf_regs.h
> @@ -0,0 +1,45 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +#ifndef _UAPI_ASM_ALPHA_PERF_REGS_H
> +#define _UAPI_ASM_ALPHA_PERF_REGS_H
> +
> +/*
> + * Indices 0-30 match the hardware (and DWARF) register numbers.
> + * r9-r15 are not saved in pt_regs and cannot be sampled.
> + */
> +enum perf_event_alpha_regs {
> + PERF_REG_ALPHA_R0,
> + PERF_REG_ALPHA_R1,
> + PERF_REG_ALPHA_R2,
> + PERF_REG_ALPHA_R3,
> + PERF_REG_ALPHA_R4,
> + PERF_REG_ALPHA_R5,
> + PERF_REG_ALPHA_R6,
> + PERF_REG_ALPHA_R7,
> + PERF_REG_ALPHA_R8,
> + PERF_REG_ALPHA_R9,
> + PERF_REG_ALPHA_R10,
> + PERF_REG_ALPHA_R11,
> + PERF_REG_ALPHA_R12,
> + PERF_REG_ALPHA_R13,
> + PERF_REG_ALPHA_R14,
> + PERF_REG_ALPHA_R15,
> + PERF_REG_ALPHA_R16,
> + PERF_REG_ALPHA_R17,
> + PERF_REG_ALPHA_R18,
> + PERF_REG_ALPHA_R19,
> + PERF_REG_ALPHA_R20,
> + PERF_REG_ALPHA_R21,
> + PERF_REG_ALPHA_R22,
> + PERF_REG_ALPHA_R23,
> + PERF_REG_ALPHA_R24,
> + PERF_REG_ALPHA_R25,
> + PERF_REG_ALPHA_R26,
> + PERF_REG_ALPHA_R27,
> + PERF_REG_ALPHA_R28,
> + PERF_REG_ALPHA_GP,
> + PERF_REG_ALPHA_SP,
> + PERF_REG_ALPHA_PC,
> + PERF_REG_ALPHA_MAX,
> +};
> +
> +#endif /* _UAPI_ASM_ALPHA_PERF_REGS_H */
> diff --git a/tools/perf/arch/alpha/include/perf_regs.h b/tools/perf/arch/alpha/include/perf_regs.h
> new file mode 100644
> index 000000000000..409f3a08af9b
> --- /dev/null
> +++ b/tools/perf/arch/alpha/include/perf_regs.h
> @@ -0,0 +1,16 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef ARCH_PERF_REGS_H
> +#define ARCH_PERF_REGS_H
> +
> +#include <stdlib.h>
> +#include <linux/types.h>
> +#include <linux/bits.h>
> +#include "../../../../arch/alpha/include/uapi/asm/perf_regs.h"
> +
> +#define PERF_REGS_MAX PERF_REG_ALPHA_MAX
> +
> +/* The kernel rejects r9-r15. */
> +#define PERF_REGS_MASK (GENMASK_ULL(PERF_REG_ALPHA_MAX - 1, 0) & \
> + ~GENMASK_ULL(PERF_REG_ALPHA_R15, PERF_REG_ALPHA_R9))
> +
> +#endif /* ARCH_PERF_REGS_H */
> diff --git a/tools/perf/check-headers.sh b/tools/perf/check-headers.sh
> index 531c0e0e84df..9baf83f79e23 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_regs.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/dwarf-regs-arch/Build b/tools/perf/util/dwarf-regs-arch/Build
> index ceb68ae86fd8..026b7b5ca895 100644
> --- a/tools/perf/util/dwarf-regs-arch/Build
> +++ b/tools/perf/util/dwarf-regs-arch/Build
> @@ -1,3 +1,4 @@
> +perf-util-$(CONFIG_LIBDW) += dwarf-regs-alpha.o
> perf-util-$(CONFIG_LIBDW) += dwarf-regs-arm64.o
> perf-util-$(CONFIG_LIBDW) += dwarf-regs-arm.o
> perf-util-$(CONFIG_LIBDW) += dwarf-regs-csky.o
> diff --git a/tools/perf/util/dwarf-regs-arch/dwarf-regs-alpha.c b/tools/perf/util/dwarf-regs-arch/dwarf-regs-alpha.c
> new file mode 100644
> index 000000000000..50fd17096597
> --- /dev/null
> +++ b/tools/perf/util/dwarf-regs-arch/dwarf-regs-alpha.c
> @@ -0,0 +1,14 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <errno.h>
> +#include <dwarf-regs.h>
> +#include "../../../arch/alpha/include/uapi/asm/perf_regs.h"
> +
> +int __get_dwarf_regnum_for_perf_regnum_alpha(int perf_regnum)
> +{
> + if (perf_regnum >= PERF_REG_ALPHA_R0 && perf_regnum <= PERF_REG_ALPHA_SP)
> + return perf_regnum;
> + if (perf_regnum == PERF_REG_ALPHA_PC)
> + return 64;
> +
> + return -ENOENT;
> +}
> diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c
> index 09aca6304002..3fd71787c658 100644
> --- a/tools/perf/util/dwarf-regs.c
> +++ b/tools/perf/util/dwarf-regs.c
> @@ -185,6 +185,8 @@ static int get_libdw_frame_nregs(unsigned int machine, unsigned int flags __mayb
> return 74;
> case EM_MIPS:
> return 71;
> + case EM_ALPHA:
> + return 65;
> default:
> return 0;
> }
> @@ -228,6 +230,9 @@ int get_dwarf_regnum_for_perf_regnum(int perf_regnum, unsigned int machine,
> case EM_MIPS:
> reg = __get_dwarf_regnum_for_perf_regnum_mips(perf_regnum);
> break;
> + case EM_ALPHA:
> + reg = __get_dwarf_regnum_for_perf_regnum_alpha(perf_regnum);
> + break;
> default:
> pr_err("ELF MACHINE %x is not supported.\n", machine);
> return -ENOENT;
> diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
> index 1da915c2683a..8d1a981ab8aa 100644
> --- a/tools/perf/util/include/dwarf-regs.h
> +++ b/tools/perf/util/include/dwarf-regs.h
> @@ -105,6 +105,8 @@ int __get_dwarf_regnum_x86_64(const char *name);
> int __get_dwarf_regnum_for_perf_regnum_i386(int perf_regnum);
> int __get_dwarf_regnum_for_perf_regnum_x86_64(int perf_regnum, int abi);
>
> +int __get_dwarf_regnum_for_perf_regnum_alpha(int perf_regnum);
> +
> int __get_dwarf_regnum_arm64(const char *name);
> int __get_dwarf_regnum_for_perf_regnum_arm(int perf_regnum);
> int __get_dwarf_regnum_for_perf_regnum_arm64(int perf_regnum);
> diff --git a/tools/perf/util/perf-regs-arch/Build b/tools/perf/util/perf-regs-arch/Build
> index be95402aa540..1fbc299775f7 100644
> --- a/tools/perf/util/perf-regs-arch/Build
> +++ b/tools/perf/util/perf-regs-arch/Build
> @@ -1,4 +1,5 @@
> perf-util-y += perf_regs_aarch64.o
> +perf-util-y += perf_regs_alpha.o
> perf-util-y += perf_regs_arm.o
> perf-util-y += perf_regs_csky.o
> perf-util-y += perf_regs_loongarch.o
> diff --git a/tools/perf/util/perf-regs-arch/perf_regs_alpha.c b/tools/perf/util/perf-regs-arch/perf_regs_alpha.c
> new file mode 100644
> index 000000000000..cb9c8a1cd74b
> --- /dev/null
> +++ b/tools/perf/util/perf-regs-arch/perf_regs_alpha.c
> @@ -0,0 +1,34 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include "../perf_regs.h"
> +#include "../../arch/alpha/include/perf_regs.h"
> +
> +static const char * const alpha_reg_names[PERF_REG_ALPHA_MAX] = {
> + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
> + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
> + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
> + "r24", "r25", "r26", "r27", "r28", "gp", "sp", "pc",
> +};
> +
> +uint64_t __perf_reg_mask_alpha(bool intr __maybe_unused)
> +{
> + return PERF_REGS_MASK;
> +}
> +
> +const char *__perf_reg_name_alpha(int id)
> +{
> + if (id < 0 || id >= PERF_REG_ALPHA_MAX)
> + return NULL;
> +
> + return alpha_reg_names[id];
> +}
> +
> +uint64_t __perf_reg_ip_alpha(void)
> +{
> + return PERF_REG_ALPHA_PC;
> +}
> +
> +uint64_t __perf_reg_sp_alpha(void)
> +{
> + return PERF_REG_ALPHA_SP;
> +}
> diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c
> index c8242b7e1db5..6eb5824f5521 100644
> --- a/tools/perf/util/perf_regs.c
> +++ b/tools/perf/util/perf_regs.c
> @@ -43,6 +43,9 @@ uint64_t perf_intr_reg_mask(uint16_t e_machine, int *abi /*inout*/)
> uint64_t mask = 0;
>
> switch (e_machine) {
> + case EM_ALPHA:
> + mask = __perf_reg_mask_alpha(/*intr=*/true);
> + break;
> case EM_ARM:
> mask = __perf_reg_mask_arm(/*intr=*/true);
> break;
> @@ -86,6 +89,9 @@ uint64_t perf_user_reg_mask(uint16_t e_machine, int *abi /*inout*/)
> uint64_t mask = 0;
>
> switch (e_machine) {
> + case EM_ALPHA:
> + mask = __perf_reg_mask_alpha(/*intr=*/false);
> + break;
> case EM_ARM:
> mask = __perf_reg_mask_arm(/*intr=*/false);
> break;
> @@ -129,6 +135,9 @@ const char *perf_reg_name(int id, uint16_t e_machine, uint32_t e_flags, int abi)
> const char *reg_name = NULL;
>
> switch (e_machine) {
> + case EM_ALPHA:
> + reg_name = __perf_reg_name_alpha(id);
> + break;
> case EM_ARM:
> reg_name = __perf_reg_name_arm(id);
> break;
> @@ -198,6 +207,8 @@ int perf_reg_value(u64 *valp, struct regs_dump *regs, int id)
> uint64_t perf_arch_reg_ip(uint16_t e_machine)
> {
> switch (e_machine) {
> + case EM_ALPHA:
> + return __perf_reg_ip_alpha();
> case EM_ARM:
> return __perf_reg_ip_arm();
> case EM_AARCH64:
> @@ -227,6 +238,8 @@ uint64_t perf_arch_reg_ip(uint16_t e_machine)
> uint64_t perf_arch_reg_sp(uint16_t e_machine)
> {
> switch (e_machine) {
> + case EM_ALPHA:
> + return __perf_reg_sp_alpha();
> case EM_ARM:
> return __perf_reg_sp_arm();
> case EM_AARCH64:
> diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h
> index c9f461b6f11f..4134e4eed802 100644
> --- a/tools/perf/util/perf_regs.h
> +++ b/tools/perf/util/perf_regs.h
> @@ -34,6 +34,11 @@ const char *__perf_reg_name_arm64(int id);
> uint64_t __perf_reg_ip_arm64(void);
> uint64_t __perf_reg_sp_arm64(void);
>
> +uint64_t __perf_reg_mask_alpha(bool intr);
> +const char *__perf_reg_name_alpha(int id);
> +uint64_t __perf_reg_ip_alpha(void);
> +uint64_t __perf_reg_sp_alpha(void);
> +
> uint64_t __perf_reg_mask_arm(bool intr);
> const char *__perf_reg_name_arm(int id);
> uint64_t __perf_reg_ip_arm(void);
>
> ---
> base-commit: bc01105ed65d15787b2eb93ac26e8624a2139e35
> change-id: 20261001-alpha-perf-tools-regs-c206a84dec7f
>
> Best regards,
> --
> Matt Turner <mattst88@xxxxxxxxx>
>
Thanks for doing this!
Register selection, recording, decoding and user stack dumps tested
on my ES40. End-to-end DWARF unwinding remains unverified because the
installed elfutils 0.195 lacks Alpha unwinding support.
Tested-by: Magnus Lindholm <linmag7@xxxxxxxxx>