perf probe: Provide perf interface for uprobes
authorSrikar Dronamraju <srikar@linux.vnet.ibm.com>
Mon, 16 Apr 2012 12:09:09 +0000 (17:39 +0530)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Fri, 11 May 2012 16:58:17 +0000 (13:58 -0300)
- Enhances perf to probe user space executables and libraries.
- Enhances -F/--funcs option of "perf probe" to list possible probe points in
  an executable file or library.
- Documents userspace probing support in perf.

[ Probing a function in the executable using function name  ]
perf probe -x /bin/zsh zfree

[ Probing a library function using function name ]
perf probe -x /lib64/libc.so.6 malloc

[ list probe-able functions in an executable ]
perf probe -F -x /bin/zsh

[ list probe-able functions in an library]
perf probe -F -x /lib/libc.so.6

Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Anton Arapov <anton@redhat.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Jim Keniston <jkenisto@linux.vnet.ibm.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Linux-mm <linux-mm@kvack.org>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/20120416120909.30661.99781.sendpatchset@srdronam.in.ibm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/Documentation/perf-probe.txt
tools/perf/builtin-probe.c
tools/perf/util/probe-event.c
tools/perf/util/probe-event.h
tools/perf/util/symbol.c
tools/perf/util/symbol.h

index 2780d9ce48bfe22b2fbaad38f39d7f2384c2d131..fb673bef4798bed4ba35cd9dd9631b443d427d08 100644 (file)
@@ -77,7 +77,8 @@ OPTIONS
 
 -F::
 --funcs::
-       Show available functions in given module or kernel.
+       Show available functions in given module or kernel. With -x/--exec,
+       can also list functions in a user space executable / shared library.
 
 --filter=FILTER::
        (Only for --vars and --funcs) Set filter. FILTER is a combination of glob
@@ -98,6 +99,11 @@ OPTIONS
 --max-probes::
        Set the maximum number of probe points for an event. Default is 128.
 
+-x::
+--exec=PATH::
+       Specify path to the executable or shared library file for user
+       space tracing. Can also be used with --funcs option.
+
 PROBE SYNTAX
 ------------
 Probe points are defined by following syntax.
@@ -182,6 +188,13 @@ Delete all probes on schedule().
 
  ./perf probe --del='schedule*'
 
+Add probes at zfree() function on /bin/zsh
+
+ ./perf probe -x /bin/zsh zfree
+
+Add probes at malloc() function on libc
+
+ ./perf probe -x /lib/libc.so.6 malloc
 
 SEE ALSO
 --------
index 4935c09dd5b58f402a6373c234042e449f58f71a..ee3d84a7c8957d4571cef223495b9a461419383a 100644 (file)
@@ -54,6 +54,7 @@ static struct {
        bool show_ext_vars;
        bool show_funcs;
        bool mod_events;
+       bool uprobes;
        int nevents;
        struct perf_probe_event events[MAX_PROBES];
        struct strlist *dellist;
@@ -75,6 +76,8 @@ static int parse_probe_event(const char *str)
                return -1;
        }
 
+       pev->uprobes = params.uprobes;
+
        /* Parse a perf-probe command into event */
        ret = parse_perf_probe_command(str, pev);
        pr_debug("%d arguments\n", pev->nargs);
@@ -125,6 +128,28 @@ static int opt_del_probe_event(const struct option *opt __used,
        return 0;
 }
 
+static int opt_set_target(const struct option *opt, const char *str,
+                       int unset __used)
+{
+       int ret = -ENOENT;
+
+       if  (str && !params.target) {
+               if (!strcmp(opt->long_name, "exec"))
+                       params.uprobes = true;
+#ifdef DWARF_SUPPORT
+               else if (!strcmp(opt->long_name, "module"))
+                       params.uprobes = false;
+#endif
+               else
+                       return ret;
+
+               params.target = str;
+               ret = 0;
+       }
+
+       return ret;
+}
+
 #ifdef DWARF_SUPPORT
 static int opt_show_lines(const struct option *opt __used,
                          const char *str, int unset __used)
