Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions tools/perf/util/perf-regs-arch/perf_regs_riscv.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,134 @@
// SPDX-License-Identifier: GPL-2.0

#include <errno.h>
#include <regex.h>
#include <string.h>
#include <linux/kernel.h>
#include <linux/zalloc.h>

#include "../debug.h"
#include "../perf_regs.h"
#include "../../arch/riscv/include/perf_regs.h"

/*
* RISC-V SDT argument formats (GCC 'nor' constraint):
*
* Register: REG e.g. a0, t1, s0, sp
* Memory: NUM(REG) e.g. 8(a0), -20(s0)
* Constant: NUM e.g. 99 (not supported by uprobe, skip)
*
* Uprobe target format:
* Register: %REG e.g. %a0
* Memory: +NUM(%REG) or -NUM(%REG)
*/

/* RISC-V register ABI names: zero, ra, sp, gp, tp, t0-t6, s0-s11, a0-a7 */
#define SDT_OP_REGEX1 "^(zero|ra|sp|gp|tp|t[0-6]|s[0-9]|s1[01]|a[0-7])$"

/* RISC-V memory operand: [-]NUM(REG) */
#define SDT_OP_REGEX2 "^(\\-)?([0-9]+)\\((zero|ra|sp|gp|tp|t[0-6]|s[0-9]|s1[01]|a[0-7])\\)$"

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.
* Possible variants of OP (RISC-V, GCC 'nor' constraint):
*
* Format Example Uprobe
* ----------------------------------------
* REG a0 %a0
* NUM(REG) 8(a0) +8(%a0)
* -NUM(REG) -20(s0) -20(%s0)
* NUM 99 (skip, constant not supported)
*/
int __perf_sdt_arg_parse_op_riscv(char *old_op, char **new_op)
{
int ret, new_len;
regmatch_t rm[4];
char prefix;

/*
* Constant argument: pure integer with no trailing '(' (e.g. "99", "-1").
* uprobe does not support immediate values, so skip them.
* Memory operands like "8(a0)" or "-20(s0)" contain '(' so are NOT
* treated as constants here; they will be matched by REGEX2 below.
*/
if (strchr(old_op, '(') == NULL &&
((*old_op >= '0' && *old_op <= '9') ||
(*old_op == '-' && old_op[1] >= '0' && old_op[1] <= '9'))) {
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, 2, rm, 0)) {
/* REG --> %REG */
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, 4, rm, 0)) {
/*
* NUM(REG) or -NUM(REG) --> +NUM(%REG) or -NUM(%REG)
* rm[1]: optional '-'
* rm[2]: decimal offset
* rm[3]: register name
*/
prefix = (rm[1].rm_so == -1) ? '+' : '-';

new_len = 5; /* sign ( % ) NULL */
new_len += (int)(rm[2].rm_eo - rm[2].rm_so);
new_len += (int)(rm[3].rm_eo - rm[3].rm_so);

*new_op = zalloc(new_len);
if (!*new_op)
return -ENOMEM;

scnprintf(*new_op, new_len, "%c%.*s(%%%.*s)", 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);
} else {
pr_debug4("Skipping unsupported SDT argument: %s\n", old_op);
return SDT_ARG_SKIP;
}

return SDT_ARG_VALID;
}

uint64_t __perf_reg_mask_riscv(bool intr __maybe_unused)
{
return PERF_REGS_MASK;
Expand Down
3 changes: 3 additions & 0 deletions tools/perf/util/perf_regs.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ int perf_sdt_arg_parse_op(uint16_t e_machine, char *old_op, char **new_op)
case EM_PPC64:
ret = __perf_sdt_arg_parse_op_powerpc(old_op, new_op);
break;
case EM_RISCV:
ret = __perf_sdt_arg_parse_op_riscv(old_op, new_op);
break;
case EM_386:
case EM_X86_64:
ret = __perf_sdt_arg_parse_op_x86(old_op, new_op);
Expand Down
1 change: 1 addition & 0 deletions tools/perf/util/perf_regs.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const char *__perf_reg_name_powerpc(int id);
uint64_t __perf_reg_ip_powerpc(void);
uint64_t __perf_reg_sp_powerpc(void);

int __perf_sdt_arg_parse_op_riscv(char *old_op, char **new_op);
uint64_t __perf_reg_mask_riscv(bool intr);
const char *__perf_reg_name_riscv(int id);
uint64_t __perf_reg_ip_riscv(void);
Expand Down
Loading