Em Wed, Apr 11, 2012 at 07:27:42PM +0530, Srikar Dronamraju escreveu: > From: Srikar Dronamraju <srikar@xxxxxxxxxxxxxxxxxx> > > - 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 Can we avoid the need for -x? I.e. we could figure out it is userspace and act accordingly. - Arnaldo > Signed-off-by: Srikar Dronamraju <srikar@xxxxxxxxxxxxxxxxxx> > --- > > Changelog: > (v9) > - Handled comments from Masami Hiramatsu that helps reduce code changes. > - Handled usability comment(print complete name of file) from Ingo Molnar. > > (v5) > - Removed the separate documentation change patch and added the > documentation changes as part of this patch. > > Usage: > [root@localhost ~]# perf probe -x /bin/zsh zfree > Add new event: > probe_zsh:zfree (on /bin/zsh:0x45400) > > You can now use it on all perf tools, such as: > > perf record -e probe_zsh:zfree -aR sleep 1 > > [root@localhost ~]# perf record -e probe_zsh:zfree -aR sleep 15 > [ perf record: Woken up 1 times to write data ] > [ perf record: Captured and wrote 0.314 MB perf.data (~13715 samples) ] > [root@localhost ~]# perf report --stdio > # Events: 3K probe_zsh:zfree > # > # Overhead Command Shared Object Symbol > # ........ ....... ............. ...... > # > 100.00% zsh zsh [.] zfree > > > # > # (For a higher level overview, try: perf report --sort comm,dso) > # > [root@localhost ~] > > [ Probing a library function using function name ] > -------------------------------------------------- > [root@localhost]# > [root@localhost]# perf probe -x /lib64/libc.so.6 malloc > Add new event: > probe_libc:malloc (on /lib64/libc-2.5.so:0x74dc0) > > You can now use it on all perf tools, such as: > > perf record -e probe_libc:malloc -aR sleep 1 > > [root@localhost]# > [root@localhost]# perf probe --list > probe_libc:malloc (on /lib64/libc-2.5.so:0x0000000000074dc0) > > > Show last 10 functions in /bin/zsh. > > # perf probe -F -x /bin/zsh | tail > zstrtol > ztrcmp > ztrdup > ztrduppfx > ztrftime > ztrlen > ztrncpy > ztrsub > zwarn > zwarnnam > > Show first 10 functions in /lib/libc.so.6 > > # perf probe -F -x /lib/libc.so.6 | head > _IO_adjust_column > _IO_adjust_wcolumn > _IO_default_doallocate > _IO_default_finish > _IO_default_pbackfail > _IO_default_uflow > _IO_default_xsgetn > _IO_default_xsputn > _IO_do_write@@GLIBC_2.2.5 > _IO_doallocbuf > > tools/perf/Documentation/perf-probe.txt | 15 + > tools/perf/builtin-probe.c | 42 +++ > tools/perf/util/probe-event.c | 424 +++++++++++++++++++++++++------ > tools/perf/util/probe-event.h | 12 + > tools/perf/util/symbol.c | 8 + > tools/perf/util/symbol.h | 1 > 6 files changed, 405 insertions(+), 97 deletions(-) > > diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt > index 2780d9c..fb673be 100644 > --- a/tools/perf/Documentation/perf-probe.txt > +++ b/tools/perf/Documentation/perf-probe.txt > @@ -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 > -------- > diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c > index 4935c09..c1bf0d8 100644 > --- a/tools/perf/builtin-probe.c > +++ b/tools/perf/builtin-probe.c > @@ -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,7 @@ 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 +127,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 +270,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", ¶ms.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", ¶ms.max_probe_points, > @@ -260,6 +284,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 +336,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 +363,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 +373,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"); > diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c > index 8a8ee64..b7dec82 100644 > --- a/tools/perf/util/probe-event.c > +++ b/tools/perf/util/probe-event.c > @@ -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 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev) > } > > 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,7 @@ 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 +1953,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 +1991,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 +2074,15 @@ static int __del_trace_probe_event(int fd, struct str_node *ent) > 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 +2091,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 +2139,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 +2195,157 @@ 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; > + } > + > + if (perf_probe_event_need_dwarf(pev)) { > + pr_warning("No dwarf based probes 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; > } > - 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; > } > diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h > index a7dee83..f9f3de8 100644 > --- a/tools/perf/util/probe-event.h > +++ b/tools/perf/util/probe-event.h > @@ -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 > diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c > index c0a028c..caaf75a 100644 > --- a/tools/perf/util/symbol.c > +++ b/tools/perf/util/symbol.c > @@ -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; > +} > diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h > index ac49ef2..9e7742c 100644 > --- a/tools/perf/util/symbol.h > +++ b/tools/perf/util/symbol.h > @@ -237,6 +237,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, > -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@xxxxxxxxx. For more info on Linux MM, see: http://www.linux-mm.org/ . Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/ Don't email: <a href=mailto:"dont@xxxxxxxxx"> email@xxxxxxxxx </a>