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

From: Arnaldo Carvalho de Melo
Date: Mon Dec 23 2013 - 09:34:32 EST


Em Mon, Dec 23, 2013 at 06:39:16AM +0900, Masami Hiramatsu escreveu:
> (2013/12/21 3:01), Arnaldo Carvalho de Melo wrote:
> > 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?
> >
>
> OK, here is the examples from 0/3. :)

Pleasn readnthis message:

http://lkml.kernel.org/r/20131218141125.GT21999@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

It is unrelated to this code being discussed in this thread, but
provides another request for having better patch descriptions and to
have relevant discussion in the corresponding patch, not just on "0/N".

> Or, should I better put this into the patch description too?

Sure thing, please!

> For example)
> - Shows the probe-able lines of the given function

The above line would be more clear as:

"The following command shows the probe-able lines of a given user space
function, something that so far was only available in the 'perf probe'
tool for kernel space functions:"

- Arnaldo

> ----
> # ./perf probe -x perf --line map__load
> <map__load@/home/fedora/ksrc/linux-2.6/tools/perf/util/map.c:0>
> 0 int map__load(struct map *map, symbol_filter_t filter)
> 1 {
> 2 const char *name = map->dso->long_name;
> int nr;
>
> 5 if (dso__loaded(map->dso, map->type))
> 6 return 0;
>
> 8 nr = dso__load(map->dso, map, filter);
> 9 if (nr < 0) {
> 10 if (map->dso->has_build_id) {
> ----
>
> - Shows the available variables of the given line
> ----
> # ./perf probe -x perf --vars map__load:8
> Available variables at map__load:8
> @<map__load+96>
> char* name
> struct map* map
> symbol_filter_t filter
> @<map__find_symbol+112>
> char* name
> symbol_filter_t filter
> @<map__find_symbol_by_name+136>
> char* name
> symbol_filter_t filter
> @<map_groups__find_symbol_by_name+176>
> char* name
> struct map* map
> symbol_filter_t filter
> ----
>
> - Set a probe with available vars on the given line
> ----
> # ./perf probe -x perf --add 'map__load:8 $vars'
>
> Added new events:
> probe_perf:map__load (on map__load:8 with $vars)
> probe_perf:map__load_1 (on map__load:8 with $vars)
> probe_perf:map__load_2 (on map__load:8 with $vars)
> probe_perf:map__load_3 (on map__load:8 with $vars)
>
> You can now use it in all perf tools, such as:
>
> perf record -e probe_perf:map__load_3 -aR sleep 1
> ----
>
> > 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;
> >> }
> >>
> >
>
>
> --
> Masami HIRAMATSU
> IT Management Research Dept. Linux Technology Center
> Hitachi, Ltd., Yokohama Research Laboratory
> E-mail: masami.hiramatsu.pt@xxxxxxxxxxx
>
--
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/