[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <CAP-5=fWMvuMXU405nu8hsysnq3id_3A1Uj4H5mTCDY3Chsfe-w@mail.gmail.com>
Date: Fri, 23 Jan 2026 16:39:49 -0800
From: Ian Rogers <irogers@...gle.com>
To: Dapeng Mi <dapeng1.mi@...ux.intel.com>
Cc: Peter Zijlstra <peterz@...radead.org>, Ingo Molnar <mingo@...hat.com>,
Arnaldo Carvalho de Melo <acme@...nel.org>, Namhyung Kim <namhyung@...nel.org>,
Adrian Hunter <adrian.hunter@...el.com>,
Alexander Shishkin <alexander.shishkin@...ux.intel.com>, linux-perf-users@...r.kernel.org,
linux-kernel@...r.kernel.org, Zide Chen <zide.chen@...el.com>,
Falcon Thomas <thomas.falcon@...el.com>, Dapeng Mi <dapeng1.mi@...el.com>,
Xudong Hao <xudong.hao@...el.com>
Subject: Re: [RFC Patch] perf regs: Remove __weak attribute from perf-regs functions
On Fri, Jan 23, 2026 at 1:13 AM Dapeng Mi <dapeng1.mi@...ux.intel.com> wrote:
>
> Currently, some architecture-specific perf-regs functions, such as
> arch__intr_reg_mask() and arch__user_reg_mask(), are defined with the
> __weak attribute. This approach ensures that only functions matching
> the architecture of the build/run host are compiled and executed,
> reducing build time and binary size.
>
> However, this __weak attribute restricts these functions to be called
> only on the same architecture, preventing cross-architecture
> functionality. For example, a perf.data file captured on x86 cannot be
> parsed on an ARM platform.
>
> To address this limitation, this patch removes the __weak attribute from
> these perf-regs functions. The architecture-specific code is moved from
> the arch/ directory to the util/perf-regs-arch/ directory. The
> appropriate architectural functions are then called based on the EM_HOST.
>
> No functional changes are intended.
Thanks Dapeng. So I think in the cases in this patch the calls are
made only only with e_machine == EM_HOST, and so using the arch
directory is ok but I prefer how you've changed it here. My concern is
cases like:
https://lore.kernel.org/lkml/20260124001611.1332019-2-irogers@google.com/
where we have an arch function for no particularly good reason and
that means that the instruction length is only decoded when running
perf script on an x86 for a perf.data file from an x86. The
differences to the cases here are that we're dealing more than with
just e_machine == EM_HOST as perf script can run on any perf.data
file.
> Suggested-by: Ian Rogers <irogers@...gle.com>
> Signed-off-by: Dapeng Mi <dapeng1.mi@...ux.intel.com>
> ---
>
> This patch is based on Ian's 2 patchset,
> https://lore.kernel.org/lkml/20260117052849.2205545-1-irogers@google.com/
> +
> https://lore.kernel.org/all/20260121021735.3625244-1-irogers@google.com/
>
> Ian, is this change what you expected?
>
> Although the __weak functions like arch__intr_reg_mask() and
> arch__user_reg_mask() have been removed and replaced with
> perf_intr_reg_mask() and perf_user_reg_mask() functions that call
> different architecture-specific sub-functions based on the em_machine
> value, there are still some challenges:
>
> 1. The sub-functions perf_intr_reg_mask_*() and perf_user_reg_mask_*()
> are still architecture-dependent and require calling perf_event_open()
> syscall to get the register bitmaps from the kernel. It remains
> impossible to obtain the bitmaps for a different architecture (e.g.,
> getting x86 register bitmaps on an ARM platform). While the code can be
> executed, the returned results may be incorrect.
I prefer this but I think these functions should still only be called
during the set up for the perf_event_open. We shouldn't call these
functions when working with a perf.data file, or a session, for
example.
> 2. Since all variants of perf_intr_reg_mask_*() and perf_user_reg_mask_*()
> sub-functions are built, it introduces build challenges.
>
> For example, there are duplicate macro definitions, such as the
> PERF_REG_EXTENDED_MASK macro, which is defined for both arm64 and
> x86 architectures. To fix the build errors, the following code changes
> were made (which may seem more like a hack).
>
> diff --git a/tools/arch/arm64/include/uapi/asm/perf_regs.h b/tools/arch/arm64/include/uapi/asm/perf_regs.h
> index 86e556429e0e..43e8e08f52ed 100644
> --- a/tools/arch/arm64/include/uapi/asm/perf_regs.h
> +++ b/tools/arch/arm64/include/uapi/asm/perf_regs.h
> @@ -43,6 +43,6 @@ enum perf_event_arm_regs {
> PERF_REG_ARM64_EXTENDED_MAX
> };
>
> -#define PERF_REG_EXTENDED_MASK (1ULL << PERF_REG_ARM64_VG)
> +#define PERF_ARM64_REG_EXTENDED_MASK (1ULL << PERF_REG_ARM64_VG)
I think avoiding the conflicts by adding the architecture name is correct.
> Additionally, there could be architecture-specific instructions called,
> such as the "mfspr" instruction on PowerPC, which causes build errors on
> other platforms like x86.
Right, the mfspr is used to add additional registers into the mask and
if we're not on a powerpc and can't run it we should assume something
conservative which your change will achieve. Fwiw, I tried to see if
similar information was available from say the ELF header, but I
couldn't see it.
With your SIMD changes and common functions you can refactor the
function signature to take the e_machine and also the perf ABI enum
value, then the mapping of either an XMM register string or SSP should
be possible by varying the ABI enum value. You'll also know to fix up
all the callers on the reading perf.data side to pass the enum value
as the code won't compile with the missing parameter.
> tools/arch/arm64/include/uapi/asm/perf_regs.h | 2 +-
> tools/perf/arch/arm/util/Build | 2 -
> tools/perf/arch/arm/util/perf_regs.c | 13 -
> tools/perf/arch/arm64/util/Build | 1 -
> tools/perf/arch/arm64/util/perf_regs.c | 141 ---------
> tools/perf/arch/csky/Build | 1 -
> tools/perf/arch/csky/util/Build | 1 -
> tools/perf/arch/csky/util/perf_regs.c | 13 -
> tools/perf/arch/loongarch/util/Build | 1 -
> tools/perf/arch/loongarch/util/perf_regs.c | 13 -
> tools/perf/arch/mips/util/Build | 1 -
> tools/perf/arch/mips/util/perf_regs.c | 13 -
> tools/perf/arch/powerpc/util/Build | 1 -
> tools/perf/arch/powerpc/util/perf_regs.c | 172 -----------
> tools/perf/arch/riscv/include/perf_regs.h | 7 +
> tools/perf/arch/riscv/util/Build | 1 -
> tools/perf/arch/riscv/util/perf_regs.c | 13 -
> tools/perf/arch/s390/util/Build | 1 -
> tools/perf/arch/s390/util/perf_regs.c | 13 -
> tools/perf/arch/x86/util/Build | 1 -
> tools/perf/arch/x86/util/perf_regs.c | 283 -----------------
> tools/perf/util/evsel.c | 4 +-
> tools/perf/util/parse-regs-options.c | 2 +-
> .../util/perf-regs-arch/perf_regs_aarch64.c | 140 ++++++++-
> .../perf/util/perf-regs-arch/perf_regs_arm.c | 10 +-
> .../perf/util/perf-regs-arch/perf_regs_csky.c | 10 +-
> .../util/perf-regs-arch/perf_regs_loongarch.c | 10 +-
> .../perf/util/perf-regs-arch/perf_regs_mips.c | 10 +-
> .../util/perf-regs-arch/perf_regs_powerpc.c | 179 ++++++++++-
> .../util/perf-regs-arch/perf_regs_riscv.c | 10 +-
> .../perf/util/perf-regs-arch/perf_regs_s390.c | 10 +-
> .../perf/util/perf-regs-arch/perf_regs_x86.c | 284 +++++++++++++++++-
> tools/perf/util/perf_regs.c | 103 ++++++-
> tools/perf/util/perf_regs.h | 27 +-
> tools/perf/util/probe-file.c | 3 +-
> 35 files changed, 778 insertions(+), 718 deletions(-)
> delete mode 100644 tools/perf/arch/arm/util/perf_regs.c
> delete mode 100644 tools/perf/arch/arm64/util/perf_regs.c
> delete mode 100644 tools/perf/arch/csky/Build
> delete mode 100644 tools/perf/arch/csky/util/Build
> delete mode 100644 tools/perf/arch/csky/util/perf_regs.c
> delete mode 100644 tools/perf/arch/loongarch/util/perf_regs.c
> delete mode 100644 tools/perf/arch/mips/util/perf_regs.c
> delete mode 100644 tools/perf/arch/powerpc/util/perf_regs.c
> delete mode 100644 tools/perf/arch/riscv/util/perf_regs.c
> delete mode 100644 tools/perf/arch/s390/util/perf_regs.c
> delete mode 100644 tools/perf/arch/x86/util/perf_regs.c
>
> diff --git a/tools/arch/arm64/include/uapi/asm/perf_regs.h b/tools/arch/arm64/include/uapi/asm/perf_regs.h
> index 86e556429e0e..43e8e08f52ed 100644
> --- a/tools/arch/arm64/include/uapi/asm/perf_regs.h
> +++ b/tools/arch/arm64/include/uapi/asm/perf_regs.h
> @@ -43,6 +43,6 @@ enum perf_event_arm_regs {
> PERF_REG_ARM64_EXTENDED_MAX
> };
>
> -#define PERF_REG_EXTENDED_MASK (1ULL << PERF_REG_ARM64_VG)
> +#define PERF_ARM64_REG_EXTENDED_MASK (1ULL << PERF_REG_ARM64_VG)
>
> #endif /* _ASM_ARM64_PERF_REGS_H */
> diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
> index 3291f893b943..b94bf3c5279a 100644
> --- a/tools/perf/arch/arm/util/Build
> +++ b/tools/perf/arch/arm/util/Build
> @@ -1,5 +1,3 @@
> -perf-util-y += perf_regs.o
> -
> perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
>
> perf-util-y += pmu.o auxtrace.o cs-etm.o
> diff --git a/tools/perf/arch/arm/util/perf_regs.c b/tools/perf/arch/arm/util/perf_regs.c
> deleted file mode 100644
> index 03a5bc0cf64c..000000000000
> --- a/tools/perf/arch/arm/util/perf_regs.c
> +++ /dev/null
> @@ -1,13 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0
> -#include "perf_regs.h"
> -#include "../../../util/perf_regs.h"
> -
> -uint64_t arch__intr_reg_mask(void)
> -{
> - return PERF_REGS_MASK;
> -}
> -
> -uint64_t arch__user_reg_mask(void)
> -{
> - return PERF_REGS_MASK;
> -}
> diff --git a/tools/perf/arch/arm64/util/Build b/tools/perf/arch/arm64/util/Build
> index 0177af19cc00..bc12c35d06c8 100644
> --- a/tools/perf/arch/arm64/util/Build
> +++ b/tools/perf/arch/arm64/util/Build
> @@ -8,6 +8,5 @@ perf-util-y += header.o
> perf-util-y += hisi-ptt.o
> perf-util-y += machine.o
> perf-util-y += mem-events.o
> -perf-util-y += perf_regs.o
> perf-util-y += pmu.o
> perf-util-y += tsc.o
> diff --git a/tools/perf/arch/arm64/util/perf_regs.c b/tools/perf/arch/arm64/util/perf_regs.c
> deleted file mode 100644
> index 9bb768e1bea1..000000000000
> --- a/tools/perf/arch/arm64/util/perf_regs.c
> +++ /dev/null
> @@ -1,141 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0
> -#include <errno.h>
> -#include <regex.h>
> -#include <string.h>
> -#include <sys/auxv.h>
> -#include <linux/kernel.h>
> -#include <linux/zalloc.h>
> -
> -#include "perf_regs.h"
> -#include "../../../perf-sys.h"
> -#include "../../../util/debug.h"
> -#include "../../../util/event.h"
> -#include "../../../util/perf_regs.h"
> -
> -#define SMPL_REG_MASK(b) (1ULL << (b))
> -
> -#ifndef HWCAP_SVE
> -#define HWCAP_SVE (1 << 22)
> -#endif
> -
> -/* %xNUM */
> -#define SDT_OP_REGEX1 "^(x[1-2]?[0-9]|3[0-1])$"
> -
> -/* [sp], [sp, NUM] */
> -#define SDT_OP_REGEX2 "^\\[sp(, )?([0-9]+)?\\]$"
> -
> -static regex_t sdt_op_regex1, sdt_op_regex2;
> -
> -static int sdt_init_op_regex(void)
> -{
> - static int initialized;
> - int ret = 0;
> -
> - if (initialized)
> - return 0;
> -
> - ret = regcomp(&sdt_op_regex1, SDT_OP_REGEX1, REG_EXTENDED);
> - if (ret)
> - goto error;
> -
> - ret = regcomp(&sdt_op_regex2, SDT_OP_REGEX2, REG_EXTENDED);
> - if (ret)
> - goto free_regex1;
> -
> - initialized = 1;
> - return 0;
> -
> -free_regex1:
> - regfree(&sdt_op_regex1);
> -error:
> - pr_debug4("Regex compilation error.\n");
> - return ret;
> -}
> -
> -/*
> - * SDT marker arguments on Arm64 uses %xREG or [sp, NUM], currently
> - * support these two formats.
> - */
> -int arch_sdt_arg_parse_op(char *old_op, char **new_op)
> -{
> - int ret, new_len;
> - regmatch_t rm[5];
> -
> - ret = sdt_init_op_regex();
> - if (ret < 0)
> - return ret;
> -
> - if (!regexec(&sdt_op_regex1, old_op, 3, rm, 0)) {
> - /* Extract xNUM */
> - new_len = 2; /* % NULL */
> - new_len += (int)(rm[1].rm_eo - rm[1].rm_so);
> -
> - *new_op = zalloc(new_len);
> - if (!*new_op)
> - return -ENOMEM;
> -
> - scnprintf(*new_op, new_len, "%%%.*s",
> - (int)(rm[1].rm_eo - rm[1].rm_so), old_op + rm[1].rm_so);
> - } else if (!regexec(&sdt_op_regex2, old_op, 5, rm, 0)) {
> - /* [sp], [sp, NUM] or [sp,NUM] */
> - new_len = 7; /* + ( % s p ) NULL */
> -
> - /* If the argument is [sp], need to fill offset '0' */
> - if (rm[2].rm_so == -1)
> - new_len += 1;
> - else
> - new_len += (int)(rm[2].rm_eo - rm[2].rm_so);
> -
> - *new_op = zalloc(new_len);
> - if (!*new_op)
> - return -ENOMEM;
> -
> - if (rm[2].rm_so == -1)
> - scnprintf(*new_op, new_len, "+0(%%sp)");
> - else
> - scnprintf(*new_op, new_len, "+%.*s(%%sp)",
> - (int)(rm[2].rm_eo - rm[2].rm_so),
> - old_op + rm[2].rm_so);
> - } else {
> - pr_debug4("Skipping unsupported SDT argument: %s\n", old_op);
> - return SDT_ARG_SKIP;
> - }
> -
> - return SDT_ARG_VALID;
> -}
I think it would be nice for the SDT clean up to be a distinct patch
from the arch__user_reg_mask/arch__intr_reg_mask clean up. If there
are regressions it will be easier to bisect.
Thanks,
Ian
> -
> -uint64_t arch__intr_reg_mask(void)
> -{
> - return PERF_REGS_MASK;
> -}
> -
> -uint64_t arch__user_reg_mask(void)
> -{
> - struct perf_event_attr attr = {
> - .type = PERF_TYPE_HARDWARE,
> - .config = PERF_COUNT_HW_CPU_CYCLES,
> - .sample_type = PERF_SAMPLE_REGS_USER,
> - .disabled = 1,
> - .exclude_kernel = 1,
> - .sample_period = 1,
> - .sample_regs_user = PERF_REGS_MASK
> - };
> - int fd;
> -
> - if (getauxval(AT_HWCAP) & HWCAP_SVE)
> - attr.sample_regs_user |= SMPL_REG_MASK(PERF_REG_ARM64_VG);
> -
> - /*
> - * Check if the pmu supports perf extended regs, before
> - * returning the register mask to sample.
> - */
> - if (attr.sample_regs_user != PERF_REGS_MASK) {
> - event_attr_init(&attr);
> - fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
> - if (fd != -1) {
> - close(fd);
> - return attr.sample_regs_user;
> - }
> - }
> - return PERF_REGS_MASK;
> -}
> diff --git a/tools/perf/arch/csky/Build b/tools/perf/arch/csky/Build
> deleted file mode 100644
> index e63eabc2c8f4..000000000000
> --- a/tools/perf/arch/csky/Build
> +++ /dev/null
> @@ -1 +0,0 @@
> -perf-util-y += util/
> diff --git a/tools/perf/arch/csky/util/Build b/tools/perf/arch/csky/util/Build
> deleted file mode 100644
> index 6b2d0e021b11..000000000000
> --- a/tools/perf/arch/csky/util/Build
> +++ /dev/null
> @@ -1 +0,0 @@
> -perf-util-y += perf_regs.o
> diff --git a/tools/perf/arch/csky/util/perf_regs.c b/tools/perf/arch/csky/util/perf_regs.c
> deleted file mode 100644
> index 2cf7a54106e0..000000000000
> --- a/tools/perf/arch/csky/util/perf_regs.c
> +++ /dev/null
> @@ -1,13 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0
> -#include "perf_regs.h"
> -#include "../../util/perf_regs.h"
> -
> -uint64_t arch__intr_reg_mask(void)
> -{
> - return PERF_REGS_MASK;
> -}
> -
> -uint64_t arch__user_reg_mask(void)
> -{
> - return PERF_REGS_MASK;
> -}
> diff --git a/tools/perf/arch/loongarch/util/Build b/tools/perf/arch/loongarch/util/Build
> index 0aa31986ecb5..0c958c8e0718 100644
> --- a/tools/perf/arch/loongarch/util/Build
> +++ b/tools/perf/arch/loongarch/util/Build
> @@ -1,5 +1,4 @@
> perf-util-y += header.o
> -perf-util-y += perf_regs.o
>
> perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
> perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> diff --git a/tools/perf/arch/loongarch/util/perf_regs.c b/tools/perf/arch/loongarch/util/perf_regs.c
> deleted file mode 100644
> index 03a5bc0cf64c..000000000000
> --- a/tools/perf/arch/loongarch/util/perf_regs.c
> +++ /dev/null
> @@ -1,13 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0
> -#include "perf_regs.h"
> -#include "../../../util/perf_regs.h"
> -
> -uint64_t arch__intr_reg_mask(void)
> -{
> - return PERF_REGS_MASK;
> -}
> -
> -uint64_t arch__user_reg_mask(void)
> -{
> - return PERF_REGS_MASK;
> -}
> diff --git a/tools/perf/arch/mips/util/Build b/tools/perf/arch/mips/util/Build
> index 691fa2051958..818b808a8247 100644
> --- a/tools/perf/arch/mips/util/Build
> +++ b/tools/perf/arch/mips/util/Build
> @@ -1,2 +1 @@
> -perf-util-y += perf_regs.o
> perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
> diff --git a/tools/perf/arch/mips/util/perf_regs.c b/tools/perf/arch/mips/util/perf_regs.c
> deleted file mode 100644
> index 2cf7a54106e0..000000000000
> --- a/tools/perf/arch/mips/util/perf_regs.c
> +++ /dev/null
> @@ -1,13 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0
> -#include "perf_regs.h"
> -#include "../../util/perf_regs.h"
> -
> -uint64_t arch__intr_reg_mask(void)
> -{
> - return PERF_REGS_MASK;
> -}
> -
> -uint64_t arch__user_reg_mask(void)
> -{
> - return PERF_REGS_MASK;
> -}
> diff --git a/tools/perf/arch/powerpc/util/Build b/tools/perf/arch/powerpc/util/Build
> index 5fd28ec713a4..43c3e7c450a3 100644
> --- a/tools/perf/arch/powerpc/util/Build
> +++ b/tools/perf/arch/powerpc/util/Build
> @@ -1,6 +1,5 @@
> perf-util-y += header.o
> perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
> -perf-util-y += perf_regs.o
> perf-util-y += mem-events.o
> perf-util-y += pmu.o
> perf-util-y += sym-handling.o
> diff --git a/tools/perf/arch/powerpc/util/perf_regs.c b/tools/perf/arch/powerpc/util/perf_regs.c
> deleted file mode 100644
> index 779073f7e992..000000000000
> --- a/tools/perf/arch/powerpc/util/perf_regs.c
> +++ /dev/null
> @@ -1,172 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0
> -#include <errno.h>
> -#include <string.h>
> -#include <regex.h>
> -#include <linux/zalloc.h>
> -
> -#include "perf_regs.h"
> -#include "../../../util/perf_regs.h"
> -#include "../../../util/debug.h"
> -#include "../../../util/event.h"
> -#include "../../../util/header.h"
> -#include "../../../perf-sys.h"
> -#include "utils_header.h"
> -
> -#include <linux/kernel.h>
> -
> -#define PVR_POWER9 0x004E
> -#define PVR_POWER10 0x0080
> -#define PVR_POWER11 0x0082
> -
> -/* REG or %rREG */
> -#define SDT_OP_REGEX1 "^(%r)?([1-2]?[0-9]|3[0-1])$"
> -
> -/* -NUM(REG) or NUM(REG) or -NUM(%rREG) or NUM(%rREG) */
> -#define SDT_OP_REGEX2 "^(\\-)?([0-9]+)\\((%r)?([1-2]?[0-9]|3[0-1])\\)$"
> -
> -static regex_t sdt_op_regex1, sdt_op_regex2;
> -
> -static int sdt_init_op_regex(void)
> -{
> - static int initialized;
> - int ret = 0;
> -
> - if (initialized)
> - return 0;
> -
> - ret = regcomp(&sdt_op_regex1, SDT_OP_REGEX1, REG_EXTENDED);
> - if (ret)
> - goto error;
> -
> - ret = regcomp(&sdt_op_regex2, SDT_OP_REGEX2, REG_EXTENDED);
> - if (ret)
> - goto free_regex1;
> -
> - initialized = 1;
> - return 0;
> -
> -free_regex1:
> - regfree(&sdt_op_regex1);
> -error:
> - pr_debug4("Regex compilation error.\n");
> - return ret;
> -}
> -
> -/*
> - * Parse OP and convert it into uprobe format, which is, +/-NUM(%gprREG).
> - * Possible variants of OP are:
> - * Format Example
> - * -------------------------
> - * NUM(REG) 48(18)
> - * -NUM(REG) -48(18)
> - * NUM(%rREG) 48(%r18)
> - * -NUM(%rREG) -48(%r18)
> - * REG 18
> - * %rREG %r18
> - * iNUM i0
> - * i-NUM i-1
> - *
> - * SDT marker arguments on Powerpc uses %rREG form with -mregnames flag
> - * and REG form with -mno-regnames. Here REG is general purpose register,
> - * which is in 0 to 31 range.
> - */
> -int arch_sdt_arg_parse_op(char *old_op, char **new_op)
> -{
> - int ret, new_len;
> - regmatch_t rm[5];
> - char prefix;
> -
> - /* Constant argument. Uprobe does not support it */
> - if (old_op[0] == 'i') {
> - pr_debug4("Skipping unsupported SDT argument: %s\n", old_op);
> - return SDT_ARG_SKIP;
> - }
> -
> - ret = sdt_init_op_regex();
> - if (ret < 0)
> - return ret;
> -
> - if (!regexec(&sdt_op_regex1, old_op, 3, rm, 0)) {
> - /* REG or %rREG --> %gprREG */
> -
> - new_len = 5; /* % g p r NULL */
> - new_len += (int)(rm[2].rm_eo - rm[2].rm_so);
> -
> - *new_op = zalloc(new_len);
> - if (!*new_op)
> - return -ENOMEM;
> -
> - scnprintf(*new_op, new_len, "%%gpr%.*s",
> - (int)(rm[2].rm_eo - rm[2].rm_so), old_op + rm[2].rm_so);
> - } else if (!regexec(&sdt_op_regex2, old_op, 5, rm, 0)) {
> - /*
> - * -NUM(REG) or NUM(REG) or -NUM(%rREG) or NUM(%rREG) -->
> - * +/-NUM(%gprREG)
> - */
> - prefix = (rm[1].rm_so == -1) ? '+' : '-';
> -
> - new_len = 8; /* +/- ( % g p r ) NULL */
> - new_len += (int)(rm[2].rm_eo - rm[2].rm_so);
> - new_len += (int)(rm[4].rm_eo - rm[4].rm_so);
> -
> - *new_op = zalloc(new_len);
> - if (!*new_op)
> - return -ENOMEM;
> -
> - scnprintf(*new_op, new_len, "%c%.*s(%%gpr%.*s)", prefix,
> - (int)(rm[2].rm_eo - rm[2].rm_so), old_op + rm[2].rm_so,
> - (int)(rm[4].rm_eo - rm[4].rm_so), old_op + rm[4].rm_so);
> - } else {
> - pr_debug4("Skipping unsupported SDT argument: %s\n", old_op);
> - return SDT_ARG_SKIP;
> - }
> -
> - return SDT_ARG_VALID;
> -}
> -
> -uint64_t arch__intr_reg_mask(void)
> -{
> - struct perf_event_attr attr = {
> - .type = PERF_TYPE_HARDWARE,
> - .config = PERF_COUNT_HW_CPU_CYCLES,
> - .sample_type = PERF_SAMPLE_REGS_INTR,
> - .precise_ip = 1,
> - .disabled = 1,
> - .exclude_kernel = 1,
> - };
> - int fd;
> - u32 version;
> - u64 extended_mask = 0, mask = PERF_REGS_MASK;
> -
> - /*
> - * Get the PVR value to set the extended
> - * mask specific to platform.
> - */
> - version = (((mfspr(SPRN_PVR)) >> 16) & 0xFFFF);
> - if (version == PVR_POWER9)
> - extended_mask = PERF_REG_PMU_MASK_300;
> - else if ((version == PVR_POWER10) || (version == PVR_POWER11))
> - extended_mask = PERF_REG_PMU_MASK_31;
> - else
> - return mask;
> -
> - attr.sample_regs_intr = extended_mask;
> - attr.sample_period = 1;
> - event_attr_init(&attr);
> -
> - /*
> - * check if the pmu supports perf extended regs, before
> - * returning the register mask to sample.
> - */
> - fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
> - if (fd != -1) {
> - close(fd);
> - mask |= extended_mask;
> - }
> - return mask;
> -}
> -
> -uint64_t arch__user_reg_mask(void)
> -{
> - return PERF_REGS_MASK;
> -}
> diff --git a/tools/perf/arch/riscv/include/perf_regs.h b/tools/perf/arch/riscv/include/perf_regs.h
> index d482edb413e5..df3566e4c2c4 100644
> --- a/tools/perf/arch/riscv/include/perf_regs.h
> +++ b/tools/perf/arch/riscv/include/perf_regs.h
> @@ -10,10 +10,17 @@
>
> #define PERF_REGS_MASK ((1ULL << PERF_REG_RISCV_MAX) - 1)
> #define PERF_REGS_MAX PERF_REG_RISCV_MAX
> +
> +#if defined (__riscv_xlen)
> +
> #if __riscv_xlen == 64
> #define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_64
> #else
> #define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_32
> #endif
>
> +#else
> +#define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_NONE
> +#endif
> +
> #endif /* ARCH_PERF_REGS_H */
> diff --git a/tools/perf/arch/riscv/util/Build b/tools/perf/arch/riscv/util/Build
> index 628b9ebd418b..da5b12e7f862 100644
> --- a/tools/perf/arch/riscv/util/Build
> +++ b/tools/perf/arch/riscv/util/Build
> @@ -1,4 +1,3 @@
> -perf-util-y += perf_regs.o
> perf-util-y += header.o
>
> perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
> diff --git a/tools/perf/arch/riscv/util/perf_regs.c b/tools/perf/arch/riscv/util/perf_regs.c
> deleted file mode 100644
> index 2cf7a54106e0..000000000000
> --- a/tools/perf/arch/riscv/util/perf_regs.c
> +++ /dev/null
> @@ -1,13 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0
> -#include "perf_regs.h"
> -#include "../../util/perf_regs.h"
> -
> -uint64_t arch__intr_reg_mask(void)
> -{
> - return PERF_REGS_MASK;
> -}
> -
> -uint64_t arch__user_reg_mask(void)
> -{
> - return PERF_REGS_MASK;
> -}
> diff --git a/tools/perf/arch/s390/util/Build b/tools/perf/arch/s390/util/Build
> index 5391d26fedd4..3b09c058e0ec 100644
> --- a/tools/perf/arch/s390/util/Build
> +++ b/tools/perf/arch/s390/util/Build
> @@ -1,6 +1,5 @@
> perf-util-y += header.o
> perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
> -perf-util-y += perf_regs.o
>
> perf-util-y += machine.o
> perf-util-y += pmu.o
> diff --git a/tools/perf/arch/s390/util/perf_regs.c b/tools/perf/arch/s390/util/perf_regs.c
> deleted file mode 100644
> index 2cf7a54106e0..000000000000
> --- a/tools/perf/arch/s390/util/perf_regs.c
> +++ /dev/null
> @@ -1,13 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0
> -#include "perf_regs.h"
> -#include "../../util/perf_regs.h"
> -
> -uint64_t arch__intr_reg_mask(void)
> -{
> - return PERF_REGS_MASK;
> -}
> -
> -uint64_t arch__user_reg_mask(void)
> -{
> - return PERF_REGS_MASK;
> -}
> diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build
> index fad256252bb9..b7b401cfbd45 100644
> --- a/tools/perf/arch/x86/util/Build
> +++ b/tools/perf/arch/x86/util/Build
> @@ -2,7 +2,6 @@ perf-util-y += header.o
> perf-util-y += tsc.o
> perf-util-y += pmu.o
> perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
> -perf-util-y += perf_regs.o
> perf-util-y += topdown.o
> perf-util-y += machine.o
> perf-util-y += event.o
> diff --git a/tools/perf/arch/x86/util/perf_regs.c b/tools/perf/arch/x86/util/perf_regs.c
> deleted file mode 100644
> index a7ca4154fdf9..000000000000
> --- a/tools/perf/arch/x86/util/perf_regs.c
> +++ /dev/null
> @@ -1,283 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0
> -#include <errno.h>
> -#include <string.h>
> -#include <regex.h>
> -#include <linux/kernel.h>
> -#include <linux/zalloc.h>
> -
> -#include "perf_regs.h"
> -#include "../../../perf-sys.h"
> -#include "../../../util/perf_regs.h"
> -#include "../../../util/debug.h"
> -#include "../../../util/event.h"
> -#include "../../../util/pmu.h"
> -#include "../../../util/pmus.h"
> -
> -struct sdt_name_reg {
> - const char *sdt_name;
> - const char *uprobe_name;
> -};
> -#define SDT_NAME_REG(n, m) {.sdt_name = "%" #n, .uprobe_name = "%" #m}
> -#define SDT_NAME_REG_END {.sdt_name = NULL, .uprobe_name = NULL}
> -
> -static const struct sdt_name_reg sdt_reg_tbl[] = {
> - SDT_NAME_REG(eax, ax),
> - SDT_NAME_REG(rax, ax),
> - SDT_NAME_REG(al, ax),
> - SDT_NAME_REG(ah, ax),
> - SDT_NAME_REG(ebx, bx),
> - SDT_NAME_REG(rbx, bx),
> - SDT_NAME_REG(bl, bx),
> - SDT_NAME_REG(bh, bx),
> - SDT_NAME_REG(ecx, cx),
> - SDT_NAME_REG(rcx, cx),
> - SDT_NAME_REG(cl, cx),
> - SDT_NAME_REG(ch, cx),
> - SDT_NAME_REG(edx, dx),
> - SDT_NAME_REG(rdx, dx),
> - SDT_NAME_REG(dl, dx),
> - SDT_NAME_REG(dh, dx),
> - SDT_NAME_REG(esi, si),
> - SDT_NAME_REG(rsi, si),
> - SDT_NAME_REG(sil, si),
> - SDT_NAME_REG(edi, di),
> - SDT_NAME_REG(rdi, di),
> - SDT_NAME_REG(dil, di),
> - SDT_NAME_REG(ebp, bp),
> - SDT_NAME_REG(rbp, bp),
> - SDT_NAME_REG(bpl, bp),
> - SDT_NAME_REG(rsp, sp),
> - SDT_NAME_REG(esp, sp),
> - SDT_NAME_REG(spl, sp),
> -
> - /* rNN registers */
> - SDT_NAME_REG(r8b, r8),
> - SDT_NAME_REG(r8w, r8),
> - SDT_NAME_REG(r8d, r8),
> - SDT_NAME_REG(r9b, r9),
> - SDT_NAME_REG(r9w, r9),
> - SDT_NAME_REG(r9d, r9),
> - SDT_NAME_REG(r10b, r10),
> - SDT_NAME_REG(r10w, r10),
> - SDT_NAME_REG(r10d, r10),
> - SDT_NAME_REG(r11b, r11),
> - SDT_NAME_REG(r11w, r11),
> - SDT_NAME_REG(r11d, r11),
> - SDT_NAME_REG(r12b, r12),
> - SDT_NAME_REG(r12w, r12),
> - SDT_NAME_REG(r12d, r12),
> - SDT_NAME_REG(r13b, r13),
> - SDT_NAME_REG(r13w, r13),
> - SDT_NAME_REG(r13d, r13),
> - SDT_NAME_REG(r14b, r14),
> - SDT_NAME_REG(r14w, r14),
> - SDT_NAME_REG(r14d, r14),
> - SDT_NAME_REG(r15b, r15),
> - SDT_NAME_REG(r15w, r15),
> - SDT_NAME_REG(r15d, r15),
> - SDT_NAME_REG_END,
> -};
> -
> -/*
> - * Perf only supports OP which is in +/-NUM(REG) form.
> - * Here plus-minus sign, NUM and parenthesis are optional,
> - * only REG is mandatory.
> - *
> - * SDT events also supports indirect addressing mode with a
> - * symbol as offset, scaled mode and constants in OP. But
> - * perf does not support them yet. Below are few examples.
> - *
> - * OP with scaled mode:
> - * (%rax,%rsi,8)
> - * 10(%ras,%rsi,8)
> - *
> - * OP with indirect addressing mode:
> - * check_action(%rip)
> - * mp_+52(%rip)
> - * 44+mp_(%rip)
> - *
> - * OP with constant values:
> - * $0
> - * $123
> - * $-1
> - */
> -#define SDT_OP_REGEX "^([+\\-]?)([0-9]*)(\\(?)(%[a-z][a-z0-9]+)(\\)?)$"
> -
> -static regex_t sdt_op_regex;
> -
> -static int sdt_init_op_regex(void)
> -{
> - static int initialized;
> - int ret = 0;
> -
> - if (initialized)
> - return 0;
> -
> - ret = regcomp(&sdt_op_regex, SDT_OP_REGEX, REG_EXTENDED);
> - if (ret < 0) {
> - pr_debug4("Regex compilation error.\n");
> - return ret;
> - }
> -
> - initialized = 1;
> - return 0;
> -}
> -
> -/*
> - * Max x86 register name length is 5(ex: %r15d). So, 6th char
> - * should always contain NULL. This helps to find register name
> - * length using strlen, instead of maintaining one more variable.
> - */
> -#define SDT_REG_NAME_SIZE 6
> -
> -/*
> - * The uprobe parser does not support all gas register names;
> - * so, we have to replace them (ex. for x86_64: %rax -> %ax).
> - * Note: If register does not require renaming, just copy
> - * paste as it is, but don't leave it empty.
> - */
> -static void sdt_rename_register(char *sdt_reg, int sdt_len, char *uprobe_reg)
> -{
> - int i = 0;
> -
> - for (i = 0; sdt_reg_tbl[i].sdt_name != NULL; i++) {
> - if (!strncmp(sdt_reg_tbl[i].sdt_name, sdt_reg, sdt_len)) {
> - strcpy(uprobe_reg, sdt_reg_tbl[i].uprobe_name);
> - return;
> - }
> - }
> -
> - strncpy(uprobe_reg, sdt_reg, sdt_len);
> -}
> -
> -int arch_sdt_arg_parse_op(char *old_op, char **new_op)
> -{
> - char new_reg[SDT_REG_NAME_SIZE] = {0};
> - int new_len = 0, ret;
> - /*
> - * rm[0]: +/-NUM(REG)
> - * rm[1]: +/-
> - * rm[2]: NUM
> - * rm[3]: (
> - * rm[4]: REG
> - * rm[5]: )
> - */
> - regmatch_t rm[6];
> - /*
> - * Max prefix length is 2 as it may contains sign(+/-)
> - * and displacement 0 (Both sign and displacement 0 are
> - * optional so it may be empty). Use one more character
> - * to hold last NULL so that strlen can be used to find
> - * prefix length, instead of maintaining one more variable.
> - */
> - char prefix[3] = {0};
> -
> - ret = sdt_init_op_regex();
> - if (ret < 0)
> - return ret;
> -
> - /*
> - * If unsupported OR does not match with regex OR
> - * register name too long, skip it.
> - */
> - if (strchr(old_op, ',') || strchr(old_op, '$') ||
> - regexec(&sdt_op_regex, old_op, 6, rm, 0) ||
> - rm[4].rm_eo - rm[4].rm_so > SDT_REG_NAME_SIZE) {
> - pr_debug4("Skipping unsupported SDT argument: %s\n", old_op);
> - return SDT_ARG_SKIP;
> - }
> -
> - /*
> - * Prepare prefix.
> - * If SDT OP has parenthesis but does not provide
> - * displacement, add 0 for displacement.
> - * SDT Uprobe Prefix
> - * -----------------------------
> - * +24(%rdi) +24(%di) +
> - * 24(%rdi) +24(%di) +
> - * %rdi %di
> - * (%rdi) +0(%di) +0
> - * -80(%rbx) -80(%bx) -
> - */
> - if (rm[3].rm_so != rm[3].rm_eo) {
> - if (rm[1].rm_so != rm[1].rm_eo)
> - prefix[0] = *(old_op + rm[1].rm_so);
> - else if (rm[2].rm_so != rm[2].rm_eo)
> - prefix[0] = '+';
> - else
> - scnprintf(prefix, sizeof(prefix), "+0");
> - }
> -
> - /* Rename register */
> - sdt_rename_register(old_op + rm[4].rm_so, rm[4].rm_eo - rm[4].rm_so,
> - new_reg);
> -
> - /* Prepare final OP which should be valid for uprobe_events */
> - new_len = strlen(prefix) +
> - (rm[2].rm_eo - rm[2].rm_so) +
> - (rm[3].rm_eo - rm[3].rm_so) +
> - strlen(new_reg) +
> - (rm[5].rm_eo - rm[5].rm_so) +
> - 1; /* NULL */
> -
> - *new_op = zalloc(new_len);
> - if (!*new_op)
> - return -ENOMEM;
> -
> - scnprintf(*new_op, new_len, "%.*s%.*s%.*s%.*s%.*s",
> - strlen(prefix), prefix,
> - (int)(rm[2].rm_eo - rm[2].rm_so), old_op + rm[2].rm_so,
> - (int)(rm[3].rm_eo - rm[3].rm_so), old_op + rm[3].rm_so,
> - strlen(new_reg), new_reg,
> - (int)(rm[5].rm_eo - rm[5].rm_so), old_op + rm[5].rm_so);
> -
> - return SDT_ARG_VALID;
> -}
> -
> -uint64_t arch__intr_reg_mask(void)
> -{
> - struct perf_event_attr attr = {
> - .type = PERF_TYPE_HARDWARE,
> - .config = PERF_COUNT_HW_CPU_CYCLES,
> - .sample_type = PERF_SAMPLE_REGS_INTR,
> - .sample_regs_intr = PERF_REG_EXTENDED_MASK,
> - .precise_ip = 1,
> - .disabled = 1,
> - .exclude_kernel = 1,
> - };
> - int fd;
> - /*
> - * In an unnamed union, init it here to build on older gcc versions
> - */
> - attr.sample_period = 1;
> -
> - if (perf_pmus__num_core_pmus() > 1) {
> - struct perf_pmu *pmu = NULL;
> - __u64 type = PERF_TYPE_RAW;
> -
> - /*
> - * The same register set is supported among different hybrid PMUs.
> - * Only check the first available one.
> - */
> - while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {
> - type = pmu->type;
> - break;
> - }
> - attr.config |= type << PERF_PMU_TYPE_SHIFT;
> - }
> -
> - event_attr_init(&attr);
> -
> - fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
> - if (fd != -1) {
> - close(fd);
> - return (PERF_REG_EXTENDED_MASK | PERF_REGS_MASK);
> - }
> -
> - return PERF_REGS_MASK;
> -}
> -
> -uint64_t arch__user_reg_mask(void)
> -{
> - return PERF_REGS_MASK;
> -}
> diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
> index 5ac1a05601b1..a36528fd41c6 100644
> --- a/tools/perf/util/evsel.c
> +++ b/tools/perf/util/evsel.c
> @@ -1055,13 +1055,13 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o
> evsel__set_sample_bit(evsel, REGS_USER);
> evsel__set_sample_bit(evsel, STACK_USER);
> if (opts->sample_user_regs &&
> - DWARF_MINIMAL_REGS(e_machine) != arch__user_reg_mask()) {
> + DWARF_MINIMAL_REGS(e_machine) != perf_user_reg_mask(EM_HOST)) {
> attr->sample_regs_user |= DWARF_MINIMAL_REGS(e_machine);
> pr_warning("WARNING: The use of --call-graph=dwarf may require all the user registers, "
> "specifying a subset with --user-regs may render DWARF unwinding unreliable, "
> "so the minimal registers set (IP, SP) is explicitly forced.\n");
> } else {
> - attr->sample_regs_user |= arch__user_reg_mask();
> + attr->sample_regs_user |= perf_user_reg_mask(EM_HOST);
> }
> attr->sample_stack_user = param->dump_size;
> attr->exclude_callchain_user = 1;
> diff --git a/tools/perf/util/parse-regs-options.c b/tools/perf/util/parse-regs-options.c
> index c0d0ef9fd495..2af6e4ad2a34 100644
> --- a/tools/perf/util/parse-regs-options.c
> +++ b/tools/perf/util/parse-regs-options.c
> @@ -70,7 +70,7 @@ __parse_regs(const struct option *opt, const char *str, int unset, bool intr)
> if (!str)
> return -1;
>
> - mask = intr ? arch__intr_reg_mask() : arch__user_reg_mask();
> + mask = intr ? perf_intr_reg_mask(EM_HOST) : perf_user_reg_mask(EM_HOST);
>
> /* because str is read-only */
> s = os = strdup(str);
> diff --git a/tools/perf/util/perf-regs-arch/perf_regs_aarch64.c b/tools/perf/util/perf-regs-arch/perf_regs_aarch64.c
> index 9dcda80d310f..fb31b7625e68 100644
> --- a/tools/perf/util/perf-regs-arch/perf_regs_aarch64.c
> +++ b/tools/perf/util/perf-regs-arch/perf_regs_aarch64.c
> @@ -1,7 +1,143 @@
> // SPDX-License-Identifier: GPL-2.0
> +#include <errno.h>
> +#include <regex.h>
> +#include <string.h>
> +#include <sys/auxv.h>
> +#include <linux/kernel.h>
> +#include <linux/zalloc.h>
>
> -#include "../perf_regs.h"
> -#include "../../../arch/arm64/include/uapi/asm/perf_regs.h"
> +#include "../debug.h" /* tools/perf/util/debug.h */
> +#include "../event.h" /* tools/perf/util/event.h */
> +#include "../perf_regs.h" /* tools/perf/util/perf_regs.h */
> +#include "../../perf-sys.h" /* tools/perf/perf-sys.h */
> +#include "../../arch/arm64/include/perf_regs.h" /* tools/perf/arch/arm64/include/perf_regs.h */
> +#include "../../../arch/arm64/include/uapi/asm/perf_regs.h" /* tools/arch/arm64/include/uapi/asm/perf_regs.h */
> +
> +#define SMPL_REG_MASK(b) (1ULL << (b))
> +
> +#ifndef HWCAP_SVE
> +#define HWCAP_SVE (1 << 22)
> +#endif
> +
> +/* %xNUM */
> +#define SDT_OP_REGEX1 "^(x[1-2]?[0-9]|3[0-1])$"
> +
> +/* [sp], [sp, NUM] */
> +#define SDT_OP_REGEX2 "^\\[sp(, )?([0-9]+)?\\]$"
> +
> +static regex_t sdt_op_regex1, sdt_op_regex2;
> +
> +static int sdt_init_op_regex(void)
> +{
> + static int initialized;
> + int ret = 0;
> +
> + if (initialized)
> + return 0;
> +
> + ret = regcomp(&sdt_op_regex1, SDT_OP_REGEX1, REG_EXTENDED);
> + if (ret)
> + goto error;
> +
> + ret = regcomp(&sdt_op_regex2, SDT_OP_REGEX2, REG_EXTENDED);
> + if (ret)
> + goto free_regex1;
> +
> + initialized = 1;
> + return 0;
> +
> +free_regex1:
> + regfree(&sdt_op_regex1);
> +error:
> + pr_debug4("Regex compilation error.\n");
> + return ret;
> +}
> +
> +/*
> + * SDT marker arguments on Arm64 uses %xREG or [sp, NUM], currently
> + * support these two formats.
> + */
> +int __perf_sdt_arg_parse_op_arm64(char *old_op, char **new_op)
> +{
> + int ret, new_len;
> + regmatch_t rm[5];
> +
> + ret = sdt_init_op_regex();
> + if (ret < 0)
> + return ret;
> +
> + if (!regexec(&sdt_op_regex1, old_op, 3, rm, 0)) {
> + /* Extract xNUM */
> + new_len = 2; /* % NULL */
> + new_len += (int)(rm[1].rm_eo - rm[1].rm_so);
> +
> + *new_op = zalloc(new_len);
> + if (!*new_op)
> + return -ENOMEM;
> +
> + scnprintf(*new_op, new_len, "%%%.*s",
> + (int)(rm[1].rm_eo - rm[1].rm_so), old_op + rm[1].rm_so);
> + } else if (!regexec(&sdt_op_regex2, old_op, 5, rm, 0)) {
> + /* [sp], [sp, NUM] or [sp,NUM] */
> + new_len = 7; /* + ( % s p ) NULL */
> +
> + /* If the argument is [sp], need to fill offset '0' */
> + if (rm[2].rm_so == -1)
> + new_len += 1;
> + else
> + new_len += (int)(rm[2].rm_eo - rm[2].rm_so);
> +
> + *new_op = zalloc(new_len);
> + if (!*new_op)
> + return -ENOMEM;
> +
> + if (rm[2].rm_so == -1)
> + scnprintf(*new_op, new_len, "+0(%%sp)");
> + else
> + scnprintf(*new_op, new_len, "+%.*s(%%sp)",
> + (int)(rm[2].rm_eo - rm[2].rm_so),
> + old_op + rm[2].rm_so);
> + } else {
> + pr_debug4("Skipping unsupported SDT argument: %s\n", old_op);
> + return SDT_ARG_SKIP;
> + }
> +
> + return SDT_ARG_VALID;
> +}
> +
> +uint64_t __perf_reg_mask_arm64(bool intr)
> +{
> + struct perf_event_attr attr = {
> + .type = PERF_TYPE_HARDWARE,
> + .config = PERF_COUNT_HW_CPU_CYCLES,
> + .sample_type = PERF_SAMPLE_REGS_USER,
> + .disabled = 1,
> + .exclude_kernel = 1,
> + .sample_period = 1,
> + .sample_regs_user = PERF_REGS_MASK
> + };
> + int fd;
> +
> + if (intr)
> + return PERF_REGS_MASK;
> +
> + if (getauxval(AT_HWCAP) & HWCAP_SVE)
> + attr.sample_regs_user |= SMPL_REG_MASK(PERF_REG_ARM64_VG);
> +
> + /*
> + * Check if the pmu supports perf extended regs, before
> + * returning the register mask to sample.
> + */
> + if (attr.sample_regs_user != PERF_REGS_MASK) {
> + event_attr_init(&attr);
> + fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
> + if (fd != -1) {
> + close(fd);
> + return attr.sample_regs_user;
> + }
> + }
> + return PERF_REGS_MASK;
> +}
>
> const char *__perf_reg_name_arm64(int id)
> {
> diff --git a/tools/perf/util/perf-regs-arch/perf_regs_arm.c b/tools/perf/util/perf-regs-arch/perf_regs_arm.c
> index e29d130a587a..70f00308991a 100644
> --- a/tools/perf/util/perf-regs-arch/perf_regs_arm.c
> +++ b/tools/perf/util/perf-regs-arch/perf_regs_arm.c
> @@ -1,7 +1,13 @@
> // SPDX-License-Identifier: GPL-2.0
>
> -#include "../perf_regs.h"
> -#include "../../../arch/arm/include/uapi/asm/perf_regs.h"
> +#include "../perf_regs.h" /* tools/perf/util/perf_regs.h */
> +#include "../../arch/arm/include/perf_regs.h" /* tools/perf/arch/arm/include/perf_regs.h */
> +#include "../../../arch/arm/include/uapi/asm/perf_regs.h" /* tools/arch/arm/include/uapi/asm/perf_regs.h */
> +
> +uint64_t __perf_reg_mask_arm(bool intr __maybe_unused)
> +{
> + return PERF_REGS_MASK;
> +}
>
> const char *__perf_reg_name_arm(int id)
> {
> diff --git a/tools/perf/util/perf-regs-arch/perf_regs_csky.c b/tools/perf/util/perf-regs-arch/perf_regs_csky.c
> index 75b461ef2eba..c656af151a03 100644
> --- a/tools/perf/util/perf-regs-arch/perf_regs_csky.c
> +++ b/tools/perf/util/perf-regs-arch/perf_regs_csky.c
> @@ -1,7 +1,13 @@
> // SPDX-License-Identifier: GPL-2.0
>
> -#include "../perf_regs.h"
> -#include "../../arch/csky/include/uapi/asm/perf_regs.h"
> +#include "../perf_regs.h" /* tools/perf/util/perf_regs.h */
> +#include "../../arch/csky/include/perf_regs.h" /* tools/perf/arch/csky/include/perf_regs.h */
> +#include "../../../arch/csky/include/uapi/asm/perf_regs.h" /* tools/arch/csky/include/uapi/asm/perf_regs.h */
> +
> +uint64_t __perf_reg_mask_csky(bool intr __maybe_unused)
> +{
> + return PERF_REGS_MASK;
> +}
>
> const char *__perf_reg_name_csky(int id)
> {
> diff --git a/tools/perf/util/perf-regs-arch/perf_regs_loongarch.c b/tools/perf/util/perf-regs-arch/perf_regs_loongarch.c
> index 043f97f4e3ac..1325a71158a8 100644
> --- a/tools/perf/util/perf-regs-arch/perf_regs_loongarch.c
> +++ b/tools/perf/util/perf-regs-arch/perf_regs_loongarch.c
> @@ -1,7 +1,13 @@
> // SPDX-License-Identifier: GPL-2.0
>
> -#include "../perf_regs.h"
> -#include "../../../arch/loongarch/include/uapi/asm/perf_regs.h"
> +#include "../perf_regs.h" /* tools/perf/util/perf_regs.h */
> +#include "../../arch/loongarch/include/perf_regs.h" /* tools/perf/arch/loongarch/include/perf_regs.h */
> +#include "../../../arch/loongarch/include/uapi/asm/perf_regs.h" /* tools/arch/loongarch/include/uapi/asm/perf_regs.h */
> +
> +uint64_t __perf_reg_mask_loongarch(bool intr __maybe_unused)
> +{
> + return PERF_REGS_MASK;
> +}
>
> const char *__perf_reg_name_loongarch(int id)
> {
> diff --git a/tools/perf/util/perf-regs-arch/perf_regs_mips.c b/tools/perf/util/perf-regs-arch/perf_regs_mips.c
> index 793178fc3c78..2d7ebd284db2 100644
> --- a/tools/perf/util/perf-regs-arch/perf_regs_mips.c
> +++ b/tools/perf/util/perf-regs-arch/perf_regs_mips.c
> @@ -1,7 +1,13 @@
> // SPDX-License-Identifier: GPL-2.0
>
> -#include "../perf_regs.h"
> -#include "../../../arch/mips/include/uapi/asm/perf_regs.h"
> +#include "../perf_regs.h" /* tools/perf/util/perf_regs.h */
> +#include "../../arch/mips/include/perf_regs.h" /* tools/perf/arch/mips/include/perf_regs.h */
> +#include "../../../arch/mips/include/uapi/asm/perf_regs.h" /* tools/arch/mips/include/uapi/asm/perf_regs.h */
> +
> +uint64_t __perf_reg_mask_mips(bool intr __maybe_unused)
> +{
> + return PERF_REGS_MASK;
> +}
>
> const char *__perf_reg_name_mips(int id)
> {
> diff --git a/tools/perf/util/perf-regs-arch/perf_regs_powerpc.c b/tools/perf/util/perf-regs-arch/perf_regs_powerpc.c
> index 08636bb09a3a..74c269e9f25c 100644
> --- a/tools/perf/util/perf-regs-arch/perf_regs_powerpc.c
> +++ b/tools/perf/util/perf-regs-arch/perf_regs_powerpc.c
> @@ -1,7 +1,182 @@
> // SPDX-License-Identifier: GPL-2.0
>
> -#include "../perf_regs.h"
> -#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h"
> +#include <errno.h>
> +#include <string.h>
> +#include <regex.h>
> +#include <linux/zalloc.h>
> +
> +#include "../debug.h" /* tools/perf/util/debug.h */
> +#include "../event.h" /* tools/perf/util/event.h */
> +#include "../header.h" /* tools/perf/util/header.h */
> +#include "../perf_regs.h" /* tools/perf/util/perf_regs.h */
> +#include "../../perf-sys.h" /* tools/perf/perf-sys.h */
> +#include "../../arch/powerpc/util/utils_header.h" /* tools/perf/arch/powerpc/util/utils_header.h */
> +#include "../../arch/powerpc/include/perf_regs.h" /* tools/perf/arch/powerpc/include/perf_regs.h */
> +#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h" /* tools/arch/powerpc/include/uapi/asm/perf_regs.h */
> +
> +#include <linux/kernel.h>
> +
> +#define PVR_POWER9 0x004E
> +#define PVR_POWER10 0x0080
> +#define PVR_POWER11 0x0082
> +
> +/* REG or %rREG */
> +#define SDT_OP_REGEX1 "^(%r)?([1-2]?[0-9]|3[0-1])$"
> +
> +/* -NUM(REG) or NUM(REG) or -NUM(%rREG) or NUM(%rREG) */
> +#define SDT_OP_REGEX2 "^(\\-)?([0-9]+)\\((%r)?([1-2]?[0-9]|3[0-1])\\)$"
> +
> +static regex_t sdt_op_regex1, sdt_op_regex2;
> +
> +static int sdt_init_op_regex(void)
> +{
> + static int initialized;
> + int ret = 0;
> +
> + if (initialized)
> + return 0;
> +
> + ret = regcomp(&sdt_op_regex1, SDT_OP_REGEX1, REG_EXTENDED);
> + if (ret)
> + goto error;
> +
> + ret = regcomp(&sdt_op_regex2, SDT_OP_REGEX2, REG_EXTENDED);
> + if (ret)
> + goto free_regex1;
> +
> + initialized = 1;
> + return 0;
> +
> +free_regex1:
> + regfree(&sdt_op_regex1);
> +error:
> + pr_debug4("Regex compilation error.\n");
> + return ret;
> +}
> +
> +/*
> + * Parse OP and convert it into uprobe format, which is, +/-NUM(%gprREG).
> + * Possible variants of OP are:
> + * Format Example
> + * -------------------------
> + * NUM(REG) 48(18)
> + * -NUM(REG) -48(18)
> + * NUM(%rREG) 48(%r18)
> + * -NUM(%rREG) -48(%r18)
> + * REG 18
> + * %rREG %r18
> + * iNUM i0
> + * i-NUM i-1
> + *
> + * SDT marker arguments on Powerpc uses %rREG form with -mregnames flag
> + * and REG form with -mno-regnames. Here REG is general purpose register,
> + * which is in 0 to 31 range.
> + */
> +int __perf_sdt_arg_parse_op_powerpc(char *old_op, char **new_op)
> +{
> + int ret, new_len;
> + regmatch_t rm[5];
> + char prefix;
> +
> + /* Constant argument. Uprobe does not support it */
> + if (old_op[0] == 'i') {
> + pr_debug4("Skipping unsupported SDT argument: %s\n", old_op);
> + return SDT_ARG_SKIP;
> + }
> +
> + ret = sdt_init_op_regex();
> + if (ret < 0)
> + return ret;
> +
> + if (!regexec(&sdt_op_regex1, old_op, 3, rm, 0)) {
> + /* REG or %rREG --> %gprREG */
> +
> + new_len = 5; /* % g p r NULL */
> + new_len += (int)(rm[2].rm_eo - rm[2].rm_so);
> +
> + *new_op = zalloc(new_len);
> + if (!*new_op)
> + return -ENOMEM;
> +
> + scnprintf(*new_op, new_len, "%%gpr%.*s",
> + (int)(rm[2].rm_eo - rm[2].rm_so), old_op + rm[2].rm_so);
> + } else if (!regexec(&sdt_op_regex2, old_op, 5, rm, 0)) {
> + /*
> + * -NUM(REG) or NUM(REG) or -NUM(%rREG) or NUM(%rREG) -->
> + * +/-NUM(%gprREG)
> + */
> + prefix = (rm[1].rm_so == -1) ? '+' : '-';
> +
> + new_len = 8; /* +/- ( % g p r ) NULL */
> + new_len += (int)(rm[2].rm_eo - rm[2].rm_so);
> + new_len += (int)(rm[4].rm_eo - rm[4].rm_so);
> +
> + *new_op = zalloc(new_len);
> + if (!*new_op)
> + return -ENOMEM;
> +
> + scnprintf(*new_op, new_len, "%c%.*s(%%gpr%.*s)", prefix,
> + (int)(rm[2].rm_eo - rm[2].rm_so), old_op + rm[2].rm_so,
> + (int)(rm[4].rm_eo - rm[4].rm_so), old_op + rm[4].rm_so);
> + } else {
> + pr_debug4("Skipping unsupported SDT argument: %s\n", old_op);
> + return SDT_ARG_SKIP;
> + }
> +
> + return SDT_ARG_VALID;
> +}
> +
> +#if defined(__powerpc64__) && defined(__powerpc__)
> +uint64_t __perf_reg_mask_powerpc(bool intr)
> +{
> + struct perf_event_attr attr = {
> + .type = PERF_TYPE_HARDWARE,
> + .config = PERF_COUNT_HW_CPU_CYCLES,
> + .sample_type = PERF_SAMPLE_REGS_INTR,
> + .precise_ip = 1,
> + .disabled = 1,
> + .exclude_kernel = 1,
> + };
> + int fd;
> + u32 version;
> + u64 extended_mask = 0, mask = PERF_REGS_MASK;
> +
> + if (!intr)
> + return PERF_REGS_MASK;
> +
> + /*
> + * Get the PVR value to set the extended
> + * mask specific to platform.
> + */
> + version = (((mfspr(SPRN_PVR)) >> 16) & 0xFFFF);
> + if (version == PVR_POWER9)
> + extended_mask = PERF_REG_PMU_MASK_300;
> + else if ((version == PVR_POWER10) || (version == PVR_POWER11))
> + extended_mask = PERF_REG_PMU_MASK_31;
> + else
> + return mask;
> +
> + attr.sample_regs_intr = extended_mask;
> + attr.sample_period = 1;
> + event_attr_init(&attr);
> +
> + /*
> + * check if the pmu supports perf extended regs, before
> + * returning the register mask to sample.
> + */
> + fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
> + if (fd != -1) {
> + close(fd);
> + mask |= extended_mask;
> + }
> + return mask;
> +}
> +#else
> +uint64_t __perf_reg_mask_powerpc(bool intr __maybe_unused)
> +{
> + return PERF_REGS_MASK;
> +}
> +#endif
>
> const char *__perf_reg_name_powerpc(int id)
> {
> diff --git a/tools/perf/util/perf-regs-arch/perf_regs_riscv.c b/tools/perf/util/perf-regs-arch/perf_regs_riscv.c
> index 337b687c655d..bf87a8ce0ad3 100644
> --- a/tools/perf/util/perf-regs-arch/perf_regs_riscv.c
> +++ b/tools/perf/util/perf-regs-arch/perf_regs_riscv.c
> @@ -1,7 +1,13 @@
> // SPDX-License-Identifier: GPL-2.0
>
> -#include "../perf_regs.h"
> -#include "../../../arch/riscv/include/uapi/asm/perf_regs.h"
> +#include "../perf_regs.h" /* tools/perf/util/perf_regs.h */
> +#include "../../arch/riscv/include/perf_regs.h" /* tools/perf/arch/riscv/include/perf_regs.h */
> +#include "../../../arch/riscv/include/uapi/asm/perf_regs.h" /* tools/arch/riscv/include/uapi/asm/perf_regs.h */
> +
> +uint64_t __perf_reg_mask_riscv(bool intr __maybe_unused)
> +{
> + return PERF_REGS_MASK;
> +}
>
> const char *__perf_reg_name_riscv(int id)
> {
> diff --git a/tools/perf/util/perf-regs-arch/perf_regs_s390.c b/tools/perf/util/perf-regs-arch/perf_regs_s390.c
> index d69bba881080..4095922997aa 100644
> --- a/tools/perf/util/perf-regs-arch/perf_regs_s390.c
> +++ b/tools/perf/util/perf-regs-arch/perf_regs_s390.c
> @@ -1,7 +1,13 @@
> // SPDX-License-Identifier: GPL-2.0
>
> -#include "../perf_regs.h"
> -#include "../../../arch/s390/include/uapi/asm/perf_regs.h"
> +#include "../perf_regs.h" /* tools/perf/util/perf_regs.h */
> +#include "../../arch/s390/include/perf_regs.h" /* tools/perf/arch/s390/include/perf_regs.h */
> +#include "../../../arch/s390/include/uapi/asm/perf_regs.h" /* tools/arch/s390/include/uapi/asm/perf_regs.h */
> +
> +uint64_t __perf_reg_mask_s390(bool intr __maybe_unused)
> +{
> + return PERF_REGS_MASK;
> +}
>
> const char *__perf_reg_name_s390(int id)
> {
> diff --git a/tools/perf/util/perf-regs-arch/perf_regs_x86.c b/tools/perf/util/perf-regs-arch/perf_regs_x86.c
> index 708954a9d35d..b3bcee7a774a 100644
> --- a/tools/perf/util/perf-regs-arch/perf_regs_x86.c
> +++ b/tools/perf/util/perf-regs-arch/perf_regs_x86.c
> @@ -1,7 +1,287 @@
> // SPDX-License-Identifier: GPL-2.0
>
> -#include "../perf_regs.h"
> -#include "../../../arch/x86/include/uapi/asm/perf_regs.h"
> +#include <errno.h>
> +#include <string.h>
> +#include <regex.h>
> +#include <linux/kernel.h>
> +#include <linux/zalloc.h>
> +
> +#include "../debug.h" /* tools/perf/util/debug.h */
> +#include "../event.h" /* tools/perf/util/event.h */
> +#include "../pmu.h" /* tools/perf/util/pmu.h */
> +#include "../pmus.h" /* tools/perf/util/pmus.h */
> +#include "../perf_regs.h" /* tools/perf/util/perf_regs.h */
> +#include "../../perf-sys.h" /* tools/perf/perf-sys.h */
> +#include "../../arch/x86/include/perf_regs.h" /* tools/perf/arch/x86/include/perf_regs.h */
> +#include "../../../arch/x86/include/uapi/asm/perf_regs.h" /* tools/arch/x86/include/uapi/asm/perf_regs.h */
> +
> +struct sdt_name_reg {
> + const char *sdt_name;
> + const char *uprobe_name;
> +};
> +#define SDT_NAME_REG(n, m) {.sdt_name = "%" #n, .uprobe_name = "%" #m}
> +#define SDT_NAME_REG_END {.sdt_name = NULL, .uprobe_name = NULL}
> +
> +static const struct sdt_name_reg sdt_reg_tbl[] = {
> + SDT_NAME_REG(eax, ax),
> + SDT_NAME_REG(rax, ax),
> + SDT_NAME_REG(al, ax),
> + SDT_NAME_REG(ah, ax),
> + SDT_NAME_REG(ebx, bx),
> + SDT_NAME_REG(rbx, bx),
> + SDT_NAME_REG(bl, bx),
> + SDT_NAME_REG(bh, bx),
> + SDT_NAME_REG(ecx, cx),
> + SDT_NAME_REG(rcx, cx),
> + SDT_NAME_REG(cl, cx),
> + SDT_NAME_REG(ch, cx),
> + SDT_NAME_REG(edx, dx),
> + SDT_NAME_REG(rdx, dx),
> + SDT_NAME_REG(dl, dx),
> + SDT_NAME_REG(dh, dx),
> + SDT_NAME_REG(esi, si),
> + SDT_NAME_REG(rsi, si),
> + SDT_NAME_REG(sil, si),
> + SDT_NAME_REG(edi, di),
> + SDT_NAME_REG(rdi, di),
> + SDT_NAME_REG(dil, di),
> + SDT_NAME_REG(ebp, bp),
> + SDT_NAME_REG(rbp, bp),
> + SDT_NAME_REG(bpl, bp),
> + SDT_NAME_REG(rsp, sp),
> + SDT_NAME_REG(esp, sp),
> + SDT_NAME_REG(spl, sp),
> +
> + /* rNN registers */
> + SDT_NAME_REG(r8b, r8),
> + SDT_NAME_REG(r8w, r8),
> + SDT_NAME_REG(r8d, r8),
> + SDT_NAME_REG(r9b, r9),
> + SDT_NAME_REG(r9w, r9),
> + SDT_NAME_REG(r9d, r9),
> + SDT_NAME_REG(r10b, r10),
> + SDT_NAME_REG(r10w, r10),
> + SDT_NAME_REG(r10d, r10),
> + SDT_NAME_REG(r11b, r11),
> + SDT_NAME_REG(r11w, r11),
> + SDT_NAME_REG(r11d, r11),
> + SDT_NAME_REG(r12b, r12),
> + SDT_NAME_REG(r12w, r12),
> + SDT_NAME_REG(r12d, r12),
> + SDT_NAME_REG(r13b, r13),
> + SDT_NAME_REG(r13w, r13),
> + SDT_NAME_REG(r13d, r13),
> + SDT_NAME_REG(r14b, r14),
> + SDT_NAME_REG(r14w, r14),
> + SDT_NAME_REG(r14d, r14),
> + SDT_NAME_REG(r15b, r15),
> + SDT_NAME_REG(r15w, r15),
> + SDT_NAME_REG(r15d, r15),
> + SDT_NAME_REG_END,
> +};
> +
> +/*
> + * Perf only supports OP which is in +/-NUM(REG) form.
> + * Here plus-minus sign, NUM and parenthesis are optional,
> + * only REG is mandatory.
> + *
> + * SDT events also supports indirect addressing mode with a
> + * symbol as offset, scaled mode and constants in OP. But
> + * perf does not support them yet. Below are few examples.
> + *
> + * OP with scaled mode:
> + * (%rax,%rsi,8)
> + * 10(%ras,%rsi,8)
> + *
> + * OP with indirect addressing mode:
> + * check_action(%rip)
> + * mp_+52(%rip)
> + * 44+mp_(%rip)
> + *
> + * OP with constant values:
> + * $0
> + * $123
> + * $-1
> + */
> +#define SDT_OP_REGEX "^([+\\-]?)([0-9]*)(\\(?)(%[a-z][a-z0-9]+)(\\)?)$"
> +
> +static regex_t sdt_op_regex;
> +
> +static int sdt_init_op_regex(void)
> +{
> + static int initialized;
> + int ret = 0;
> +
> + if (initialized)
> + return 0;
> +
> + ret = regcomp(&sdt_op_regex, SDT_OP_REGEX, REG_EXTENDED);
> + if (ret < 0) {
> + pr_debug4("Regex compilation error.\n");
> + return ret;
> + }
> +
> + initialized = 1;
> + return 0;
> +}
> +
> +/*
> + * Max x86 register name length is 5(ex: %r15d). So, 6th char
> + * should always contain NULL. This helps to find register name
> + * length using strlen, instead of maintaining one more variable.
> + */
> +#define SDT_REG_NAME_SIZE 6
> +
> +/*
> + * The uprobe parser does not support all gas register names;
> + * so, we have to replace them (ex. for x86_64: %rax -> %ax).
> + * Note: If register does not require renaming, just copy
> + * paste as it is, but don't leave it empty.
> + */
> +static void sdt_rename_register(char *sdt_reg, int sdt_len, char *uprobe_reg)
> +{
> + int i = 0;
> +
> + for (i = 0; sdt_reg_tbl[i].sdt_name != NULL; i++) {
> + if (!strncmp(sdt_reg_tbl[i].sdt_name, sdt_reg, sdt_len)) {
> + strcpy(uprobe_reg, sdt_reg_tbl[i].uprobe_name);
> + return;
> + }
> + }
> +
> + strncpy(uprobe_reg, sdt_reg, sdt_len);
> +}
> +
> +int __perf_sdt_arg_parse_op_x86(char *old_op, char **new_op)
> +{
> + char new_reg[SDT_REG_NAME_SIZE] = {0};
> + int new_len = 0, ret;
> + /*
> + * rm[0]: +/-NUM(REG)
> + * rm[1]: +/-
> + * rm[2]: NUM
> + * rm[3]: (
> + * rm[4]: REG
> + * rm[5]: )
> + */
> + regmatch_t rm[6];
> + /*
> + * Max prefix length is 2 as it may contains sign(+/-)
> + * and displacement 0 (Both sign and displacement 0 are
> + * optional so it may be empty). Use one more character
> + * to hold last NULL so that strlen can be used to find
> + * prefix length, instead of maintaining one more variable.
> + */
> + char prefix[3] = {0};
> +
> + ret = sdt_init_op_regex();
> + if (ret < 0)
> + return ret;
> +
> + /*
> + * If unsupported OR does not match with regex OR
> + * register name too long, skip it.
> + */
> + if (strchr(old_op, ',') || strchr(old_op, '$') ||
> + regexec(&sdt_op_regex, old_op, 6, rm, 0) ||
> + rm[4].rm_eo - rm[4].rm_so > SDT_REG_NAME_SIZE) {
> + pr_debug4("Skipping unsupported SDT argument: %s\n", old_op);
> + return SDT_ARG_SKIP;
> + }
> +
> + /*
> + * Prepare prefix.
> + * If SDT OP has parenthesis but does not provide
> + * displacement, add 0 for displacement.
> + * SDT Uprobe Prefix
> + * -----------------------------
> + * +24(%rdi) +24(%di) +
> + * 24(%rdi) +24(%di) +
> + * %rdi %di
> + * (%rdi) +0(%di) +0
> + * -80(%rbx) -80(%bx) -
> + */
> + if (rm[3].rm_so != rm[3].rm_eo) {
> + if (rm[1].rm_so != rm[1].rm_eo)
> + prefix[0] = *(old_op + rm[1].rm_so);
> + else if (rm[2].rm_so != rm[2].rm_eo)
> + prefix[0] = '+';
> + else
> + scnprintf(prefix, sizeof(prefix), "+0");
> + }
> +
> + /* Rename register */
> + sdt_rename_register(old_op + rm[4].rm_so, rm[4].rm_eo - rm[4].rm_so,
> + new_reg);
> +
> + /* Prepare final OP which should be valid for uprobe_events */
> + new_len = strlen(prefix) +
> + (rm[2].rm_eo - rm[2].rm_so) +
> + (rm[3].rm_eo - rm[3].rm_so) +
> + strlen(new_reg) +
> + (rm[5].rm_eo - rm[5].rm_so) +
> + 1; /* NULL */
> +
> + *new_op = zalloc(new_len);
> + if (!*new_op)
> + return -ENOMEM;
> +
> + scnprintf(*new_op, new_len, "%.*s%.*s%.*s%.*s%.*s",
> + strlen(prefix), prefix,
> + (int)(rm[2].rm_eo - rm[2].rm_so), old_op + rm[2].rm_so,
> + (int)(rm[3].rm_eo - rm[3].rm_so), old_op + rm[3].rm_so,
> + strlen(new_reg), new_reg,
> + (int)(rm[5].rm_eo - rm[5].rm_so), old_op + rm[5].rm_so);
> +
> + return SDT_ARG_VALID;
> +}
> +
> +uint64_t __perf_reg_mask_x86(bool intr)
> +{
> + struct perf_event_attr attr = {
> + .type = PERF_TYPE_HARDWARE,
> + .config = PERF_COUNT_HW_CPU_CYCLES,
> + .sample_type = PERF_SAMPLE_REGS_INTR,
> + .sample_regs_intr = PERF_REG_EXTENDED_MASK,
> + .precise_ip = 1,
> + .disabled = 1,
> + .exclude_kernel = 1,
> + };
> + int fd;
> +
> + if (!intr)
> + return PERF_REGS_MASK;
> +
> + /*
> + * In an unnamed union, init it here to build on older gcc versions
> + */
> + attr.sample_period = 1;
> +
> + if (perf_pmus__num_core_pmus() > 1) {
> + struct perf_pmu *pmu = NULL;
> + __u64 type = PERF_TYPE_RAW;
> +
> + /*
> + * The same register set is supported among different hybrid PMUs.
> + * Only check the first available one.
> + */
> + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {
> + type = pmu->type;
> + break;
> + }
> + attr.config |= type << PERF_PMU_TYPE_SHIFT;
> + }
> +
> + event_attr_init(&attr);
> +
> + fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
> + if (fd != -1) {
> + close(fd);
> + return (PERF_REG_EXTENDED_MASK | PERF_REGS_MASK);
> + }
> +
> + return PERF_REGS_MASK;
> +}
>
> const char *__perf_reg_name_x86(int id)
> {
> diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c
> index 5f61e0f51d40..8fd910f0a4cf 100644
> --- a/tools/perf/util/perf_regs.c
> +++ b/tools/perf/util/perf_regs.c
> @@ -10,20 +10,109 @@
> #define EM_LOONGARCH 258
> #endif
>
> -int __weak arch_sdt_arg_parse_op(char *old_op __maybe_unused,
> - char **new_op __maybe_unused)
> +int perf_sdt_arg_parse_op(uint16_t e_machine, char *old_op, char **new_op)
> {
> - return SDT_ARG_SKIP;
> + int ret = SDT_ARG_SKIP;
> +
> + switch (e_machine) {
> + case EM_AARCH64:
> + ret = __perf_sdt_arg_parse_op_arm64(old_op, new_op);
> + break;
> + case EM_PPC:
> + case EM_PPC64:
> + ret =__perf_sdt_arg_parse_op_powerpc(old_op, new_op);
> + break;
> + case EM_386:
> + case EM_X86_64:
> + ret = __perf_sdt_arg_parse_op_x86(old_op, new_op);
> + break;
> + default:
> + break;
> + }
> +
> + return ret;
> }
>
> -uint64_t __weak arch__intr_reg_mask(void)
> +uint64_t perf_intr_reg_mask(uint16_t e_machine)
> {
> - return 0;
> + uint64_t mask = 0;
> +
> + switch (e_machine) {
> + case EM_ARM:
> + mask = __perf_reg_mask_arm(/*intr=*/true);
> + break;
> + case EM_AARCH64:
> + mask = __perf_reg_mask_arm64(/*intr=*/true);
> + break;
> + case EM_CSKY:
> + mask = __perf_reg_mask_csky(/*intr=*/true);
> + break;
> + case EM_LOONGARCH:
> + mask = __perf_reg_mask_loongarch(/*intr=*/true);
> + break;
> + case EM_MIPS:
> + mask = __perf_reg_mask_mips(/*intr=*/true);
> + break;
> + case EM_PPC:
> + case EM_PPC64:
> + mask = __perf_reg_mask_powerpc(/*intr=*/true);
> + break;
> + case EM_RISCV:
> + mask = __perf_reg_mask_riscv(/*intr=*/true);
> + break;
> + case EM_S390:
> + mask = __perf_reg_mask_s390(/*intr=*/true);
> + break;
> + case EM_386:
> + case EM_X86_64:
> + mask = __perf_reg_mask_x86(/*intr=*/true);
> + break;
> + default:
> + break;
> + }
> +
> + return mask;
> }
>
> -uint64_t __weak arch__user_reg_mask(void)
> +uint64_t perf_user_reg_mask(uint16_t e_machine)
> {
> - return 0;
> + uint64_t mask = 0;
> +
> + switch (e_machine) {
> + case EM_ARM:
> + mask = __perf_reg_mask_arm(/*intr=*/false);
> + break;
> + case EM_AARCH64:
> + mask = __perf_reg_mask_arm64(/*intr=*/false);
> + break;
> + case EM_CSKY:
> + mask = __perf_reg_mask_csky(/*intr=*/false);
> + break;
> + case EM_LOONGARCH:
> + mask = __perf_reg_mask_loongarch(/*intr=*/false);
> + break;
> + case EM_MIPS:
> + mask = __perf_reg_mask_mips(/*intr=*/false);
> + break;
> + case EM_PPC:
> + case EM_PPC64:
> + mask = __perf_reg_mask_powerpc(/*intr=*/false);
> + break;
> + case EM_RISCV:
> + mask = __perf_reg_mask_riscv(/*intr=*/false);
> + break;
> + case EM_S390:
> + mask = __perf_reg_mask_s390(/*intr=*/false);
> + break;
> + case EM_386:
> + case EM_X86_64:
> + mask = __perf_reg_mask_x86(/*intr=*/false);
> + break;
> + default:
> + break;
> + }
> +
> + return mask;
> }
>
> const char *perf_reg_name(int id, uint16_t e_machine)
> diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h
> index 2c2a8de6912d..77c0b517b94a 100644
> --- a/tools/perf/util/perf_regs.h
> +++ b/tools/perf/util/perf_regs.h
> @@ -12,38 +12,59 @@ enum {
> SDT_ARG_SKIP,
> };
>
> -int arch_sdt_arg_parse_op(char *old_op, char **new_op);
> -uint64_t arch__intr_reg_mask(void);
> -uint64_t arch__user_reg_mask(void);
> +int perf_sdt_arg_parse_op(uint16_t e_machine, char *old_op, char **new_op);
> +uint64_t perf_intr_reg_mask(uint16_t e_machine);
> +uint64_t perf_user_reg_mask(uint16_t e_machine);
>
> const char *perf_reg_name(int id, uint16_t e_machine);
> int perf_reg_value(u64 *valp, struct regs_dump *regs, int id);
> uint64_t perf_arch_reg_ip(uint16_t e_machine);
> uint64_t perf_arch_reg_sp(uint16_t e_machine);
> +
> +int __perf_sdt_arg_parse_op_arm64(char *old_op, char **new_op);
> +uint64_t __perf_reg_mask_arm64(bool intr);
> 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_arm(bool intr);
> const char *__perf_reg_name_arm(int id);
> uint64_t __perf_reg_ip_arm(void);
> uint64_t __perf_reg_sp_arm(void);
> +
> +uint64_t __perf_reg_mask_csky(bool intr);
> const char *__perf_reg_name_csky(int id);
> uint64_t __perf_reg_ip_csky(void);
> uint64_t __perf_reg_sp_csky(void);
> +
> +uint64_t __perf_reg_mask_loongarch(bool intr);
> const char *__perf_reg_name_loongarch(int id);
> uint64_t __perf_reg_ip_loongarch(void);
> uint64_t __perf_reg_sp_loongarch(void);
> +
> +uint64_t __perf_reg_mask_mips(bool intr);
> const char *__perf_reg_name_mips(int id);
> uint64_t __perf_reg_ip_mips(void);
> uint64_t __perf_reg_sp_mips(void);
> +
> +int __perf_sdt_arg_parse_op_powerpc(char *old_op, char **new_op);
> +uint64_t __perf_reg_mask_powerpc(bool intr);
> const char *__perf_reg_name_powerpc(int id);
> uint64_t __perf_reg_ip_powerpc(void);
> uint64_t __perf_reg_sp_powerpc(void);
> +
> +uint64_t __perf_reg_mask_riscv(bool intr);
> const char *__perf_reg_name_riscv(int id);
> uint64_t __perf_reg_ip_riscv(void);
> uint64_t __perf_reg_sp_riscv(void);
> +
> +uint64_t __perf_reg_mask_s390(bool intr);
> const char *__perf_reg_name_s390(int id);
> uint64_t __perf_reg_ip_s390(void);
> uint64_t __perf_reg_sp_s390(void);
> +
> +int __perf_sdt_arg_parse_op_x86(char *old_op, char **new_op);
> +uint64_t __perf_reg_mask_x86(bool intr);
> const char *__perf_reg_name_x86(int id);
> uint64_t __perf_reg_ip_x86(void);
> uint64_t __perf_reg_sp_x86(void);
> diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c
> index 5069fb61f48c..f78c3bc3d601 100644
> --- a/tools/perf/util/probe-file.c
> +++ b/tools/perf/util/probe-file.c
> @@ -28,6 +28,7 @@
> #include "session.h"
> #include "perf_regs.h"
> #include "string2.h"
> +#include "dwarf-regs.h"
>
> /* 4096 - 2 ('\n' + '\0') */
> #define MAX_CMDLEN 4094
> @@ -784,7 +785,7 @@ static int synthesize_sdt_probe_arg(struct strbuf *buf, int i, const char *arg)
> op = desc;
> }
>
> - ret = arch_sdt_arg_parse_op(op, &new_op);
> + ret = perf_sdt_arg_parse_op(EM_HOST, op, &new_op);
>
> if (ret < 0)
> goto error;
> --
> 2.34.1
>
Powered by blists - more mailing lists