@@ -246,9 +271,9 @@ static const struct option options[] = {
                   "file", "vmlinux pathname"),
        OPT_STRING('s', "source", &symbol_conf.source_prefix,
                   "directory", "path to kernel source"),
-       OPT_STRING('m', "module", &params.target,
-                  "modname|path",
-                  "target module name (for online) or path (for offline)"),
+       OPT_CALLBACK('m', "module", NULL, "modname|path",
+               "target module name (for online) or path (for offline)",
+               opt_set_target),
 #endif
        OPT__DRY_RUN(&probe_event_dry_run),
        OPT_INTEGER('\0', "max-probes", &params.max_probe_points,
@@ -260,6 +285,8 @@ static const struct option options[] = {
                     "\t\t\t(default: \"" DEFAULT_VAR_FILTER "\" for --vars,\n"
                     "\t\t\t \"" DEFAULT_FUNC_FILTER "\" for --funcs)",
                     opt_set_filter),
+       OPT_CALLBACK('x', "exec", NULL, "executable|path",
+                       "target executable name or path", opt_set_target),
        OPT_END()
 };
 
@@ -310,6 +337,10 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
                        pr_err("  Error: Don't use --list with --funcs.\n");
                        usage_with_options(probe_usage, options);
                }
+               if (params.uprobes) {
+                       pr_warning("  Error: Don't use --list with --exec.\n");
+                       usage_with_options(probe_usage, options);
+               }
                ret = show_perf_probe_events();
                if (ret < 0)
                        pr_err("  Error: Failed to show event list. (%d)\n",
@@ -333,8 +364,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
                if (!params.filter)
                        params.filter = strfilter__new(DEFAULT_FUNC_FILTER,
                                                       NULL);
-               ret = show_available_funcs(params.target,
-                                          params.filter);
+               ret = show_available_funcs(params.target, params.filter,
+                                       params.uprobes);
                strfilter__delete(params.filter);
                if (ret < 0)
                        pr_err("  Error: Failed to show functions."
@@ -343,7 +374,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
        }
 
 #ifdef DWARF_SUPPORT
-       if (params.show_lines) {
+       if (params.show_lines && !params.uprobes) {
                if (params.mod_events) {
                        pr_err("  Error: Don't use --line with"
                               " --add/--del.\n");
index 8a8ee64e72d14e52a12d6e26915950cbd9969649..59dccc98b5540284373ead1b43a74eb81a7a856d 100644 (file)
@@ -44,6 +44,7 @@
 #include "trace-event.h"       /* For __unused */
 #include "probe-event.h"
 #include "probe-finder.h"
+#include "session.h"
 
 #define MAX_CMDLEN 256
 #define MAX_PROBE_ARGS 128
@@ -70,6 +71,8 @@ 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 struct machine machine;
 
 /* Initialize symbol maps and path of vmlinux/modules */
@@ -170,6 +173,34 @@ const char *kernel_get_module_path(const char *module)
        return (dso) ? dso->long_name : NULL;
 }
 
+static int init_user_exec(void)
+{
+       int ret = 0;
+
+       symbol_conf.try_vmlinux_path = false;
+       symbol_conf.sort_by_name = true;
+       ret = symbol__init();
+
+       if (ret < 0)
+               pr_debug("Failed to init symbol map.\n");
+
+       return ret;
+}
+
+static int convert_to_perf_probe_point(struct probe_trace_point *tp,
+                                       struct perf_probe_point *pp)
+{
+       pp->function = strdup(tp->symbol);
+
+       if (pp->function == NULL)
+               return -ENOMEM;
+
+       pp->offset = tp->offset;
+       pp->retprobe = tp->retprobe;
+
+       return 0;
+}
+
 #ifdef DWARF_SUPPORT
 /* Open new debuginfo of given module */
 static struct debuginfo *open_debuginfo(const char *module)
@@ -224,10 +255,7 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
        if (ret <= 0) {
                pr_debug("Failed to find corresponding probes from "
                         "debuginfo. Use kprobe event information.\n");
-               pp->function = strdup(tp->symbol);
-               if (pp->function == NULL)
-                       return -ENOMEM;
-               pp->offset = tp->offset;
+               return convert_to_perf_probe_point(tp, pp);
        }
        pp->retprobe = tp->retprobe;
 
@@ -275,9 +303,20 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
                                          int max_tevs, const char *target)
 {
        bool need_dwarf = perf_probe_event_need_dwarf(pev);
-       struct debuginfo *dinfo = open_debuginfo(target);
+       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) {
                if (need_dwarf) {
                        pr_warning("Failed to open debuginfo file.\n");
@@ -603,23 +642,22 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
                pr_err("Failed to find symbol %s in kernel.\n", tp->symbol);
                return -ENOENT;
        }
-       pp->function = strdup(tp->symbol);
-       if (pp->function == NULL)
-               return -ENOMEM;
-       pp->offset = tp->offset;
-       pp->retprobe = tp->retprobe;
 
-       return 0;
+       return convert_to_perf_probe_point(tp, pp);
 }
 
 static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
                                struct probe_trace_event **tevs __unused,
-                               int max_tevs __unused, const char *mod __unused)
+                               int max_tevs __unused, const char *target)
 {
        if (perf_probe_event_need_dwarf(pev)) {
                pr_warning("Debuginfo-analysis is not supported.\n");
                return -ENOSYS;
        }
+
+       if (pev->uprobes)
+               return convert_name_to_addr(pev, target);
+
        return 0;
 }
 
