Re: [PATCH -tip 2/3] perf-probe: Support dwarf on uprobe events

From: Arnaldo Carvalho de Melo
Date: Fri Dec 20 2013 - 13:02:07 EST


Em Fri, Dec 20, 2013 at 10:02:59AM +0000, Masami Hiramatsu escreveu:
> Support dwarf(debuginfo) based operations for uprobe events.
> With this change, perf probe can analyze debuginfo of user
> application binary to set up new uprobe event.
> This allows perf-probe --add/--line works with -x option.
> (Actually, --vars has already accepted -x option)

Can you provide an example?

Showing output from commands when new features are implemented can speed
up the process of having it used :-)

- Arnaldo

> Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@xxxxxxxxxxx>
> ---
> tools/perf/builtin-probe.c | 2
> tools/perf/util/probe-event.c | 230 +++++++++++++++++++++++++++--------------
> 2 files changed, 155 insertions(+), 77 deletions(-)
>
> diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
> index b40d064..7fc566a 100644
> --- a/tools/perf/builtin-probe.c
> +++ b/tools/perf/builtin-probe.c
> @@ -420,7 +420,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
> }
>
> #ifdef HAVE_DWARF_SUPPORT
> - if (params.show_lines && !params.uprobes) {
> + if (params.show_lines) {
> if (params.mod_events) {
> pr_err(" Error: Don't use --line with"
> " --add/--del.\n");
> diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
> index 05be5de..e27cecb 100644
> --- a/tools/perf/util/probe-event.c
> +++ b/tools/perf/util/probe-event.c
> @@ -186,6 +186,90 @@ static int init_user_exec(void)
> return ret;
> }
>
> +static const char *__target_symbol;
> +static struct symbol *__result_sym;
> +
> +static int filter_target_symbol(struct map *map __maybe_unused,
> + struct symbol *sym)
> +{
> + if (strcmp(__target_symbol, sym->name) == 0) {
> + __result_sym = sym;
> + return 0;
> + }
> + return 1;
> +}
> +
> +/* Find the offset of the symbol in the executable binary */
> +static int find_symbol_offset(const char *exec, const char *function,
> + unsigned long *offs)
> +{
> + struct symbol *sym;
> + struct map *map = NULL;
> + int ret = -EINVAL;
> +
> + if (!offs)
> + return -EINVAL;
> +
> + map = dso__new_map(exec);
> + if (!map) {
> + pr_warning("Cannot find appropriate DSO for %s.\n", exec);
> + goto out;
> + }
> + pr_debug("Search %s in %s\n", function, exec);
> + __target_symbol = function;
> + __result_sym = NULL;
> + if (map__load(map, filter_target_symbol)) {
> + pr_err("Failed to find %s in %s.\n", function, exec);
> + goto out;
> + }
> + sym = __result_sym;
> + if (!sym) {
> + pr_warning("Cannot find %s in DSO %s\n", function, exec);
> + goto out;
> + }
> +
> + *offs = (map->start > sym->start) ? map->start : 0;
> + *offs += sym->start + map->pgoff;
> + ret = 0;
> +out:
> + if (map) {
> + dso__delete(map->dso);
> + map__delete(map);
> + }
> + return ret;
> +}
> +
> +static int convert_exec_to_group(const char *exec, char **result)
> +{
> + char *ptr1, *ptr2, *exec_copy;
> + char buf[64];
> + int ret;
> +
> + exec_copy = strdup(exec);
> + if (!exec_copy)
> + return -ENOMEM;
> +
> + ptr1 = basename(exec_copy);
> + if (!ptr1) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + ptr2 = strpbrk(ptr1, "-._");
> + if (ptr2)
> + *ptr2 = '\0';
> + ret = e_snprintf(buf, 64, "%s_%s", PERFPROBE_GROUP, ptr1);
> + if (ret < 0)
> + goto out;
> +
> + *result = strdup(buf);
> + ret = *result ? 0 : -ENOMEM;
> +
> +out:
> + free(exec_copy);
> + return ret;
> +}
> +
> static int convert_to_perf_probe_point(struct probe_trace_point *tp,
> struct perf_probe_point *pp)
> {
> @@ -261,6 +345,45 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
> return 0;
> }
>
> +static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
> + int ntevs, const char *exec,
> + const char *group)
> +{
> + int i, ret = 0;
> + unsigned long offset;
> + char buf[32];
> +
> + if (!exec)
> + return 0;
> +
> + for (i = 0; i < ntevs && ret >= 0; i++) {
> + /* Get proper offset */
> + ret = find_symbol_offset(exec, tevs[i].point.symbol, &offset);
> + if (ret < 0)
> + break;
> + offset += tevs[i].point.offset;
> + tevs[i].point.offset = 0;
> + free(tevs[i].point.symbol);
> + ret = e_snprintf(buf, 32, "0x%lx", offset);
> + if (ret < 0)
> + break;
> + tevs[i].point.module = strdup(exec);
> + tevs[i].point.symbol = strdup(buf);
> + if (!tevs[i].point.symbol || !tevs[i].point.module) {
> + ret = -ENOMEM;
> + break;
> + }
> + /* Replace group name if not given */
> + if (!group) {
> + free(tevs[i].group);
> + ret = convert_exec_to_group(exec, &tevs[i].group);
> + }
> + tevs[i].uprobes = true;
> + }
> +
> + return ret;
> +}
> +
> static int add_module_to_probe_trace_events(struct probe_trace_event *tevs,
> int ntevs, const char *module)
> {
> @@ -305,15 +428,6 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
> struct debuginfo *dinfo;
> int ntevs, ret = 0;
>
> - if (pev->uprobes) {
> - if (need_dwarf) {
> - pr_warning("Debuginfo-analysis is not yet supported"
> - " with -x/--exec option.\n");
> - return -ENOSYS;
> - }
> - return convert_name_to_addr(pev, target);
> - }
> -
> dinfo = open_debuginfo(target);
>
> if (!dinfo) {
> @@ -332,9 +446,14 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
>
> if (ntevs > 0) { /* Succeeded to find trace events */
> pr_debug("find %d probe_trace_events.\n", ntevs);
> - if (target)
> - ret = add_module_to_probe_trace_events(*tevs, ntevs,
> - target);
> + if (target) {
> + if (pev->uprobes)
> + ret = add_exec_to_probe_trace_events(*tevs,
> + ntevs, target, pev->group);
> + else
> + ret = add_module_to_probe_trace_events(*tevs,
> + ntevs, target);
> + }
> return ret < 0 ? ret : ntevs;
> }
>
> @@ -654,9 +773,6 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
> return -ENOSYS;
> }
>
> - if (pev->uprobes)
> - return convert_name_to_addr(pev, target);
> -
> return 0;
> }
>
> @@ -1916,11 +2032,26 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
> int ret = 0, i;
> struct probe_trace_event *tev;
>
> + if (pev->uprobes)
> + if (!pev->group) {
> + ret = convert_exec_to_group(target, &pev->group);
> + if (ret != 0) {
> + pr_warning("Failed to make group name.\n");
> + 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 */
>
> + if (pev->uprobes) {
> + ret = convert_name_to_addr(pev, target);
> + if (ret < 0)
> + return ret;
> + }
> +
> /* Allocate trace event buffer */
> tev = *tevs = zalloc(sizeof(struct probe_trace_event));
> if (tev == NULL)
> @@ -2279,88 +2410,35 @@ int show_available_funcs(const char *target, struct strfilter *_filter,
> 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;
> + unsigned 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);
> + ret = find_symbol_offset(exec, pp->function, &vaddr);
> + if (ret < 0)
> goto out;
> - }
>
> - if (map->start > sym->start)
> - vaddr = map->start;
> - vaddr += sym->start + pp->offset + map->pgoff;
> + vaddr += pp->offset;
> 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;
> - }
> + pev->event = pp->function;
> + } else
> + free(pp->function);
>
> - 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);
> + e_snprintf(pp->function, MAX_PROBE_ARGS, "0x%lx", vaddr);
> ret = 0;
>
> out:
> - if (map) {
> - dso__delete(map->dso);
> - map__delete(map);
> - }
> - if (function)
> - free(function);
> return ret;
> }
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/