提交 eb948e50 编写于 作者: M Masami Hiramatsu 提交者: Arnaldo Carvalho de Melo

perf probe: Allow to add events on the local functions

Allow to add events on the local functions without debuginfo.
(With the debuginfo, we can add events even on inlined functions)
Currently, probing on local functions requires debuginfo to
locate actual address. It is also possible without debuginfo since
we have symbol maps.

Without this change;
  ----
  # ./perf probe -a t_show
  Added new event:
    probe:t_show         (on t_show)

  You can now use it in all perf tools, such as:

          perf record -e probe:t_show -aR sleep 1

  # ./perf probe -x perf -a identity__map_ip
  no symbols found in /kbuild/ksrc/linux-3/tools/perf/perf, maybe install a debug package?
  Failed to load map.
    Error: Failed to add events. (-22)
  ----
As the above results, perf probe just put one event
on the first found symbol for kprobe event. Moreover,
for uprobe event, perf probe failed to find local
functions.

With this change;
  ----
  # ./perf probe -a t_show
  Added new events:
    probe:t_show         (on t_show)
    probe:t_show_1       (on t_show)
    probe:t_show_2       (on t_show)
    probe:t_show_3       (on t_show)

  You can now use it in all perf tools, such as:

          perf record -e probe:t_show_3 -aR sleep 1

  # ./perf probe -x perf -a identity__map_ip
  Added new events:
    probe_perf:identity__map_ip (on identity__map_ip in /kbuild/ksrc/linux-3/tools/perf/perf)
    probe_perf:identity__map_ip_1 (on identity__map_ip in /kbuild/ksrc/linux-3/tools/perf/perf)
    probe_perf:identity__map_ip_2 (on identity__map_ip in /kbuild/ksrc/linux-3/tools/perf/perf)
    probe_perf:identity__map_ip_3 (on identity__map_ip in /kbuild/ksrc/linux-3/tools/perf/perf)

  You can now use it in all perf tools, such as:

          perf record -e probe_perf:identity__map_ip_3 -aR sleep 1
  ----
Now we succeed to put events on every given local functions
for both kprobes and uprobes. :)

Note that this also introduces some symbol rbtree
iteration macros; symbols__for_each, dso__for_each_symbol,
and map__for_each_symbol. These are for walking through
the symbol list in a map.

Changes from v2:
  - Fix add_exec_to_probe_trace_events() not to convert address
    to tp->symbol any more.
  - Fix to set kernel probes based on ref_reloc_sym.
