linux/tools/perf/arch/arm64/util/perf_regs.c
Leo Yan 9a4e47ef98 perf parse-regs: Introduce a weak function arch__sample_reg_masks()
Every architecture can provide a register list for sampling. If an
architecture doesn't support register sampling, it won't define the data
structure 'sample_reg_masks'. Consequently, any code using this
structure must be protected by the macro 'HAVE_PERF_REGS_SUPPORT'.

This patch defines a weak function, arch__sample_reg_masks(), which will
be replaced by an architecture-defined function for returning the
architecture's register list. With this refactoring, the function always
exists, the condition checking for 'HAVE_PERF_REGS_SUPPORT' is not
needed anymore, so remove it.

Signed-off-by: Leo Yan <leo.yan@linux.dev>
Reviewed-by: Ian Rogers <irogers@google.com>
Cc: James Clark <james.clark@arm.com>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Huacai Chen <chenhuacai@kernel.org>
Cc: Guo Ren <guoren@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: Mike Leach <mike.leach@linaro.org>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Ming Wang <wangming01@loongson.cn>
Cc: John Garry <john.g.garry@oracle.com>
Cc: Paul Walmsley <paul.walmsley@sifive.com>
Cc: linux-csky@vger.kernel.org
Cc: linux-riscv@lists.infradead.org
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Link: https://lore.kernel.org/r/20240214113947.240957-4-leo.yan@linux.dev
2024-02-15 13:48:36 -08:00

182 lines
4.3 KiB
C

// 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"
#ifndef HWCAP_SVE
#define HWCAP_SVE (1 << 22)
#endif
static const struct sample_reg sample_reg_masks[] = {
SMPL_REG(x0, PERF_REG_ARM64_X0),
SMPL_REG(x1, PERF_REG_ARM64_X1),
SMPL_REG(x2, PERF_REG_ARM64_X2),
SMPL_REG(x3, PERF_REG_ARM64_X3),
SMPL_REG(x4, PERF_REG_ARM64_X4),
SMPL_REG(x5, PERF_REG_ARM64_X5),
SMPL_REG(x6, PERF_REG_ARM64_X6),
SMPL_REG(x7, PERF_REG_ARM64_X7),
SMPL_REG(x8, PERF_REG_ARM64_X8),
SMPL_REG(x9, PERF_REG_ARM64_X9),
SMPL_REG(x10, PERF_REG_ARM64_X10),
SMPL_REG(x11, PERF_REG_ARM64_X11),
SMPL_REG(x12, PERF_REG_ARM64_X12),
SMPL_REG(x13, PERF_REG_ARM64_X13),
SMPL_REG(x14, PERF_REG_ARM64_X14),
SMPL_REG(x15, PERF_REG_ARM64_X15),
SMPL_REG(x16, PERF_REG_ARM64_X16),
SMPL_REG(x17, PERF_REG_ARM64_X17),
SMPL_REG(x18, PERF_REG_ARM64_X18),
SMPL_REG(x19, PERF_REG_ARM64_X19),
SMPL_REG(x20, PERF_REG_ARM64_X20),
SMPL_REG(x21, PERF_REG_ARM64_X21),
SMPL_REG(x22, PERF_REG_ARM64_X22),
SMPL_REG(x23, PERF_REG_ARM64_X23),
SMPL_REG(x24, PERF_REG_ARM64_X24),
SMPL_REG(x25, PERF_REG_ARM64_X25),
SMPL_REG(x26, PERF_REG_ARM64_X26),
SMPL_REG(x27, PERF_REG_ARM64_X27),
SMPL_REG(x28, PERF_REG_ARM64_X28),
SMPL_REG(x29, PERF_REG_ARM64_X29),
SMPL_REG(lr, PERF_REG_ARM64_LR),
SMPL_REG(sp, PERF_REG_ARM64_SP),
SMPL_REG(pc, PERF_REG_ARM64_PC),
SMPL_REG(vg, PERF_REG_ARM64_VG),
SMPL_REG_END
};
/* %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;
}
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;
}
const struct sample_reg *arch__sample_reg_masks(void)
{
return sample_reg_masks;
}