162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <errno.h> 362306a36Sopenharmony_ci#include <string.h> 462306a36Sopenharmony_ci#include <regex.h> 562306a36Sopenharmony_ci#include <linux/kernel.h> 662306a36Sopenharmony_ci#include <linux/zalloc.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "perf_regs.h" 962306a36Sopenharmony_ci#include "../../../perf-sys.h" 1062306a36Sopenharmony_ci#include "../../../util/perf_regs.h" 1162306a36Sopenharmony_ci#include "../../../util/debug.h" 1262306a36Sopenharmony_ci#include "../../../util/event.h" 1362306a36Sopenharmony_ci#include "../../../util/pmu.h" 1462306a36Sopenharmony_ci#include "../../../util/pmus.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ciconst struct sample_reg sample_reg_masks[] = { 1762306a36Sopenharmony_ci SMPL_REG(AX, PERF_REG_X86_AX), 1862306a36Sopenharmony_ci SMPL_REG(BX, PERF_REG_X86_BX), 1962306a36Sopenharmony_ci SMPL_REG(CX, PERF_REG_X86_CX), 2062306a36Sopenharmony_ci SMPL_REG(DX, PERF_REG_X86_DX), 2162306a36Sopenharmony_ci SMPL_REG(SI, PERF_REG_X86_SI), 2262306a36Sopenharmony_ci SMPL_REG(DI, PERF_REG_X86_DI), 2362306a36Sopenharmony_ci SMPL_REG(BP, PERF_REG_X86_BP), 2462306a36Sopenharmony_ci SMPL_REG(SP, PERF_REG_X86_SP), 2562306a36Sopenharmony_ci SMPL_REG(IP, PERF_REG_X86_IP), 2662306a36Sopenharmony_ci SMPL_REG(FLAGS, PERF_REG_X86_FLAGS), 2762306a36Sopenharmony_ci SMPL_REG(CS, PERF_REG_X86_CS), 2862306a36Sopenharmony_ci SMPL_REG(SS, PERF_REG_X86_SS), 2962306a36Sopenharmony_ci#ifdef HAVE_ARCH_X86_64_SUPPORT 3062306a36Sopenharmony_ci SMPL_REG(R8, PERF_REG_X86_R8), 3162306a36Sopenharmony_ci SMPL_REG(R9, PERF_REG_X86_R9), 3262306a36Sopenharmony_ci SMPL_REG(R10, PERF_REG_X86_R10), 3362306a36Sopenharmony_ci SMPL_REG(R11, PERF_REG_X86_R11), 3462306a36Sopenharmony_ci SMPL_REG(R12, PERF_REG_X86_R12), 3562306a36Sopenharmony_ci SMPL_REG(R13, PERF_REG_X86_R13), 3662306a36Sopenharmony_ci SMPL_REG(R14, PERF_REG_X86_R14), 3762306a36Sopenharmony_ci SMPL_REG(R15, PERF_REG_X86_R15), 3862306a36Sopenharmony_ci#endif 3962306a36Sopenharmony_ci SMPL_REG2(XMM0, PERF_REG_X86_XMM0), 4062306a36Sopenharmony_ci SMPL_REG2(XMM1, PERF_REG_X86_XMM1), 4162306a36Sopenharmony_ci SMPL_REG2(XMM2, PERF_REG_X86_XMM2), 4262306a36Sopenharmony_ci SMPL_REG2(XMM3, PERF_REG_X86_XMM3), 4362306a36Sopenharmony_ci SMPL_REG2(XMM4, PERF_REG_X86_XMM4), 4462306a36Sopenharmony_ci SMPL_REG2(XMM5, PERF_REG_X86_XMM5), 4562306a36Sopenharmony_ci SMPL_REG2(XMM6, PERF_REG_X86_XMM6), 4662306a36Sopenharmony_ci SMPL_REG2(XMM7, PERF_REG_X86_XMM7), 4762306a36Sopenharmony_ci SMPL_REG2(XMM8, PERF_REG_X86_XMM8), 4862306a36Sopenharmony_ci SMPL_REG2(XMM9, PERF_REG_X86_XMM9), 4962306a36Sopenharmony_ci SMPL_REG2(XMM10, PERF_REG_X86_XMM10), 5062306a36Sopenharmony_ci SMPL_REG2(XMM11, PERF_REG_X86_XMM11), 5162306a36Sopenharmony_ci SMPL_REG2(XMM12, PERF_REG_X86_XMM12), 5262306a36Sopenharmony_ci SMPL_REG2(XMM13, PERF_REG_X86_XMM13), 5362306a36Sopenharmony_ci SMPL_REG2(XMM14, PERF_REG_X86_XMM14), 5462306a36Sopenharmony_ci SMPL_REG2(XMM15, PERF_REG_X86_XMM15), 5562306a36Sopenharmony_ci SMPL_REG_END 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistruct sdt_name_reg { 5962306a36Sopenharmony_ci const char *sdt_name; 6062306a36Sopenharmony_ci const char *uprobe_name; 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci#define SDT_NAME_REG(n, m) {.sdt_name = "%" #n, .uprobe_name = "%" #m} 6362306a36Sopenharmony_ci#define SDT_NAME_REG_END {.sdt_name = NULL, .uprobe_name = NULL} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic const struct sdt_name_reg sdt_reg_tbl[] = { 6662306a36Sopenharmony_ci SDT_NAME_REG(eax, ax), 6762306a36Sopenharmony_ci SDT_NAME_REG(rax, ax), 6862306a36Sopenharmony_ci SDT_NAME_REG(al, ax), 6962306a36Sopenharmony_ci SDT_NAME_REG(ah, ax), 7062306a36Sopenharmony_ci SDT_NAME_REG(ebx, bx), 7162306a36Sopenharmony_ci SDT_NAME_REG(rbx, bx), 7262306a36Sopenharmony_ci SDT_NAME_REG(bl, bx), 7362306a36Sopenharmony_ci SDT_NAME_REG(bh, bx), 7462306a36Sopenharmony_ci SDT_NAME_REG(ecx, cx), 7562306a36Sopenharmony_ci SDT_NAME_REG(rcx, cx), 7662306a36Sopenharmony_ci SDT_NAME_REG(cl, cx), 7762306a36Sopenharmony_ci SDT_NAME_REG(ch, cx), 7862306a36Sopenharmony_ci SDT_NAME_REG(edx, dx), 7962306a36Sopenharmony_ci SDT_NAME_REG(rdx, dx), 8062306a36Sopenharmony_ci SDT_NAME_REG(dl, dx), 8162306a36Sopenharmony_ci SDT_NAME_REG(dh, dx), 8262306a36Sopenharmony_ci SDT_NAME_REG(esi, si), 8362306a36Sopenharmony_ci SDT_NAME_REG(rsi, si), 8462306a36Sopenharmony_ci SDT_NAME_REG(sil, si), 8562306a36Sopenharmony_ci SDT_NAME_REG(edi, di), 8662306a36Sopenharmony_ci SDT_NAME_REG(rdi, di), 8762306a36Sopenharmony_ci SDT_NAME_REG(dil, di), 8862306a36Sopenharmony_ci SDT_NAME_REG(ebp, bp), 8962306a36Sopenharmony_ci SDT_NAME_REG(rbp, bp), 9062306a36Sopenharmony_ci SDT_NAME_REG(bpl, bp), 9162306a36Sopenharmony_ci SDT_NAME_REG(rsp, sp), 9262306a36Sopenharmony_ci SDT_NAME_REG(esp, sp), 9362306a36Sopenharmony_ci SDT_NAME_REG(spl, sp), 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* rNN registers */ 9662306a36Sopenharmony_ci SDT_NAME_REG(r8b, r8), 9762306a36Sopenharmony_ci SDT_NAME_REG(r8w, r8), 9862306a36Sopenharmony_ci SDT_NAME_REG(r8d, r8), 9962306a36Sopenharmony_ci SDT_NAME_REG(r9b, r9), 10062306a36Sopenharmony_ci SDT_NAME_REG(r9w, r9), 10162306a36Sopenharmony_ci SDT_NAME_REG(r9d, r9), 10262306a36Sopenharmony_ci SDT_NAME_REG(r10b, r10), 10362306a36Sopenharmony_ci SDT_NAME_REG(r10w, r10), 10462306a36Sopenharmony_ci SDT_NAME_REG(r10d, r10), 10562306a36Sopenharmony_ci SDT_NAME_REG(r11b, r11), 10662306a36Sopenharmony_ci SDT_NAME_REG(r11w, r11), 10762306a36Sopenharmony_ci SDT_NAME_REG(r11d, r11), 10862306a36Sopenharmony_ci SDT_NAME_REG(r12b, r12), 10962306a36Sopenharmony_ci SDT_NAME_REG(r12w, r12), 11062306a36Sopenharmony_ci SDT_NAME_REG(r12d, r12), 11162306a36Sopenharmony_ci SDT_NAME_REG(r13b, r13), 11262306a36Sopenharmony_ci SDT_NAME_REG(r13w, r13), 11362306a36Sopenharmony_ci SDT_NAME_REG(r13d, r13), 11462306a36Sopenharmony_ci SDT_NAME_REG(r14b, r14), 11562306a36Sopenharmony_ci SDT_NAME_REG(r14w, r14), 11662306a36Sopenharmony_ci SDT_NAME_REG(r14d, r14), 11762306a36Sopenharmony_ci SDT_NAME_REG(r15b, r15), 11862306a36Sopenharmony_ci SDT_NAME_REG(r15w, r15), 11962306a36Sopenharmony_ci SDT_NAME_REG(r15d, r15), 12062306a36Sopenharmony_ci SDT_NAME_REG_END, 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* 12462306a36Sopenharmony_ci * Perf only supports OP which is in +/-NUM(REG) form. 12562306a36Sopenharmony_ci * Here plus-minus sign, NUM and parenthesis are optional, 12662306a36Sopenharmony_ci * only REG is mandatory. 12762306a36Sopenharmony_ci * 12862306a36Sopenharmony_ci * SDT events also supports indirect addressing mode with a 12962306a36Sopenharmony_ci * symbol as offset, scaled mode and constants in OP. But 13062306a36Sopenharmony_ci * perf does not support them yet. Below are few examples. 13162306a36Sopenharmony_ci * 13262306a36Sopenharmony_ci * OP with scaled mode: 13362306a36Sopenharmony_ci * (%rax,%rsi,8) 13462306a36Sopenharmony_ci * 10(%ras,%rsi,8) 13562306a36Sopenharmony_ci * 13662306a36Sopenharmony_ci * OP with indirect addressing mode: 13762306a36Sopenharmony_ci * check_action(%rip) 13862306a36Sopenharmony_ci * mp_+52(%rip) 13962306a36Sopenharmony_ci * 44+mp_(%rip) 14062306a36Sopenharmony_ci * 14162306a36Sopenharmony_ci * OP with constant values: 14262306a36Sopenharmony_ci * $0 14362306a36Sopenharmony_ci * $123 14462306a36Sopenharmony_ci * $-1 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_ci#define SDT_OP_REGEX "^([+\\-]?)([0-9]*)(\\(?)(%[a-z][a-z0-9]+)(\\)?)$" 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic regex_t sdt_op_regex; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int sdt_init_op_regex(void) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci static int initialized; 15362306a36Sopenharmony_ci int ret = 0; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (initialized) 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci ret = regcomp(&sdt_op_regex, SDT_OP_REGEX, REG_EXTENDED); 15962306a36Sopenharmony_ci if (ret < 0) { 16062306a36Sopenharmony_ci pr_debug4("Regex compilation error.\n"); 16162306a36Sopenharmony_ci return ret; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci initialized = 1; 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/* 16962306a36Sopenharmony_ci * Max x86 register name length is 5(ex: %r15d). So, 6th char 17062306a36Sopenharmony_ci * should always contain NULL. This helps to find register name 17162306a36Sopenharmony_ci * length using strlen, instead of maintaining one more variable. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci#define SDT_REG_NAME_SIZE 6 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci/* 17662306a36Sopenharmony_ci * The uprobe parser does not support all gas register names; 17762306a36Sopenharmony_ci * so, we have to replace them (ex. for x86_64: %rax -> %ax). 17862306a36Sopenharmony_ci * Note: If register does not require renaming, just copy 17962306a36Sopenharmony_ci * paste as it is, but don't leave it empty. 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_cistatic void sdt_rename_register(char *sdt_reg, int sdt_len, char *uprobe_reg) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci int i = 0; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci for (i = 0; sdt_reg_tbl[i].sdt_name != NULL; i++) { 18662306a36Sopenharmony_ci if (!strncmp(sdt_reg_tbl[i].sdt_name, sdt_reg, sdt_len)) { 18762306a36Sopenharmony_ci strcpy(uprobe_reg, sdt_reg_tbl[i].uprobe_name); 18862306a36Sopenharmony_ci return; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci strncpy(uprobe_reg, sdt_reg, sdt_len); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ciint arch_sdt_arg_parse_op(char *old_op, char **new_op) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci char new_reg[SDT_REG_NAME_SIZE] = {0}; 19862306a36Sopenharmony_ci int new_len = 0, ret; 19962306a36Sopenharmony_ci /* 20062306a36Sopenharmony_ci * rm[0]: +/-NUM(REG) 20162306a36Sopenharmony_ci * rm[1]: +/- 20262306a36Sopenharmony_ci * rm[2]: NUM 20362306a36Sopenharmony_ci * rm[3]: ( 20462306a36Sopenharmony_ci * rm[4]: REG 20562306a36Sopenharmony_ci * rm[5]: ) 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_ci regmatch_t rm[6]; 20862306a36Sopenharmony_ci /* 20962306a36Sopenharmony_ci * Max prefix length is 2 as it may contains sign(+/-) 21062306a36Sopenharmony_ci * and displacement 0 (Both sign and displacement 0 are 21162306a36Sopenharmony_ci * optional so it may be empty). Use one more character 21262306a36Sopenharmony_ci * to hold last NULL so that strlen can be used to find 21362306a36Sopenharmony_ci * prefix length, instead of maintaining one more variable. 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ci char prefix[3] = {0}; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci ret = sdt_init_op_regex(); 21862306a36Sopenharmony_ci if (ret < 0) 21962306a36Sopenharmony_ci return ret; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* 22262306a36Sopenharmony_ci * If unsupported OR does not match with regex OR 22362306a36Sopenharmony_ci * register name too long, skip it. 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_ci if (strchr(old_op, ',') || strchr(old_op, '$') || 22662306a36Sopenharmony_ci regexec(&sdt_op_regex, old_op, 6, rm, 0) || 22762306a36Sopenharmony_ci rm[4].rm_eo - rm[4].rm_so > SDT_REG_NAME_SIZE) { 22862306a36Sopenharmony_ci pr_debug4("Skipping unsupported SDT argument: %s\n", old_op); 22962306a36Sopenharmony_ci return SDT_ARG_SKIP; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci /* 23362306a36Sopenharmony_ci * Prepare prefix. 23462306a36Sopenharmony_ci * If SDT OP has parenthesis but does not provide 23562306a36Sopenharmony_ci * displacement, add 0 for displacement. 23662306a36Sopenharmony_ci * SDT Uprobe Prefix 23762306a36Sopenharmony_ci * ----------------------------- 23862306a36Sopenharmony_ci * +24(%rdi) +24(%di) + 23962306a36Sopenharmony_ci * 24(%rdi) +24(%di) + 24062306a36Sopenharmony_ci * %rdi %di 24162306a36Sopenharmony_ci * (%rdi) +0(%di) +0 24262306a36Sopenharmony_ci * -80(%rbx) -80(%bx) - 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci if (rm[3].rm_so != rm[3].rm_eo) { 24562306a36Sopenharmony_ci if (rm[1].rm_so != rm[1].rm_eo) 24662306a36Sopenharmony_ci prefix[0] = *(old_op + rm[1].rm_so); 24762306a36Sopenharmony_ci else if (rm[2].rm_so != rm[2].rm_eo) 24862306a36Sopenharmony_ci prefix[0] = '+'; 24962306a36Sopenharmony_ci else 25062306a36Sopenharmony_ci scnprintf(prefix, sizeof(prefix), "+0"); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* Rename register */ 25462306a36Sopenharmony_ci sdt_rename_register(old_op + rm[4].rm_so, rm[4].rm_eo - rm[4].rm_so, 25562306a36Sopenharmony_ci new_reg); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* Prepare final OP which should be valid for uprobe_events */ 25862306a36Sopenharmony_ci new_len = strlen(prefix) + 25962306a36Sopenharmony_ci (rm[2].rm_eo - rm[2].rm_so) + 26062306a36Sopenharmony_ci (rm[3].rm_eo - rm[3].rm_so) + 26162306a36Sopenharmony_ci strlen(new_reg) + 26262306a36Sopenharmony_ci (rm[5].rm_eo - rm[5].rm_so) + 26362306a36Sopenharmony_ci 1; /* NULL */ 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci *new_op = zalloc(new_len); 26662306a36Sopenharmony_ci if (!*new_op) 26762306a36Sopenharmony_ci return -ENOMEM; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci scnprintf(*new_op, new_len, "%.*s%.*s%.*s%.*s%.*s", 27062306a36Sopenharmony_ci strlen(prefix), prefix, 27162306a36Sopenharmony_ci (int)(rm[2].rm_eo - rm[2].rm_so), old_op + rm[2].rm_so, 27262306a36Sopenharmony_ci (int)(rm[3].rm_eo - rm[3].rm_so), old_op + rm[3].rm_so, 27362306a36Sopenharmony_ci strlen(new_reg), new_reg, 27462306a36Sopenharmony_ci (int)(rm[5].rm_eo - rm[5].rm_so), old_op + rm[5].rm_so); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return SDT_ARG_VALID; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ciuint64_t arch__intr_reg_mask(void) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct perf_event_attr attr = { 28262306a36Sopenharmony_ci .type = PERF_TYPE_HARDWARE, 28362306a36Sopenharmony_ci .config = PERF_COUNT_HW_CPU_CYCLES, 28462306a36Sopenharmony_ci .sample_type = PERF_SAMPLE_REGS_INTR, 28562306a36Sopenharmony_ci .sample_regs_intr = PERF_REG_EXTENDED_MASK, 28662306a36Sopenharmony_ci .precise_ip = 1, 28762306a36Sopenharmony_ci .disabled = 1, 28862306a36Sopenharmony_ci .exclude_kernel = 1, 28962306a36Sopenharmony_ci }; 29062306a36Sopenharmony_ci int fd; 29162306a36Sopenharmony_ci /* 29262306a36Sopenharmony_ci * In an unnamed union, init it here to build on older gcc versions 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci attr.sample_period = 1; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (perf_pmus__num_core_pmus() > 1) { 29762306a36Sopenharmony_ci struct perf_pmu *pmu = NULL; 29862306a36Sopenharmony_ci __u64 type = PERF_TYPE_RAW; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* 30162306a36Sopenharmony_ci * The same register set is supported among different hybrid PMUs. 30262306a36Sopenharmony_ci * Only check the first available one. 30362306a36Sopenharmony_ci */ 30462306a36Sopenharmony_ci while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { 30562306a36Sopenharmony_ci type = pmu->type; 30662306a36Sopenharmony_ci break; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci attr.config |= type << PERF_PMU_TYPE_SHIFT; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci event_attr_init(&attr); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci fd = sys_perf_event_open(&attr, 0, -1, -1, 0); 31462306a36Sopenharmony_ci if (fd != -1) { 31562306a36Sopenharmony_ci close(fd); 31662306a36Sopenharmony_ci return (PERF_REG_EXTENDED_MASK | PERF_REGS_MASK); 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return PERF_REGS_MASK; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ciuint64_t arch__user_reg_mask(void) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci return PERF_REGS_MASK; 32562306a36Sopenharmony_ci} 326