@@ -1341,11 +1379,18 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)
        if (buf == NULL)
                return NULL;
 
-       len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s%s+%lu",
-                        tp->retprobe ? 'r' : 'p',
-                        tev->group, tev->event,
-                        tp->module ?: "", tp->module ? ":" : "",
-                        tp->symbol, tp->offset);
+       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);
+       else
+               len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s%s+%lu",
+                                tp->retprobe ? 'r' : 'p',
+                                tev->group, tev->event,
+                                tp->module ?: "", tp->module ? ":" : "",
+                                tp->symbol, tp->offset);
+
        if (len <= 0)
                goto error;
 
@@ -1364,7 +1409,7 @@ error:
 }
 
 static int convert_to_perf_probe_event(struct probe_trace_event *tev,
-                                      struct perf_probe_event *pev)
+                              struct perf_probe_event *pev, bool is_kprobe)
 {
        char buf[64] = "";
        int i, ret;
@@ -1376,7 +1421,11 @@ static int convert_to_perf_probe_event(struct probe_trace_event *tev,
                return -ENOMEM;
 
        /* Convert trace_point to probe_point */
-       ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point);
+       if (is_kprobe)
+               ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point);
+       else
+               ret = convert_to_perf_probe_point(&tev->point, &pev->point);
+
        if (ret < 0)
                return ret;
 
@@ -1472,7 +1521,26 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)
        memset(tev, 0, sizeof(*tev));
 }
 
-static int open_kprobe_events(bool readwrite)
+static void print_warn_msg(const char *file, bool is_kprobe)
+{
+
+       if (errno == ENOENT) {
+               const char *config;
+
+               if (!is_kprobe)
+                       config = "CONFIG_UPROBE_EVENTS";
+               else
+                       config = "CONFIG_KPROBE_EVENTS";
+
+               pr_warning("%s file does not exist - please rebuild kernel"
+                               " with %s.\n", file, config);
+       } else
+               pr_warning("Failed to open %s file: %s\n", file,
+                               strerror(errno));
+}
+
+static int open_probe_events(const char *trace_file, bool readwrite,
+                               bool is_kprobe)
 {
        char buf[PATH_MAX];
        const char *__debugfs;
@@ -1484,27 +1552,31 @@ static int open_kprobe_events(bool readwrite)
                return -ENOENT;
        }
 