Signed-off-by: NMasami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: "David A. Long" <dave.long@linaro.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: yrl.pp-manager.tt@hitachi.com
Link: http://lkml.kernel.org/r/20140206053225.29635.15026.stgit@kbuild-fedora.yrl.intra.hitachi.co.jpSigned-off-by: NArnaldo Carvalho de Melo <acme@redhat.com>
上级 5a6f6314
......@@ -102,6 +102,16 @@ struct dso {
char name[0];
};
/* dso__for_each_symbol - iterate over the symbols of given type
*
* @dso: the 'struct dso *' in which symbols itereated
* @pos: the 'struct symbol *' to use as a loop cursor
* @n: the 'struct rb_node *' to use as a temporary storage
* @type: the 'enum map_type' type of symbols
*/
#define dso__for_each_symbol(dso, pos, n, type) \
symbols__for_each_entry(&(dso)->symbols[(type)], pos, n)
static inline void dso__set_loaded(struct dso *dso, enum map_type type)
{
dso->loaded |= (1 << type);
......
......@@ -90,6 +90,16 @@ u64 map__objdump_2mem(struct map *map, u64 ip);
struct symbol;
/* map__for_each_symbol - iterate over the symbols in the given map
*
* @map: the 'struct map *' in which symbols itereated
* @pos: the 'struct symbol *' to use as a loop cursor
* @n: the 'struct rb_node *' to use as a temporary storage
* Note: caller must ensure map->dso is not NULL (map is loaded).
*/
#define map__for_each_symbol(map, pos, n) \
dso__for_each_symbol(map->dso, pos, n, map->type)
typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym);
void map__init(struct map *map, enum map_type type,
......
......@@ -70,8 +70,6 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
}
static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
static int convert_name_to_addr(struct perf_probe_event *pev,
const char *exec);
static void clear_probe_trace_event(struct probe_trace_event *tev);
static struct machine *host_machine;
......@@ -249,6 +247,14 @@ static int convert_exec_to_group(const char *exec, char **result)
return ret;
}
static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
{
int i;
for (i = 0; i < ntevs; i++)
clear_probe_trace_event(tevs + i);
}
#ifdef HAVE_DWARF_SUPPORT
/* Open new debuginfo of given module */
static struct debuginfo *open_debuginfo(const char *module)
......@@ -353,8 +359,7 @@ static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
int ntevs, const char *exec)
{
int i, ret = 0;
unsigned long offset, stext = 0;
char buf[32];
unsigned long stext = 0;
if (!exec)
return 0;
......@@ -365,15 +370,9 @@ static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
for (i = 0; i < ntevs && ret >= 0; i++) {
/* point.address is the addres of point.symbol + point.offset */
offset = tevs[i].point.address - stext;
tevs[i].point.offset = 0;
zfree(&tevs[i].point.symbol);
ret = e_snprintf(buf, 32, "0x%lx", offset);
if (ret < 0)
break;
tevs[i].point.address -= stext;
tevs[i].point.module = strdup(exec);
tevs[i].point.symbol = strdup(buf);
if (!tevs[i].point.symbol || !tevs[i].point.module) {
if (!tevs[i].point.module) {
ret = -ENOMEM;
break;
}
......@@ -452,14 +451,6 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs,
return 0;
}
static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
{
int i;
for (i = 0; i < ntevs; i++)
clear_probe_trace_event(tevs + i);
}
/* Try to find perf_probe_event with debuginfo */
static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
struct probe_trace_event **tevs,
......@@ -1586,20 +1577,27 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)
if (buf == NULL)
return NULL;
len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s ", tp->retprobe ? 'r' : 'p',
tev->group, tev->event);
if (len <= 0)
goto error;
/* Uprobes must have tp->address and tp->module */
if (tev->uprobes && (!tp->address || !tp->module))
goto error;
/* Use the tp->address for uprobes */
if (tev->uprobes)
len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s:%s",
tp->retprobe ? 'r' : 'p',
tev->group, tev->event,
tp->module, tp->symbol);
ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s:0x%lx",
tp->module, tp->address);
else
len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s%s+%lu",
tp->retprobe ? 'r' : 'p',
tev->group, tev->event,
ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s%s%s+%lu",
tp->module ?: "", tp->module ? ":" : "",
tp->symbol, tp->offset);
if (len <= 0)
if (ret <= 0)
goto error;
len += ret;
for (i = 0; i < tev->nargs; i++) {
ret = synthesize_probe_trace_arg(&tev->args[i], buf + len,
......@@ -2150,113 +2148,175 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
return ret;
}
static int convert_to_probe_trace_events(struct perf_probe_event *pev,
static char *looking_function_name;
static int num_matched_functions;
static int probe_function_filter(struct map *map __maybe_unused,
struct symbol *sym)
{
if ((sym->binding == STB_GLOBAL || sym->binding == STB_LOCAL) &&
strcmp(looking_function_name, sym->name) == 0) {
num_matched_functions++;
return 0;
}
return 1;
}
#define strdup_or_goto(str, label) \
({ char *__p = strdup(str); if (!__p) goto label; __p; })
/*
* Find probe function addresses from map.
* Return an error or the number of found probe_trace_event
*/
static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
struct probe_trace_event **tevs,
int max_tevs, const char *target)
{
struct map *map = NULL;
struct kmap *kmap = NULL;
struct ref_reloc_sym *reloc_sym = NULL;
struct symbol *sym;
int ret, i;
struct rb_node *nd;
struct probe_trace_event *tev;
struct perf_probe_point *pp = &pev->point;
struct probe_trace_point *tp;
int ret, i;
if (pev->uprobes && !pev->group) {
/* Replace group name if not given */
ret = convert_exec_to_group(target, &pev->group);
if (ret != 0) {
pr_warning("Failed to make a group name.\n");
return ret;
}
/* Init maps of given executable or kernel */
if (pev->uprobes)
map = dso__new_map(target);
else
map = kernel_get_module_map(target);
if (!map) {
ret = -EINVAL;
goto out;
}
/* Convert perf_probe_event with debuginfo */
ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target);
if (ret != 0)
return ret; /* Found in debuginfo or got an error */
if (pev->uprobes) {
ret = convert_name_to_addr(pev, target);
if (ret < 0)
return ret;
/*
* Load matched symbols: Since the different local symbols may have
* same name but different addresses, this lists all the symbols.
*/
num_matched_functions = 0;
looking_function_name = pp->function;
ret = map__load(map, probe_function_filter);
if (ret || num_matched_functions == 0) {
pr_err("Failed to find symbol %s in %s\n", pp->function,
target ? : "kernel");
ret = -ENOENT;
goto out;
} else if (num_matched_functions > max_tevs) {
pr_err("Too many functions matched in %s\n",
target ? : "kernel");
ret = -E2BIG;
goto out;
}
/* Allocate trace event buffer */
tev = *tevs = zalloc(sizeof(struct probe_trace_event));
if (tev == NULL)
return -ENOMEM;
/* Copy parameters */
tev->point.symbol = strdup(pev->point.function);
if (tev->point.symbol == NULL) {
ret = -ENOMEM;
goto error;
if (!pev->uprobes) {
kmap = map__kmap(map);
reloc_sym = kmap->ref_reloc_sym;
if (!reloc_sym) {
pr_warning("Relocated base symbol is not found!\n");
ret = -EINVAL;
goto out;
}
}
if (target) {
tev->point.module = strdup(target);
if (tev->point.module == NULL) {
/* Setup result trace-probe-events */
*tevs = zalloc(sizeof(*tev) * num_matched_functions);
if (!*tevs) {
ret = -ENOMEM;
goto error;
goto out;
}
ret = 0;
map__for_each_symbol(map, sym, nd) {
tev = (*tevs) + ret;
tp = &tev->point;
if (ret == num_matched_functions) {
pr_warning("Too many symbols are listed. Skip it.\n");
break;
}
ret++;
tev->point.offset = pev->point.offset;
tev->point.retprobe = pev->point.retprobe;
tev->nargs = pev->nargs;
if (pp->offset > sym->end - sym->start) {
pr_warning("Offset %ld is bigger than the size of %s\n",
pp->offset, sym->name);
ret = -ENOENT;
goto err_out;
}
/* Add one probe point */
tp->address = map->unmap_ip(map, sym->start) + pp->offset;
if (reloc_sym) {
tp->symbol = strdup_or_goto(reloc_sym->name, nomem_out);
tp->offset = tp->address - reloc_sym->addr;
} else {
tp->symbol = strdup_or_goto(sym->name, nomem_out);
tp->offset = pp->offset;
}
tp->retprobe = pp->retprobe;
if (target)
tev->point.module = strdup_or_goto(target, nomem_out);
tev->uprobes = pev->uprobes;
tev->nargs = pev->nargs;
if (tev->nargs) {
tev->args = zalloc(sizeof(struct probe_trace_arg)
* tev->nargs);
if (tev->args == NULL) {
ret = -ENOMEM;
goto error;
tev->args = zalloc(sizeof(struct probe_trace_arg) *
tev->nargs);
if (tev->args == NULL)
goto nomem_out;
}
for (i = 0; i < tev->nargs; i++) {
if (pev->args[i].name) {
tev->args[i].name = strdup(pev->args[i].name);
if (tev->args[i].name == NULL) {
ret = -ENOMEM;
goto error;
}
}
tev->args[i].value = strdup(pev->args[i].var);
if (tev->args[i].value == NULL) {
ret = -ENOMEM;
goto error;
}
if (pev->args[i].type) {
tev->args[i].type = strdup(pev->args[i].type);
if (tev->args[i].type == NULL) {
ret = -ENOMEM;
goto error;
}
if (pev->args[i].name)
tev->args[i].name =
strdup_or_goto(pev->args[i].name,
nomem_out);
tev->args[i].value = strdup_or_goto(pev->args[i].var,
nomem_out);
if (pev->args[i].type)
tev->args[i].type =
strdup_or_goto(pev->args[i].type,
nomem_out);
}
}
out:
if (map && pev->uprobes) {
/* Only when using uprobe(exec) map needs to be released */
dso__delete(map->dso);
map__delete(map);
}
return ret;
if (pev->uprobes)
return 1;
nomem_out:
ret = -ENOMEM;
err_out:
clear_probe_trace_events(*tevs, num_matched_functions);
zfree(tevs);
goto out;
}
/* Currently just checking function name from symbol map */
sym = __find_kernel_function_by_name(tev->point.symbol, NULL);
if (!sym) {
pr_warning("Kernel symbol \'%s\' not found.\n",
tev->point.symbol);
ret = -ENOENT;
goto error;
} else if (tev->point.offset > sym->end - sym->start) {
pr_warning("Offset specified is greater than size of %s\n",
tev->point.symbol);
ret = -ENOENT;
goto error;
static int convert_to_probe_trace_events(struct perf_probe_event *pev,
struct probe_trace_event **tevs,
int max_tevs, const char *target)
{
int ret;
if (pev->uprobes && !pev->group) {
/* Replace group name if not given */
ret = convert_exec_to_group(target, &pev->group);
if (ret != 0) {
pr_warning("Failed to make a group name.\n");
return ret;
}
}
return 1;
error:
clear_probe_trace_event(tev);
free(tev);
*tevs = NULL;
return ret;
/* Convert perf_probe_event with debuginfo */
ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target);
if (ret != 0)
return ret; /* Found in debuginfo or got an error */
return find_probe_trace_events_from_map(pev, tevs, max_tevs, target);
}
struct __event_package {
......@@ -2461,7 +2521,7 @@ static struct strfilter *available_func_filter;
static int filter_available_functions(struct map *map __maybe_unused,
struct symbol *sym)
{
if (sym->binding == STB_GLOBAL &&
if ((sym->binding == STB_GLOBAL || sym->binding == STB_LOCAL) &&
strfilter__compare(available_func_filter, sym->name))
return 0;
return 1;
......@@ -2509,95 +2569,3 @@ int show_available_funcs(const char *target, struct strfilter *_filter,
return ret;
}
/*
* uprobe_events only accepts address:
* Convert function and any offset to address
*/
static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec)
{
struct perf_probe_point *pp = &pev->point;
struct symbol *sym;
struct map *map = NULL;
char *function = NULL;
int ret = -EINVAL;
unsigned long long vaddr = 0;
if (!pp->function) {
pr_warning("No function specified for uprobes");
goto out;
}
function = strdup(pp->function);
if (!function) {
pr_warning("Failed to allocate memory by strdup.\n");
ret = -ENOMEM;
goto out;
}
map = dso__new_map(exec);
if (!map) {
pr_warning("Cannot find appropriate DSO for %s.\n", exec);
goto out;
}
available_func_filter = strfilter__new(function, NULL);
if (map__load(map, filter_available_functions)) {
pr_err("Failed to load map.\n");
goto out;
}
sym = map__find_symbol_by_name(map, function, NULL);
if (!sym) {
pr_warning("Cannot find %s in DSO %s\n", function, exec);
goto out;
}
if (map->start > sym->start)
vaddr = map->start;
vaddr += sym->start + pp->offset + map->pgoff;
pp->offset = 0;
if (!pev->event) {
pev->event = function;
function = NULL;
}
if (!pev->group) {
char *ptr1, *ptr2, *exec_copy;
pev->group = zalloc(sizeof(char *) * 64);
exec_copy = strdup(exec);
if (!exec_copy) {
ret = -ENOMEM;
pr_warning("Failed to copy exec string.\n");
goto out;
}
ptr1 = strdup(basename(exec_copy));
if (ptr1) {
ptr2 = strpbrk(ptr1, "-._");
if (ptr2)
*ptr2 = '\0';
e_snprintf(pev->group, 64, "%s_%s", PERFPROBE_GROUP,
ptr1);
free(ptr1);
}
free(exec_copy);
}
free(pp->function);
pp->function = zalloc(sizeof(char *) * MAX_PROBE_ARGS);
if (!pp->function) {
ret = -ENOMEM;
pr_warning("Failed to allocate memory by zalloc.\n");
goto out;
}
e_snprintf(pp->function, MAX_PROBE_ARGS, "0x%llx", vaddr);
ret = 0;
out:
if (map) {
dso__delete(map->dso);
map__delete(map);
}
if (function)
free(function);
return ret;
}
......@@ -79,6 +79,17 @@ struct symbol {
void symbol__delete(struct symbol *sym);
void symbols__delete(struct rb_root *symbols);
/* symbols__for_each_entry - iterate over symbols (rb_root)
*
* @symbols: the rb_root of symbols
* @pos: the 'struct symbol *' to use as a loop cursor
* @nd: the 'struct rb_node *' to use as a temporary storage
*/
#define symbols__for_each_entry(symbols, pos, nd) \
for (nd = rb_first(symbols); \
nd && (pos = rb_entry(nd, struct symbol, rb_node)); \
nd = rb_next(nd))
static inline size_t symbol__size(const struct symbol *sym)
{
return sym->end - sym->start + 1;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册