Commit da15bd9d authored by Wang Nan's avatar Wang Nan Committed by Arnaldo Carvalho de Melo

perf probe: Support probing at absolute address

It should be useful to allow 'perf probe' probe at absolute offset of a
target. For example, when (u)probing at a instruction of a shared object
in a embedded system where debuginfo is not avaliable but we know the
offset of that instruction by manually digging.

This patch enables following perf probe command syntax:

  # perf probe 0xffffffff811e6615

And

  # perf probe /lib/x86_64-linux-gnu/libc-2.19.so 0xeb860

In the above example, we don't need a anchor symbol, so it is possible
to compute absolute addresses using other methods and then use 'perf
probe' to create the probing points.

v1 -> v2:
  Drop the leading '+' in cmdline;
  Allow uprobing at offset 0x0;
  Improve 'perf probe -l' result when uprobe at area without debuginfo.

v2 -> v3:
  Split bugfix to a separated patch.

Test result:

  # perf probe 0xffffffff8119d175 %ax
  # perf probe sys_write %ax
  # perf probe /lib64/libc-2.18.so 0x0 %ax
  # perf probe /lib64/libc-2.18.so 0x5 %ax
  # perf probe /lib64/libc-2.18.so 0xd8e40 %ax
  # perf probe /lib64/libc-2.18.so __write %ax
  # perf probe /lib64/libc-2.18.so 0xd8e49 %ax
  # cat /sys/kernel/debug/tracing/uprobe_events

  p:probe_libc/abs_0 /lib64/libc-2.18.so:0x          (null) arg1=%ax
  p:probe_libc/abs_5 /lib64/libc-2.18.so:0x0000000000000005 arg1=%ax
  p:probe_libc/abs_d8e40 /lib64/libc-2.18.so:0x00000000000d8e40 arg1=%ax
  p:probe_libc/__write /lib64/libc-2.18.so:0x00000000000d8e40 arg1=%ax
  p:probe_libc/abs_d8e49 /lib64/libc-2.18.so:0x00000000000d8e49 arg1=%ax

  # cat /sys/kernel/debug/tracing/kprobe_events

  p:probe/abs_ffffffff8119d175 0xffffffff8119d175 arg1=%ax
  p:probe/sys_write _text+1692016 arg1=%ax

  # perf probe -l

  Failed to find debug information for address 5
    probe:abs_ffffffff8119d175 (on sys_write+5 with arg1)
    probe:sys_write      (on sys_write with arg1)
    probe_libc:__write   (on @unix/syscall-template.S:81 in /lib64/libc-2.18.so with arg1)
    probe_libc:abs_0     (on 0x0 in /lib64/libc-2.18.so with arg1)
    probe_libc:abs_5     (on 0x5 in /lib64/libc-2.18.so with arg1)
    probe_libc:abs_d8e40 (on @unix/syscall-template.S:81 in /lib64/libc-2.18.so with arg1)
    probe_libc:abs_d8e49 (on __GI___libc_write+9 in /lib64/libc-2.18.so with arg1)
