Commit d451a205 authored by Ravi Bangoria's avatar Ravi Bangoria Committed by Arnaldo Carvalho de Melo

perf/sdt/x86: Move OP parser to tools/perf/arch/x86/

SDT marker argument is in N@OP format. N is the size of argument and OP
is the actual assembly operand. OP is arch dependent component and hence
it's parsing logic also should be placed under tools/perf/arch/.
Signed-off-by: default avatarRavi Bangoria <ravi.bangoria@linux.vnet.ibm.com>
Acked-by: default avatarMasami Hiramatsu <mhiramat@kernel.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexis Berlemont <alexis.berlemont@gmail.com>
Cc: Hemant Kumar <hemant@linux.vnet.ibm.com>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20170328094754.3156-3-ravi.bangoria@linux.vnet.ibm.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 2d01ecc5
#include <string.h> #include <string.h>
#include <regex.h>
#include "../../perf.h" #include "../../perf.h"
#include "../../util/util.h" #include "../../util/util.h"
#include "../../util/perf_regs.h" #include "../../util/perf_regs.h"
#include "../../util/debug.h"
const struct sample_reg sample_reg_masks[] = { const struct sample_reg sample_reg_masks[] = {
SMPL_REG(AX, PERF_REG_X86_AX), SMPL_REG(AX, PERF_REG_X86_AX),
...@@ -37,7 +39,7 @@ struct sdt_name_reg { ...@@ -37,7 +39,7 @@ struct sdt_name_reg {
#define SDT_NAME_REG(n, m) {.sdt_name = "%" #n, .uprobe_name = "%" #m} #define SDT_NAME_REG(n, m) {.sdt_name = "%" #n, .uprobe_name = "%" #m}
#define SDT_NAME_REG_END {.sdt_name = NULL, .uprobe_name = NULL} #define SDT_NAME_REG_END {.sdt_name = NULL, .uprobe_name = NULL}
static const struct sdt_name_reg sdt_reg_renamings[] = { static const struct sdt_name_reg sdt_reg_tbl[] = {
SDT_NAME_REG(eax, ax), SDT_NAME_REG(eax, ax),
SDT_NAME_REG(rax, ax), SDT_NAME_REG(rax, ax),
SDT_NAME_REG(al, ax), SDT_NAME_REG(al, ax),
...@@ -95,45 +97,158 @@ static const struct sdt_name_reg sdt_reg_renamings[] = { ...@@ -95,45 +97,158 @@ static const struct sdt_name_reg sdt_reg_renamings[] = {
SDT_NAME_REG_END, SDT_NAME_REG_END,
}; };
int sdt_rename_register(char **pdesc, char *old_name) /*
* 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)
{ {
const struct sdt_name_reg *rnames = sdt_reg_renamings; static int initialized;
char *new_desc, *old_desc = *pdesc; int ret = 0;
size_t prefix_len, sdt_len, uprobe_len, old_desc_len, offset;
int ret = -1; if (initialized)
return 0;
while (ret != 0 && rnames->sdt_name != NULL) {
sdt_len = strlen(rnames->sdt_name); ret = regcomp(&sdt_op_regex, SDT_OP_REGEX, REG_EXTENDED);
ret = strncmp(old_name, rnames->sdt_name, sdt_len); if (ret < 0) {
rnames += !!ret; pr_debug4("Regex compilation error.\n");
return ret;
} }
if (rnames->sdt_name == NULL) initialized = 1;
return 0; 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, insted of maintaing 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 maintaing one more variable.
*/
char prefix[3] = {0};
sdt_len = strlen(rnames->sdt_name); ret = sdt_init_op_regex();
uprobe_len = strlen(rnames->uprobe_name); if (ret < 0)
old_desc_len = strlen(old_desc) + 1; return ret;
new_desc = zalloc(old_desc_len + uprobe_len - sdt_len); /*
if (new_desc == NULL) * If unsupported OR does not match with regex OR
return -1; * 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;
}
/* Copy the chars before the register name (at least '%') */ /*
prefix_len = old_name - old_desc; * Prepare prefix.
memcpy(new_desc, old_desc, prefix_len); * 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
strncpy(prefix, "+0", 2);
}
/* Copy the new register name */ /* Rename register */
memcpy(new_desc + prefix_len, rnames->uprobe_name, uprobe_len); sdt_rename_register(old_op + rm[4].rm_so, rm[4].rm_eo - rm[4].rm_so,
new_reg);
/* Copy the chars after the register name (if need be) */ /* Prepare final OP which should be valid for uprobe_events */
offset = prefix_len + sdt_len; new_len = strlen(prefix) +
if (offset < old_desc_len) (rm[2].rm_eo - rm[2].rm_so) +
memcpy(new_desc + prefix_len + uprobe_len, (rm[3].rm_eo - rm[3].rm_so) +
old_desc + offset, old_desc_len - offset); strlen(new_reg) +
(rm[5].rm_eo - rm[5].rm_so) +
1; /* NULL */
free(old_desc); *new_op = zalloc(new_len);
*pdesc = new_desc; if (!*new_op)
return -ENOMEM;
return 0; 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;
} }
...@@ -6,10 +6,10 @@ const struct sample_reg __weak sample_reg_masks[] = { ...@@ -6,10 +6,10 @@ const struct sample_reg __weak sample_reg_masks[] = {
SMPL_REG_END SMPL_REG_END
}; };
int __weak sdt_rename_register(char **pdesc __maybe_unused, int __weak arch_sdt_arg_parse_op(char *old_op __maybe_unused,
char *old_name __maybe_unused) char **new_op __maybe_unused)
{ {
return 0; return SDT_ARG_SKIP;
} }
#ifdef HAVE_PERF_REGS_SUPPORT #ifdef HAVE_PERF_REGS_SUPPORT
......
...@@ -15,11 +15,12 @@ struct sample_reg { ...@@ -15,11 +15,12 @@ struct sample_reg {
extern const struct sample_reg sample_reg_masks[]; extern const struct sample_reg sample_reg_masks[];
/* enum {
* The table sdt_reg_renamings is used for adjusting gcc/gas-generated SDT_ARG_VALID = 0,
* registers before filling the uprobe tracer interface. SDT_ARG_SKIP,
*/ };
int sdt_rename_register(char **pdesc, char *old_name);
int arch_sdt_arg_parse_op(char *old_op, char **new_op);
#ifdef HAVE_PERF_REGS_SUPPORT #ifdef HAVE_PERF_REGS_SUPPORT
#include <perf_regs.h> #include <perf_regs.h>
......
...@@ -694,123 +694,67 @@ static const char * const type_to_suffix[] = { ...@@ -694,123 +694,67 @@ static const char * const type_to_suffix[] = {
"", ":u8", ":u16", "", ":u32", "", "", "", ":u64" "", ":u8", ":u16", "", ":u32", "", "", "", ":u64"
}; };
static int synthesize_sdt_probe_arg(struct strbuf *buf, int i, const char *arg) /*
* Isolate the string number and convert it into a decimal value;
* this will be an index to get suffix of the uprobe name (defining
* the type)
*/
static int sdt_arg_parse_size(char *n_ptr, const char **suffix)
{ {
char *tmp, *desc = strdup(arg);
const char *prefix = "", *suffix = "";
int ret = -1;
if (desc == NULL) {
pr_debug4("Allocation error\n");
return ret;
}
tmp = strchr(desc, '@');
if (tmp) {
long type_idx; long type_idx;
/*
* Isolate the string number and convert it into a type_idx = strtol(n_ptr, NULL, 10);
* binary value; this will be an index to get suffix
* of the uprobe name (defining the type)
*/
tmp[0] = '\0';
type_idx = strtol(desc, NULL, 10);
/* Check that the conversion went OK */
if (type_idx == LONG_MIN || type_idx == LONG_MAX) {
pr_debug4("Failed to parse sdt type\n");
goto error;
}
/* Check that the converted value is OK */
if (type_idx < -8 || type_idx > 8) { if (type_idx < -8 || type_idx > 8) {
pr_debug4("Failed to get a valid sdt type\n"); pr_debug4("Failed to get a valid sdt type\n");
goto error; return -1;
}
suffix = type_to_suffix[type_idx + 8];
/* Get rid of the sdt prefix which is now useless */
tmp++;
memmove(desc, tmp, strlen(tmp) + 1);
} }
/* *suffix = type_to_suffix[type_idx + 8];
* The uprobe tracer format does not support all the return 0;
* addressing modes (notably: in x86 the scaled mode); so, we }
* detect ',' characters, if there is just one, there is no
* use converting the sdt arg into a uprobe one.
*/
if (strchr(desc, ',')) {
pr_debug4("Skipping unsupported SDT argument; %s\n", desc);
goto out;
}
/*
* If the argument addressing mode is indirect, we must check
* a few things...
*/
tmp = strchr(desc, '(');
if (tmp) {
int j;
/* static int synthesize_sdt_probe_arg(struct strbuf *buf, int i, const char *arg)
* ...if the addressing mode is indirect with a {
* positive offset (ex.: "1608(%ax)"), we need to add char *op, *desc = strdup(arg), *new_op = NULL;
* a '+' prefix so as to be compliant with uprobe const char *suffix = "";
* format. int ret = -1;
*/
if (desc[0] != '+' && desc[0] != '-')
prefix = "+";
/* if (desc == NULL) {
* ...or if the addressing mode is indirect with a symbol pr_debug4("Allocation error\n");
* as offset, the argument will not be supported by return ret;
* the uprobe tracer format; so, let's skip this one.
*/
for (j = 0; j < tmp - desc; j++) {
if (desc[j] != '+' && desc[j] != '-' &&
!isdigit(desc[j])) {
pr_debug4("Skipping unsupported SDT argument; "
"%s\n", desc);
goto out;
}
}
} }
/* /*
* The uprobe tracer format does not support constants; if we * Argument is in N@OP format. N is size of the argument and OP is
* find one in the current argument, let's skip the argument. * the actual assembly operand. N can be omitted; in that case
* argument is just OP(without @).
*/ */
if (strchr(desc, '$')) { op = strchr(desc, '@');
pr_debug4("Skipping unsupported SDT argument; %s\n", desc); if (op) {
goto out; op[0] = '\0';
op++;
if (sdt_arg_parse_size(desc, &suffix))
goto error;
} else {
op = desc;
} }
/* ret = arch_sdt_arg_parse_op(op, &new_op);
* The uprobe parser does not support all gas register names;
* so, we have to replace them (ex. for x86_64: %rax -> %ax);
* the loop below looks for the register names (starting with
* a '%' and tries to perform the needed renamings.
*/
tmp = strchr(desc, '%');
while (tmp) {
size_t offset = tmp - desc;
ret = sdt_rename_register(&desc, desc + offset);
if (ret < 0) if (ret < 0)
goto error; goto error;
/* if (ret == SDT_ARG_VALID) {
* The desc pointer might have changed; so, let's not ret = strbuf_addf(buf, " arg%d=%s%s", i + 1, new_op, suffix);
* try to reuse tmp for next lookup if (ret < 0)
*/
tmp = strchr(desc + offset + 1, '%');
}
if (strbuf_addf(buf, " arg%d=%s%s%s", i + 1, prefix, desc, suffix) < 0)
goto error; goto error;
}
out:
ret = 0; ret = 0;
error: error:
free(desc); free(desc);
free(new_op);
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment