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)

Signed-off-by: Masami Hiramatsu <masami.hiramatsu...@hitachi.com>
---
 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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to