-       ret = e_snprintf(buf, PATH_MAX, "%stracing/kprobe_events", __debugfs);
+       ret = e_snprintf(buf, PATH_MAX, "%s/%s", __debugfs, trace_file);
        if (ret >= 0) {
                pr_debug("Opening %s write=%d\n", buf, readwrite);
                if (readwrite && !probe_event_dry_run)
                        ret = open(buf, O_RDWR, O_APPEND);
                else
                        ret = open(buf, O_RDONLY, 0);
-       }
 
-       if (ret < 0) {
-               if (errno == ENOENT)
-                       pr_warning("kprobe_events file does not exist - please"
-                                " rebuild kernel with CONFIG_KPROBE_EVENT.\n");
-               else
-                       pr_warning("Failed to open kprobe_events file: %s\n",
-                                  strerror(errno));
+               if (ret < 0)
+                       print_warn_msg(buf, is_kprobe);
        }
        return ret;
 }
 
-/* Get raw string list of current kprobe_events */
+static int open_kprobe_events(bool readwrite)
+{
+       return open_probe_events("tracing/kprobe_events", readwrite, true);
+}
+
+static int open_uprobe_events(bool readwrite)
+{
+       return open_probe_events("tracing/uprobe_events", readwrite, false);
+}
+
+/* Get raw string list of current kprobe_events  or uprobe_events */
 static struct strlist *get_probe_trace_command_rawlist(int fd)
 {
        int ret, idx;
@@ -1569,36 +1641,26 @@ static int show_perf_probe_event(struct perf_probe_event *pev)
        return ret;
 }
 
