222 lines
5.0 KiB
C
222 lines
5.0 KiB
C
// 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)
|
|
*
|
|
* Note: 'zero' (x0) is hardwired to 0 and not in pt_regs; skip it.
|
|
*
|
|
* Uprobe target format:
|
|
* Register: %REG e.g. %a0
|
|
* Memory: +NUM(%REG) or -NUM(%REG)
|
|
*/
|
|
|
|
/* RISC-V register ABI names: ra, sp, gp, tp, t0-t6, s0-s11, a0-a7 */
|
|
#define SDT_OP_REGEX1 "^(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]+)\\((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;
|
|
}
|
|
|
|
const char *__perf_reg_name_riscv(int id)
|
|
{
|
|
switch (id) {
|
|
case PERF_REG_RISCV_PC:
|
|
return "pc";
|
|
case PERF_REG_RISCV_RA:
|
|
return "ra";
|
|
case PERF_REG_RISCV_SP:
|
|
return "sp";
|
|
case PERF_REG_RISCV_GP:
|
|
return "gp";
|
|
case PERF_REG_RISCV_TP:
|
|
return "tp";
|
|
case PERF_REG_RISCV_T0:
|
|
return "t0";
|
|
case PERF_REG_RISCV_T1:
|
|
return "t1";
|
|
case PERF_REG_RISCV_T2:
|
|
return "t2";
|
|
case PERF_REG_RISCV_S0:
|
|
return "s0";
|
|
case PERF_REG_RISCV_S1:
|
|
return "s1";
|
|
case PERF_REG_RISCV_A0:
|
|
return "a0";
|
|
case PERF_REG_RISCV_A1:
|
|
return "a1";
|
|
case PERF_REG_RISCV_A2:
|
|
return "a2";
|
|
case PERF_REG_RISCV_A3:
|
|
return "a3";
|
|
case PERF_REG_RISCV_A4:
|
|
return "a4";
|
|
case PERF_REG_RISCV_A5:
|
|
return "a5";
|
|
case PERF_REG_RISCV_A6:
|
|
return "a6";
|
|
case PERF_REG_RISCV_A7:
|
|
return "a7";
|
|
case PERF_REG_RISCV_S2:
|
|
return "s2";
|
|
case PERF_REG_RISCV_S3:
|
|
return "s3";
|
|
case PERF_REG_RISCV_S4:
|
|
return "s4";
|
|
case PERF_REG_RISCV_S5:
|
|
return "s5";
|
|
case PERF_REG_RISCV_S6:
|
|
return "s6";
|
|
case PERF_REG_RISCV_S7:
|
|
return "s7";
|
|
case PERF_REG_RISCV_S8:
|
|
return "s8";
|
|
case PERF_REG_RISCV_S9:
|
|
return "s9";
|
|
case PERF_REG_RISCV_S10:
|
|
return "s10";
|
|
case PERF_REG_RISCV_S11:
|
|
return "s11";
|
|
case PERF_REG_RISCV_T3:
|
|
return "t3";
|
|
case PERF_REG_RISCV_T4:
|
|
return "t4";
|
|
case PERF_REG_RISCV_T5:
|
|
return "t5";
|
|
case PERF_REG_RISCV_T6:
|
|
return "t6";
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
uint64_t __perf_reg_ip_riscv(void)
|
|
{
|
|
return PERF_REG_RISCV_PC;
|
|
}
|
|
|
|
uint64_t __perf_reg_sp_riscv(void)
|
|
{
|
|
return PERF_REG_RISCV_SP;
|
|
}
|