Signed-off-by: default avatarWang Nan <wangnan0@huawei.com>
Acked-by: default avatarMasami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1440586666-235233-7-git-send-email-wangnan0@huawei.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 6c6e024f
...@@ -1204,9 +1204,27 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) ...@@ -1204,9 +1204,27 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
if (file_spec) if (file_spec)
pp->file = tmp; pp->file = tmp;
else else {
pp->function = tmp; pp->function = tmp;
/*
* Keep pp->function even if this is absolute address,
* so it can mark whether abs_address is valid.
* Which make 'perf probe lib.bin 0x0' possible.
*
* Note that checking length of tmp is not needed
* because when we access tmp[1] we know tmp[0] is '0',
* so tmp[1] should always valid (but could be '\0').
*/
if (tmp && !strncmp(tmp, "0x", 2)) {
pp->abs_address = strtoul(pp->function, &tmp, 0);
if (*tmp != '\0') {
semantic_error("Invalid absolute address.\n");
return -EINVAL;
}
}
}
/* Parse other options */ /* Parse other options */
while (ptr) { while (ptr) {
arg = ptr; arg = ptr;
...@@ -1804,14 +1822,29 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev) ...@@ -1804,14 +1822,29 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)
if (len <= 0) if (len <= 0)
goto error; goto error;
/* Uprobes must have tp->address and tp->module */ /* Uprobes must have tp->module */
if (tev->uprobes && (!tp->address || !tp->module)) if (tev->uprobes && !tp->module)
goto error; goto error;
/*
* If tp->address == 0, then this point must be a
* absolute address uprobe.
* try_to_find_absolute_address() should have made
* tp->symbol to "0x0".
*/
if (tev->uprobes && !tp->address) {
if (!tp->symbol || strcmp(tp->symbol, "0x0"))
goto error;
}
/* Use the tp->address for uprobes */ /* Use the tp->address for uprobes */
if (tev->uprobes) if (tev->uprobes)
ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s:0x%lx", ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s:0x%lx",
tp->module, tp->address); tp->module, tp->address);
else if (!strncmp(tp->symbol, "0x", 2))
/* Absolute address. See try_to_find_absolute_address() */
ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s%s0x%lx",
tp->module ?: "", tp->module ? ":" : "",
tp->address);
else else
ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s%s%s+%lu", ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s%s%s+%lu",
tp->module ?: "", tp->module ? ":" : "", tp->module ?: "", tp->module ? ":" : "",
...@@ -2331,7 +2364,9 @@ static int probe_trace_event__set_name(struct probe_trace_event *tev, ...@@ -2331,7 +2364,9 @@ static int probe_trace_event__set_name(struct probe_trace_event *tev,
if (pev->event) if (pev->event)
event = pev->event; event = pev->event;
else else
if (pev->point.function && !strisglob(pev->point.function)) if (pev->point.function &&
(strncmp(pev->point.function, "0x", 2) != 0) &&
!strisglob(pev->point.function))
event = pev->point.function; event = pev->point.function;
else else
event = tev->point.realname; event = tev->point.realname;
...@@ -2598,6 +2633,98 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, ...@@ -2598,6 +2633,98 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
goto out; goto out;
} }
static int try_to_find_absolute_address(struct perf_probe_event *pev,
struct probe_trace_event **tevs)
{
struct perf_probe_point *pp = &pev->point;
struct probe_trace_event *tev;
struct probe_trace_point *tp;
int i, err;
if (!(pev->point.function && !strncmp(pev->point.function, "0x", 2)))
return -EINVAL;
if (perf_probe_event_need_dwarf(pev))
return -EINVAL;
/*
* This is 'perf probe /lib/libc.so 0xabcd'. Try to probe at
* absolute address.
*
* Only one tev can be generated by this.
*/
*tevs = zalloc(sizeof(*tev));
if (!*tevs)
return -ENOMEM;
tev = *tevs;
tp = &tev->point;
/*
* Don't use tp->offset, use address directly, because
* in synthesize_probe_trace_command() address cannot be
* zero.
*/
tp->address = pev->point.abs_address;
tp->retprobe = pp->retprobe;
tev->uprobes = pev->uprobes;
err = -ENOMEM;
/*
* Give it a '0x' leading symbol name.
* In __add_probe_trace_events, a NULL symbol is interpreted as
* invalud.
*/
if (asprintf(&tp->symbol, "0x%lx", tp->address) < 0)
goto errout;
/* For kprobe, check range */
if ((!tev->uprobes) &&
(kprobe_warn_out_range(tev->point.symbol,
tev->point.address))) {
err = -EACCES;
goto errout;
}
if (asprintf(&tp->realname, "abs_%lx", tp->address) < 0)
goto errout;
if (pev->target) {
tp->module = strdup(pev->target);
if (!tp->module)
goto errout;
}
if (tev->group) {
tev->group = strdup(pev->group);
if (!tev->group)
goto errout;
}
if (pev->event) {
tev->event = strdup(pev->event);
if (!tev->event)
goto errout;
}
tev->nargs = pev->nargs;
tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
if (!tev->args) {
err = -ENOMEM;
goto errout;
}
for (i = 0; i < tev->nargs; i++)
copy_to_probe_trace_arg(&tev->args[i], &pev->args[i]);
return 1;
errout:
if (*tevs) {
clear_probe_trace_events(*tevs, 1);
*tevs = NULL;
}
return err;
}
bool __weak arch__prefers_symtab(void) { return false; } bool __weak arch__prefers_symtab(void) { return false; }
static int convert_to_probe_trace_events(struct perf_probe_event *pev, static int convert_to_probe_trace_events(struct perf_probe_event *pev,
...@@ -2614,6 +2741,10 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, ...@@ -2614,6 +2741,10 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
} }
} }
ret = try_to_find_absolute_address(pev, tevs);
if (ret > 0)
return ret;
if (arch__prefers_symtab() && !perf_probe_event_need_dwarf(pev)) { if (arch__prefers_symtab() && !perf_probe_event_need_dwarf(pev)) {
ret = find_probe_trace_events_from_map(pev, tevs); ret = find_probe_trace_events_from_map(pev, tevs);
if (ret > 0) if (ret > 0)
...@@ -2784,3 +2915,22 @@ int show_available_funcs(const char *target, struct strfilter *_filter, ...@@ -2784,3 +2915,22 @@ int show_available_funcs(const char *target, struct strfilter *_filter,
return ret; return ret;
} }
int copy_to_probe_trace_arg(struct probe_trace_arg *tvar,
struct perf_probe_arg *pvar)
{
tvar->value = strdup(pvar->var);
if (tvar->value == NULL)
return -ENOMEM;
if (pvar->type) {
tvar->type = strdup(pvar->type);
if (tvar->type == NULL)
return -ENOMEM;
}
if (pvar->name) {
tvar->name = strdup(pvar->name);
if (tvar->name == NULL)
return -ENOMEM;
} else
tvar->name = NULL;
return 0;
}
...@@ -59,6 +59,7 @@ struct perf_probe_point { ...@@ -59,6 +59,7 @@ struct perf_probe_point {
bool retprobe; /* Return probe flag */ bool retprobe; /* Return probe flag */
char *lazy_line; /* Lazy matching pattern */ char *lazy_line; /* Lazy matching pattern */
unsigned long offset; /* Offset from function entry */ unsigned long offset; /* Offset from function entry */
unsigned long abs_address; /* Absolute address of the point */
}; };
/* Perf probe probing argument field chain */ /* Perf probe probing argument field chain */
...@@ -156,4 +157,7 @@ int e_snprintf(char *str, size_t size, const char *format, ...) ...@@ -156,4 +157,7 @@ int e_snprintf(char *str, size_t size, const char *format, ...)
/* Maximum index number of event-name postfix */ /* Maximum index number of event-name postfix */
#define MAX_EVENT_INDEX 1024 #define MAX_EVENT_INDEX 1024
int copy_to_probe_trace_arg(struct probe_trace_arg *tvar,
struct perf_probe_arg *pvar);
#endif /*_PROBE_EVENT_H */ #endif /*_PROBE_EVENT_H */
...@@ -553,24 +553,9 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) ...@@ -553,24 +553,9 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf)
char buf[32], *ptr; char buf[32], *ptr;
int ret = 0; int ret = 0;
if (!is_c_varname(pf->pvar->var)) {
/* Copy raw parameters */ /* Copy raw parameters */
pf->tvar->value = strdup(pf->pvar->var); if (!is_c_varname(pf->pvar->var))
if (pf->tvar->value == NULL) return copy_to_probe_trace_arg(pf->tvar, pf->pvar);
return -ENOMEM;
if (pf->pvar->type) {
pf->tvar->type = strdup(pf->pvar->type);
if (pf->tvar->type == NULL)
return -ENOMEM;
}
if (pf->pvar->name) {
pf->tvar->name = strdup(pf->pvar->name);
if (pf->tvar->name == NULL)
return -ENOMEM;
} else
pf->tvar->name = NULL;
return 0;
}
if (pf->pvar->name) if (pf->pvar->name)
pf->tvar->name = strdup(pf->pvar->name); pf->tvar->name = strdup(pf->pvar->name);
......
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