-/* List up current perf-probe events */
-int show_perf_probe_events(void)
+static int __show_perf_probe_events(int fd, bool is_kprobe)
 {
-       int fd, ret;
+       int ret = 0;
        struct probe_trace_event tev;
        struct perf_probe_event pev;
        struct strlist *rawlist;
        struct str_node *ent;
 
-       setup_pager();
-       ret = init_vmlinux();
-       if (ret < 0)
-               return ret;
-
        memset(&tev, 0, sizeof(tev));
        memset(&pev, 0, sizeof(pev));
 
-       fd = open_kprobe_events(false);
-       if (fd < 0)
-               return fd;
-
        rawlist = get_probe_trace_command_rawlist(fd);
-       close(fd);
        if (!rawlist)
                return -ENOENT;
 
        strlist__for_each(ent, rawlist) {
                ret = parse_probe_trace_command(ent->s, &tev);
                if (ret >= 0) {
-                       ret = convert_to_perf_probe_event(&tev, &pev);
+                       ret = convert_to_perf_probe_event(&tev, &pev,
+                                                               is_kprobe);
                        if (ret >= 0)
                                ret = show_perf_probe_event(&pev);
                }
@@ -1612,6 +1674,33 @@ int show_perf_probe_events(void)
        return ret;
 }
 
+/* List up current perf-probe events */
+int show_perf_probe_events(void)
+{
+       int fd, ret;
+
+       setup_pager();
+       fd = open_kprobe_events(false);
+
+       if (fd < 0)
+               return fd;
+
+       ret = init_vmlinux();
+       if (ret < 0)
+               return ret;
+
+       ret = __show_perf_probe_events(fd, true);
+       close(fd);
+
+       fd = open_uprobe_events(false);
+       if (fd >= 0) {
+               ret = __show_perf_probe_events(fd, false);
+               close(fd);
+       }
+
+       return ret;
+}
+
 /* Get current perf-probe event names */
 static struct strlist *get_probe_trace_event_names(int fd, bool include_group)
 {
@@ -1717,7 +1806,11 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
        const char *event, *group;
        struct strlist *namelist;
 
-       fd = open_kprobe_events(true);
+       if (pev->uprobes)
+               fd = open_uprobe_events(true);
+       else
+               fd = open_kprobe_events(true);
+
        if (fd < 0)
                return fd;
        /* Get current event names */
@@ -1829,6 +1922,8 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
        tev->point.offset = pev->point.offset;
        tev->point.retprobe = pev->point.retprobe;
        tev->nargs = pev->nargs;
+       tev->uprobes = pev->uprobes;
+
        if (tev->nargs) {
                tev->args = zalloc(sizeof(struct probe_trace_arg)
                                   * tev->nargs);
@@ -1859,6 +1954,9 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
                }
        }
 
+       if (pev->uprobes)
+               return 1;
+
        /* Currently just checking function name from symbol map */
        sym = __find_kernel_function_by_name(tev->point.symbol, NULL);
        if (!sym) {
@@ -1894,12 +1992,18 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
        int i, j, ret;
        struct __event_package *pkgs;
 
+       ret = 0;
        pkgs = zalloc(sizeof(struct __event_package) * npevs);
+
        if (pkgs == NULL)
                return -ENOMEM;
 
-       /* Init vmlinux path */
-       ret = init_vmlinux();
+       if (!pevs->uprobes)
+               /* Init vmlinux path */
+               ret = init_vmlinux();
+       else
+               ret = init_user_exec();
+
        if (ret < 0) {
                free(pkgs);
                return ret;
@@ -1971,23 +2075,15 @@ error:
        return ret;
 }
 
-static int del_trace_probe_event(int fd, const char *group,
-                                 const char *event, struct strlist *namelist)
+static int del_trace_probe_event(int fd, const char *buf,
+                                                 struct strlist *namelist)
 {
-       char buf[128];
        struct str_node *ent, *n;
-       int found = 0, ret = 0;
-
-       ret = e_snprintf(buf, 128, "%s:%s", group, event);
-       if (ret < 0) {
-               pr_err("Failed to copy event.\n");
-               return ret;
-       }
+       int ret = -1;
 
        if (strpbrk(buf, "*?")) { /* Glob-exp */
                strlist__for_each_safe(ent, n, namelist)
                        if (strglobmatch(ent->s, buf)) {
-                               found++;
                                ret = __del_trace_probe_event(fd, ent);
                                if (ret < 0)
                                        break;
@@ -1996,40 +2092,43 @@ static int del_trace_probe_event(int fd, const char *group,
        } else {
                ent = strlist__find(namelist, buf);
                if (ent) {
-                       found++;
                        ret = __del_trace_probe_event(fd, ent);
                        if (ret >= 0)
                                strlist__remove(namelist, ent);
                }
        }
-       if (found == 0 && ret >= 0)
-               pr_info("Info: Event \"%s\" does not exist.\n", buf);
 
        return ret;
 }
 
 int del_perf_probe_events(struct strlist *dellist)
 {
-       int fd, ret = 0;
+       int ret = -1, ufd = -1, kfd = -1;
+       char buf[128];
        const char *group, *event;
        char *p, *str;
        struct str_node *ent;
-       struct strlist *namelist;
-
-       fd = open_kprobe_events(true);
-       if (fd < 0)
-               return fd;
+       struct strlist *namelist = NULL, *unamelist = NULL;
 
        /* Get current event names */
-       namelist = get_probe_trace_event_names(fd, true);
-       if (namelist == NULL)
-               return -EINVAL;
+       kfd = open_kprobe_events(true);
+       if (kfd < 0)
+               return kfd;
+
+       namelist = get_probe_trace_event_names(kfd, true);
+       ufd = open_uprobe_events(true);
+
+       if (ufd >= 0)
+               unamelist = get_probe_trace_event_names(ufd, true);
+
+       if (namelist == NULL && unamelist == NULL)
+               goto error;
 
        strlist__for_each(ent, dellist) {
                str = strdup(ent->s);
                if (str == NULL) {
                        ret = -ENOMEM;
-                       break;
+                       goto error;
                }
                pr_debug("Parsing: %s\n", str);
                p = strchr(str, ':');
@@ -2041,17 +2140,46 @@ int del_perf_probe_events(struct strlist *dellist)
                        group = "*";
                        event = str;
                }
+
+               ret = e_snprintf(buf, 128, "%s:%s", group, event);
+               if (ret < 0) {
+                       pr_err("Failed to copy event.");
+                       free(str);
+                       goto error;
+               }
+
                pr_debug("Group: %s, Event: %s\n", group, event);
-               ret = del_trace_probe_event(fd, group, event, namelist);
+
+               if (namelist)
+                       ret = del_trace_probe_event(kfd, buf, namelist);
+
+               if (unamelist && ret != 0)
+                       ret = del_trace_probe_event(ufd, buf, unamelist);
+
+               if (ret != 0)
+                       pr_info("Info: Event \"%s\" does not exist.\n", buf);
+
                free(str);
-               if (ret < 0)
-                       break;
        }
-       strlist__delete(namelist);
-       close(fd);
+
+error:
+       if (kfd >= 0) {
+               if (namelist)
+                       strlist__delete(namelist);
+
+               close(kfd);
+       }
+
+       if (ufd >= 0) {
+               if (unamelist)
+                       strlist__delete(unamelist);
+
+               close(ufd);
+       }
 
        return ret;
 }
+
 /* TODO: don't use a global variable for filter ... */
 static struct strfilter *available_func_filter;
 
@@ -2068,30 +2196,152 @@ static int filter_available_functions(struct map *map __unused,
        return 1;
 }
 
-int show_available_funcs(const char *target, struct strfilter *_filter)
+static int __show_available_funcs(struct map *map)
+{
+       if (map__load(map, filter_available_functions)) {
+               pr_err("Failed to load map.\n");
+               return -EINVAL;
+       }
+       if (!dso__sorted_by_name(map->dso, map->type))
+               dso__sort_by_name(map->dso, map->type);
+
+       dso__fprintf_symbols_by_name(map->dso, map->type, stdout);
+       return 0;
+}
+
+static int available_kernel_funcs(const char *module)
 {
        struct map *map;
        int ret;
 
-       setup_pager();
-
        ret = init_vmlinux();
        if (ret < 0)
                return ret;
 
-       map = kernel_get_module_map(target);
+       map = kernel_get_module_map(module);
        if (!map) {
-               pr_err("Failed to find %s map.\n", (target) ? : "kernel");
+               pr_err("Failed to find %s map.\n", (module) ? : "kernel");
                return -EINVAL;
        }
+       return __show_available_funcs(map);
+}
+
+static int available_user_funcs(const char *target)
+{
+       struct map *map;
+       int ret;
+
+       ret = init_user_exec();
+       if (ret < 0)
+               return ret;
+
+       map = dso__new_map(target);
+       ret = __show_available_funcs(map);
+       dso__delete(map->dso);
+       map__delete(map);
+       return ret;
+}
+
+int show_available_funcs(const char *target, struct strfilter *_filter,
+                                       bool user)
+{
+       setup_pager();
        available_func_filter = _filter;
+
+       if (!user)
+               return available_kernel_funcs(target);
+
+       return available_user_funcs(target);
+}
+
+/*
+ * 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, *name = 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;
+       }
+
+       name = realpath(exec, NULL);
+       if (!name) {
+               pr_warning("Cannot find realpath for %s.\n", exec);
+               goto out;
+       }
+       map = dso__new_map(name);
+       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");
-               return -EINVAL;
+               goto out;
        }
-       if (!dso__sorted_by_name(map->dso, map->type))
-               dso__sort_by_name(map->dso, map->type);
 
-       dso__fprintf_symbols_by_name(map->dso, map->type, stdout);
-       return 0;
+       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;
+
+               pev->group = zalloc(sizeof(char *) * 64);
+               ptr1 = strdup(basename(exec));
+               if (ptr1) {
+                       ptr2 = strpbrk(ptr1, "-._");
+                       if (ptr2)
+                               *ptr2 = '\0';
+                       e_snprintf(pev->group, 64, "%s_%s", PERFPROBE_GROUP,
+                                       ptr1);
+                       free(ptr1);
+               }
+       }
+       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);
+       if (name)
+               free(name);
+       return ret;
 }
index a7dee835f49c45698d72406864a0daa29649315e..f9f3de8b4220b9c4989016f212192cb48cd83aeb 100644 (file)
@@ -7,7 +7,7 @@
 
 extern bool probe_event_dry_run;
 
-/* kprobe-tracer tracing point */
+/* kprobe-tracer and uprobe-tracer tracing point */
 struct probe_trace_point {
        char            *symbol;        /* Base symbol */
        char            *module;        /* Module name */
@@ -21,7 +21,7 @@ struct probe_trace_arg_ref {
        long                            offset; /* Offset value */
 };
 
-/* kprobe-tracer tracing argument */
+/* kprobe-tracer and uprobe-tracer tracing argument */
 struct probe_trace_arg {
        char                            *name;  /* Argument name */
        char                            *value; /* Base value */
@@ -29,12 +29,13 @@ struct probe_trace_arg {
        struct probe_trace_arg_ref      *ref;   /* Referencing offset */
 };
 
-/* kprobe-tracer tracing event (point + arg) */
+/* kprobe-tracer and uprobe-tracer tracing event (point + arg) */
 struct probe_trace_event {
        char                            *event; /* Event name */
        char                            *group; /* Group name */
        struct probe_trace_point        point;  /* Trace point */
        int                             nargs;  /* Number of args */
+       bool                            uprobes;        /* uprobes only */
        struct probe_trace_arg          *args;  /* Arguments */
 };
 
@@ -70,6 +71,7 @@ struct perf_probe_event {
        char                    *group; /* Group name */
        struct perf_probe_point point;  /* Probe point */
        int                     nargs;  /* Number of arguments */
+       bool                    uprobes;
        struct perf_probe_arg   *args;  /* Arguments */
 };
 
@@ -129,8 +131,8 @@ extern int show_line_range(struct line_range *lr, const char *module);
 extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
                               int max_probe_points, const char *module,
                               struct strfilter *filter, bool externs);
-extern int show_available_funcs(const char *module, struct strfilter *filter);
-
+extern int show_available_funcs(const char *module, struct strfilter *filter,
+                               bool user);
 
 /* Maximum index number of event-name postfix */
 #define MAX_EVENT_INDEX        1024
index c0a028c3ebaf35905e99e69f4cef76e5e343f44f..caaf75ad645a23b7d0423083d5799c8fc89dffc6 100644 (file)
@@ -2784,3 +2784,11 @@ int machine__load_vmlinux_path(struct machine *machine, enum map_type type,
 
        return ret;
 }
+
+struct map *dso__new_map(const char *name)
+{
+       struct dso *dso = dso__new(name);
+       struct map *map = map__new2(0, dso, MAP__FUNCTION);
+
+       return map;
+}
index 1f003884f1abb596333b2fac6a27ef1f41b1c716..5649d63798cbfc0a24f3892d5a83b0d5809512ee 100644 (file)
@@ -242,6 +242,7 @@ void dso__set_long_name(struct dso *dso, char *name);
 void dso__set_build_id(struct dso *dso, void *build_id);
 void dso__read_running_kernel_build_id(struct dso *dso,
                                       struct machine *machine);
+struct map *dso__new_map(const char *name);
 struct symbol *dso__find_symbol(struct dso *dso, enum map_type type,
                                u64 addr);
 struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type,