perf probe: Support argument name
Set given names to event arguments. The syntax is same as kprobe-tracer, you can add 'NAME=' right before each argument. e.g. ./perf probe vfs_read foo=file then, 'foo' is set to the argument name as below. ./perf probe -l probe:vfs_read (on vfs_read@linux-2.6-tip/fs/read_write.c with foo) Cc: Ingo Molnar <mingo@elte.hu> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Mike Galbraith <efault@gmx.de> Cc: Frederic Weisbecker <fweisbec@gmail.com> LKML-Reference: <20100412171653.3790.74624.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
fcd1498405
commit
48481938b0
5 changed files with 53 additions and 25 deletions
|
@ -79,7 +79,15 @@ Probe points are defined by following syntax.
|
||||||
'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'.
|
'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'.
|
||||||
'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition. In addition, '@SRC' specifies a source file which has that function.
|
'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition. In addition, '@SRC' specifies a source file which has that function.
|
||||||
It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern.
|
It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern.
|
||||||
'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc).
|
'ARG' specifies the arguments of this probe point, (see PROBE ARGUMENT).
|
||||||
|
|
||||||
|
PROBE ARGUMENT
|
||||||
|
--------------
|
||||||
|
Each probe argument follows below syntax.
|
||||||
|
|
||||||
|
[NAME=]LOCALVAR|$retval|%REG|@SYMBOL
|
||||||
|
|
||||||
|
'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc).
|
||||||
|
|
||||||
LINE SYNTAX
|
LINE SYNTAX
|
||||||
-----------
|
-----------
|
||||||
|
|
|
@ -142,9 +142,9 @@ static const struct option options[] = {
|
||||||
OPT_CALLBACK('a', "add", NULL,
|
OPT_CALLBACK('a', "add", NULL,
|
||||||
#ifdef DWARF_SUPPORT
|
#ifdef DWARF_SUPPORT
|
||||||
"[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT"
|
"[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT"
|
||||||
" [ARG ...]",
|
" [[NAME=]ARG ...]",
|
||||||
#else
|
#else
|
||||||
"[EVENT=]FUNC[+OFF|%return] [ARG ...]",
|
"[EVENT=]FUNC[+OFF|%return] [[NAME=]ARG ...]",
|
||||||
#endif
|
#endif
|
||||||
"probe point definition, where\n"
|
"probe point definition, where\n"
|
||||||
"\t\tGROUP:\tGroup name (optional)\n"
|
"\t\tGROUP:\tGroup name (optional)\n"
|
||||||
|
|
|
@ -437,22 +437,28 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
|
||||||
/* Parse perf-probe event argument */
|
/* Parse perf-probe event argument */
|
||||||
static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg)
|
static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg)
|
||||||
{
|
{
|
||||||
const char *tmp;
|
char *tmp;
|
||||||
struct perf_probe_arg_field **fieldp;
|
struct perf_probe_arg_field **fieldp;
|
||||||
|
|
||||||
pr_debug("parsing arg: %s into ", str);
|
pr_debug("parsing arg: %s into ", str);
|
||||||
|
|
||||||
|
tmp = strchr(str, '=');
|
||||||
|
if (tmp) {
|
||||||
|
arg->name = xstrndup(str, tmp - str);
|
||||||
|
str = tmp + 1;
|
||||||
|
}
|
||||||
|
|
||||||
tmp = strpbrk(str, "-.");
|
tmp = strpbrk(str, "-.");
|
||||||
if (!is_c_varname(str) || !tmp) {
|
if (!is_c_varname(str) || !tmp) {
|
||||||
/* A variable, register, symbol or special value */
|
/* A variable, register, symbol or special value */
|
||||||
arg->name = xstrdup(str);
|
arg->var = xstrdup(str);
|
||||||
pr_debug("%s\n", arg->name);
|
pr_debug("%s\n", arg->var);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Structure fields */
|
/* Structure fields */
|
||||||
arg->name = xstrndup(str, tmp - str);
|
arg->var = xstrndup(str, tmp - str);
|
||||||
pr_debug("%s, ", arg->name);
|
pr_debug("%s, ", arg->var);
|
||||||
fieldp = &arg->field;
|
fieldp = &arg->field;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
@ -497,7 +503,7 @@ void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev)
|
||||||
pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs);
|
pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs);
|
||||||
for (i = 0; i < pev->nargs; i++) {
|
for (i = 0; i < pev->nargs; i++) {
|
||||||
parse_perf_probe_arg(argv[i + 1], &pev->args[i]);
|
parse_perf_probe_arg(argv[i + 1], &pev->args[i]);
|
||||||
if (is_c_varname(pev->args[i].name) && pev->point.retprobe)
|
if (is_c_varname(pev->args[i].var) && pev->point.retprobe)
|
||||||
semantic_error("You can't specify local variable for"
|
semantic_error("You can't specify local variable for"
|
||||||
" kretprobe");
|
" kretprobe");
|
||||||
}
|
}
|
||||||
|
@ -514,7 +520,7 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
for (i = 0; i < pev->nargs; i++)
|
for (i = 0; i < pev->nargs; i++)
|
||||||
if (is_c_varname(pev->args[i].name))
|
if (is_c_varname(pev->args[i].var))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -575,7 +581,10 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len)
|
||||||
int ret;
|
int ret;
|
||||||
char *tmp = buf;
|
char *tmp = buf;
|
||||||
|
|
||||||
ret = e_snprintf(tmp, len, "%s", pa->name);
|
if (pa->name && pa->var)
|
||||||
|
ret = e_snprintf(tmp, len, "%s=%s", pa->name, pa->var);
|
||||||
|
else
|
||||||
|
ret = e_snprintf(tmp, len, "%s", pa->name ? pa->name : pa->var);
|
||||||
if (ret <= 0)
|
if (ret <= 0)
|
||||||
goto error;
|
goto error;
|
||||||
tmp += ret;
|
tmp += ret;
|
||||||
|
@ -803,6 +812,8 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
|
||||||
for (i = 0; i < pev->nargs; i++) {
|
for (i = 0; i < pev->nargs; i++) {
|
||||||
if (pev->args[i].name)
|
if (pev->args[i].name)
|
||||||
free(pev->args[i].name);
|
free(pev->args[i].name);
|
||||||
|
if (pev->args[i].var)
|
||||||
|
free(pev->args[i].var);
|
||||||
field = pev->args[i].field;
|
field = pev->args[i].field;
|
||||||
while (field) {
|
while (field) {
|
||||||
next = field->next;
|
next = field->next;
|
||||||
|
@ -1117,8 +1128,11 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
|
||||||
if (tev->nargs) {
|
if (tev->nargs) {
|
||||||
tev->args = xzalloc(sizeof(struct kprobe_trace_arg)
|
tev->args = xzalloc(sizeof(struct kprobe_trace_arg)
|
||||||
* tev->nargs);
|
* tev->nargs);
|
||||||
for (i = 0; i < tev->nargs; i++)
|
for (i = 0; i < tev->nargs; i++) {
|
||||||
tev->args[i].value = xstrdup(pev->args[i].name);
|
if (pev->args[i].name)
|
||||||
|
tev->args[i].name = xstrdup(pev->args[i].name);
|
||||||
|
tev->args[i].value = xstrdup(pev->args[i].var);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Currently just checking function name from symbol map */
|
/* Currently just checking function name from symbol map */
|
||||||
|
|
|
@ -55,6 +55,7 @@ struct perf_probe_arg_field {
|
||||||
/* Perf probe probing argument */
|
/* Perf probe probing argument */
|
||||||
struct perf_probe_arg {
|
struct perf_probe_arg {
|
||||||
char *name; /* Argument name */
|
char *name; /* Argument name */
|
||||||
|
char *var; /* Variable name */
|
||||||
struct perf_probe_arg_field *field; /* Structure fields */
|
struct perf_probe_arg_field *field; /* Structure fields */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -484,35 +484,40 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
|
||||||
convert_location(expr, pf);
|
convert_location(expr, pf);
|
||||||
|
|
||||||
if (pf->pvar->field)
|
if (pf->pvar->field)
|
||||||
convert_variable_fields(vr_die, pf->pvar->name,
|
convert_variable_fields(vr_die, pf->pvar->var,
|
||||||
pf->pvar->field, &pf->tvar->ref);
|
pf->pvar->field, &pf->tvar->ref);
|
||||||
/* *expr will be cached in libdw. Don't free it. */
|
/* *expr will be cached in libdw. Don't free it. */
|
||||||
return ;
|
return ;
|
||||||
error:
|
error:
|
||||||
/* TODO: Support const_value */
|
/* TODO: Support const_value */
|
||||||
die("Failed to find the location of %s at this address.\n"
|
die("Failed to find the location of %s at this address.\n"
|
||||||
" Perhaps, it has been optimized out.", pf->pvar->name);
|
" Perhaps, it has been optimized out.", pf->pvar->var);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find a variable in a subprogram die */
|
/* Find a variable in a subprogram die */
|
||||||
static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
|
static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
|
||||||
{
|
{
|
||||||
Dwarf_Die vr_die;
|
Dwarf_Die vr_die;
|
||||||
char buf[128];
|
char buf[32];
|
||||||
|
|
||||||
/* TODO: Support struct members and arrays */
|
/* TODO: Support arrays */
|
||||||
if (!is_c_varname(pf->pvar->name)) {
|
if (pf->pvar->name)
|
||||||
/* Copy raw parameters */
|
pf->tvar->name = xstrdup(pf->pvar->name);
|
||||||
pf->tvar->value = xstrdup(pf->pvar->name);
|
else {
|
||||||
} else {
|
synthesize_perf_probe_arg(pf->pvar, buf, 32);
|
||||||
synthesize_perf_probe_arg(pf->pvar, buf, 128);
|
|
||||||
pf->tvar->name = xstrdup(buf);
|
pf->tvar->name = xstrdup(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_c_varname(pf->pvar->var)) {
|
||||||
|
/* Copy raw parameters */
|
||||||
|
pf->tvar->value = xstrdup(pf->pvar->var);
|
||||||
|
} else {
|
||||||
pr_debug("Searching '%s' variable in context.\n",
|
pr_debug("Searching '%s' variable in context.\n",
|
||||||
pf->pvar->name);
|
pf->pvar->var);
|
||||||
/* Search child die for local variables and parameters. */
|
/* Search child die for local variables and parameters. */
|
||||||
if (!die_find_variable(sp_die, pf->pvar->name, &vr_die))
|
if (!die_find_variable(sp_die, pf->pvar->var, &vr_die))
|
||||||
die("Failed to find '%s' in this function.",
|
die("Failed to find '%s' in this function.",
|
||||||
pf->pvar->name);
|
pf->pvar->var);
|
||||||
convert_variable(&vr_die, pf);
|
convert_variable(&vr_die, pf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue