From 6dbe31f7baf6d50fa396440dae0808bb712e9a37 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 23 Apr 2015 22:46:14 +0900 Subject: perf probe: Remove all probes matches given pattern at once Fix perf-probe --del option to delete all matched probes in both of kprobes and uprobes at once. When we have 2 or more events on different binaries as below, ---- # ./perf probe -l probe:vfs_read (on vfs_read@ksrc/linux-3/fs/read_write.c) probe_libc:malloc (on __libc_malloc@malloc/malloc.c in /usr/lib64/libc-2.17 ---- Trying to remove all event with '*' just removes kprobe events at first. ---- # ./perf probe -d \* Removed event: probe:vfs_read ---- And in 2nd try, it removes all uprobe events. ---- # ./perf probe -d \* Removed event: probe_libc:malloc ---- This fixes to remove all event at once as below. ---- # ./perf probe -d \* Removed event: probe:vfs_read Removed event: probe_libc:malloc ---- Reported-by: Arnaldo Carvalho de Melo Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20150423134614.26128.18106.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index d8bb616ff57c..291bf23a5013 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -2686,7 +2686,7 @@ static int del_trace_probe_event(int fd, const char *buf, struct strlist *namelist) { struct str_node *ent, *n; - int ret = -1; + int ret = -ENOENT; if (strpbrk(buf, "*?")) { /* Glob-exp */ strlist__for_each_safe(ent, n, namelist) @@ -2710,7 +2710,7 @@ static int del_trace_probe_event(int fd, const char *buf, int del_perf_probe_events(struct strlist *dellist) { - int ret = -1, ufd = -1, kfd = -1; + int ret = -1, ret2, ufd = -1, kfd = -1; char buf[128]; const char *group, *event; char *p, *str; @@ -2731,8 +2731,10 @@ int del_perf_probe_events(struct strlist *dellist) goto error; } - if (namelist == NULL && unamelist == NULL) + if (namelist == NULL && unamelist == NULL) { + ret = -ENOENT; goto error; + } strlist__for_each(ent, dellist) { str = strdup(ent->s); @@ -2759,17 +2761,23 @@ int del_perf_probe_events(struct strlist *dellist) } pr_debug("Group: %s, Event: %s\n", group, event); + free(str); + ret = ret2 = -ENOENT; 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); + if ((ret >= 0 || ret == -ENOENT) && unamelist) + ret2 = del_trace_probe_event(ufd, buf, unamelist); - free(str); + /* Since we can remove probes which already removed, don't check it */ + if (ret == -ENOENT && ret2 == -ENOENT) + pr_debug("Event \"%s\" does not exist.\n", buf); + else if (ret < 0 || ret2 < 0) { + if (ret >= 0) + ret = ret2; + break; + } } error: -- cgit v1.2.3-59-g8ed1b From d5c2e2c17ae1d630ddbceb53a264f24cc99703a4 Mon Sep 17 00:00:00 2001 From: "Naveen N. Rao" Date: Tue, 28 Apr 2015 17:35:39 +0530 Subject: perf probe ppc64le: Prefer symbol table lookup over DWARF Use symbol table lookups by default if DWARF is not necessary, since powerpc ABIv2 encodes local entry points in the symbol table and the function entry address in DWARF may not be appropriate for kprobes, as described here: https://sourceware.org/bugzilla/show_bug.cgi?id=17638 "The DWARF address ranges deliberately include the *whole* function, both global and local entry points." ... "If you want to set probes on a local entry point, you should look up the symbol in the main symbol table (not DWARF), and check the st_other bits; they will indicate whether the function has a local entry point, and what its offset from the global entry point is. Note that GDB does the same when setting a breakpoint on a function entry." Signed-off-by: Naveen N. Rao Reviewed-by: Srikar Dronamraju Cc: Ananth N Mavinakayanahalli Cc: Masami Hiramatsu Cc: Michael Ellerman Cc: Sukadev Bhattiprolu Cc: linuxppc-dev@lists.ozlabs.org Link: http://lkml.kernel.org/r/88a10e22f4aaba2aef812824ca4b10d7beeea012.1430217967.git.naveen.n.rao@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/powerpc/util/sym-handling.c | 8 ++++++++ tools/perf/util/probe-event.c | 8 ++++++++ tools/perf/util/probe-event.h | 1 + 3 files changed, 17 insertions(+) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/arch/powerpc/util/sym-handling.c b/tools/perf/arch/powerpc/util/sym-handling.c index 012a0f8098de..a1700609e145 100644 --- a/tools/perf/arch/powerpc/util/sym-handling.c +++ b/tools/perf/arch/powerpc/util/sym-handling.c @@ -9,6 +9,7 @@ #include "debug.h" #include "symbol.h" #include "map.h" +#include "probe-event.h" #ifdef HAVE_LIBELF_SUPPORT bool elf__needs_adjust_symbols(GElf_Ehdr ehdr) @@ -57,3 +58,10 @@ int arch__compare_symbol_names(const char *namea, const char *nameb) return strcmp(namea, nameb); } #endif + +#if defined(_CALL_ELF) && _CALL_ELF == 2 +bool arch__prefers_symtab(void) +{ + return true; +} +#endif diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 291bf23a5013..4dfb412ff1f4 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -2567,6 +2567,8 @@ err_out: goto out; } +bool __weak arch__prefers_symtab(void) { return false; } + static int convert_to_probe_trace_events(struct perf_probe_event *pev, struct probe_trace_event **tevs, int max_tevs, const char *target) @@ -2582,6 +2584,12 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, } } + if (arch__prefers_symtab() && !perf_probe_event_need_dwarf(pev)) { + ret = find_probe_trace_events_from_map(pev, tevs, max_tevs, target); + if (ret > 0) + return ret; /* Found in symbol table */ + } + /* Convert perf_probe_event with debuginfo */ ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target); if (ret != 0) diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index d6b783447be9..52bca4bf07ca 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -135,6 +135,7 @@ extern int show_available_vars(struct perf_probe_event *pevs, int npevs, struct strfilter *filter, bool externs); extern int show_available_funcs(const char *module, struct strfilter *filter, bool user); +bool arch__prefers_symtab(void); /* Maximum index number of event-name postfix */ #define MAX_EVENT_INDEX 1024 -- cgit v1.2.3-59-g8ed1b From 7b6ff0bdbf4f7f429c2116cca92a6d171217449e Mon Sep 17 00:00:00 2001 From: "Naveen N. Rao" Date: Tue, 28 Apr 2015 17:35:40 +0530 Subject: perf probe ppc64le: Fixup function entry if using kallsyms lookup On powerpc ABIv2, if no debug-info is found and we use kallsyms, we need to fixup the function entry to point to the local entry point. Use offset of 8 since current toolchains always generate 2 instructions (8 bytes). Signed-off-by: Naveen N. Rao Reviewed-by: Srikar Dronamraju Cc: Ananth N Mavinakayanahalli Cc: Masami Hiramatsu Cc: Michael Ellerman Cc: Sukadev Bhattiprolu Cc: linuxppc-dev@lists.ozlabs.org Link: http://lkml.kernel.org/r/92253021e77a104b23b615c8c23bf9501dfe60bf.1430217967.git.naveen.n.rao@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/powerpc/util/sym-handling.c | 15 +++++++++++++++ tools/perf/util/probe-event.c | 5 +++++ tools/perf/util/probe-event.h | 2 ++ 3 files changed, 22 insertions(+) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/arch/powerpc/util/sym-handling.c b/tools/perf/arch/powerpc/util/sym-handling.c index a1700609e145..bbc1a50768dd 100644 --- a/tools/perf/arch/powerpc/util/sym-handling.c +++ b/tools/perf/arch/powerpc/util/sym-handling.c @@ -64,4 +64,19 @@ bool arch__prefers_symtab(void) { return true; } + +#define PPC64LE_LEP_OFFSET 8 + +void arch__fix_tev_from_maps(struct perf_probe_event *pev, + struct probe_trace_event *tev, struct map *map) +{ + /* + * ppc64 ABIv2 local entry point is currently always 2 instructions + * (8 bytes) after the global entry point. + */ + if (!pev->uprobes && map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS) { + tev->point.address += PPC64LE_LEP_OFFSET; + tev->point.offset += PPC64LE_LEP_OFFSET; + } +} #endif diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 4dfb412ff1f4..eb75a5efcfd7 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -2447,6 +2447,10 @@ static int find_probe_functions(struct map *map, char *name) #define strdup_or_goto(str, label) \ ({ char *__p = strdup(str); if (!__p) goto label; __p; }) +void __weak arch__fix_tev_from_maps(struct perf_probe_event *pev __maybe_unused, + struct probe_trace_event *tev __maybe_unused, + struct map *map __maybe_unused) { } + /* * Find probe function addresses from map. * Return an error or the number of found probe_trace_event @@ -2553,6 +2557,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, strdup_or_goto(pev->args[i].type, nomem_out); } + arch__fix_tev_from_maps(pev, tev, map); } out: diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 52bca4bf07ca..180f142cc170 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -136,6 +136,8 @@ extern int show_available_vars(struct perf_probe_event *pevs, int npevs, extern int show_available_funcs(const char *module, struct strfilter *filter, bool user); bool arch__prefers_symtab(void); +void arch__fix_tev_from_maps(struct perf_probe_event *pev, + struct probe_trace_event *tev, struct map *map); /* Maximum index number of event-name postfix */ #define MAX_EVENT_INDEX 1024 -- cgit v1.2.3-59-g8ed1b From 3099c026002e97b8c173d9d0bbdfc39257d14402 Mon Sep 17 00:00:00 2001 From: "Naveen N. Rao" Date: Tue, 28 Apr 2015 17:35:34 +0530 Subject: perf probe: Improve detection of file/function name in the probe pattern Currently, perf probe considers patterns including a '.' to be a file. However, this causes problems on powerpc ABIv1 where all functions have a leading '.': $ perf probe -F | grep schedule_timeout_interruptible .schedule_timeout_interruptible $ perf probe .schedule_timeout_interruptible Semantic error :File always requires line number or lazy pattern. Error: Command Parse Error. Fix this: - by checking the probe pattern in more detail, and - skipping leading dot if one exists when creating/deleting events. Signed-off-by: Naveen N. Rao Reviewed-by: Srikar Dronamraju Acked-by: Masami Hiramatsu Cc: Ananth N Mavinakayanahalli Cc: Michael Ellerman Cc: Srikar Dronamraju Cc: Sukadev Bhattiprolu Cc: linuxppc-dev@lists.ozlabs.org Link: http://lkml.kernel.org/r/db680f7cb11c4452b632f908e67151f3aa0f4602.1430217967.git.naveen.n.rao@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index eb75a5efcfd7..416c10f8fd2a 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -1077,6 +1077,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) struct perf_probe_point *pp = &pev->point; char *ptr, *tmp; char c, nc = 0; + bool file_spec = false; /* * * perf probe [EVENT=]SRC[:LN|;PTN] @@ -1105,6 +1106,23 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) arg = tmp; } + /* + * Check arg is function or file name and copy it. + * + * We consider arg to be a file spec if and only if it satisfies + * all of the below criteria:: + * - it does not include any of "+@%", + * - it includes one of ":;", and + * - it has a period '.' in the name. + * + * Otherwise, we consider arg to be a function specification. + */ + if (!strpbrk(arg, "+@%") && (ptr = strpbrk(arg, ";:")) != NULL) { + /* This is a file spec if it includes a '.' before ; or : */ + if (memchr(arg, '.', ptr - arg)) + file_spec = true; + } + ptr = strpbrk(arg, ";:+@%"); if (ptr) { nc = *ptr; @@ -1115,10 +1133,9 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) if (tmp == NULL) return -ENOMEM; - /* Check arg is function or file and copy it */ - if (strchr(tmp, '.')) /* File */ + if (file_spec) pp->file = tmp; - else /* Function */ + else pp->function = tmp; /* Parse other options */ @@ -2265,6 +2282,9 @@ static int get_new_event_name(char *buf, size_t len, const char *base, { int i, ret; + if (*base == '.') + base++; + /* Try no suffix */ ret = e_snprintf(buf, len, "%s", base); if (ret < 0) { @@ -2766,6 +2786,9 @@ int del_perf_probe_events(struct strlist *dellist) event = str; } + if (event && *event == '.') + event++; + ret = e_snprintf(buf, 128, "%s:%s", group, event); if (ret < 0) { pr_err("Failed to copy event."); -- cgit v1.2.3-59-g8ed1b From b6a896438b3275df434a8f99bee58292b31693bd Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 24 Apr 2015 18:47:50 +0900 Subject: perf probe: Accept filter argument for --list Currently, perf-probe --list option ignores given event filter. ---- # ./perf probe -l vfs\* probe:vfs_read (on vfs_read@ksrc/linux-3/fs/read_write.c) probe_libc:malloc (on __libc_malloc@malloc/malloc.c in /usr/lib64/libc-2.17.so) ---- This changes --list option to accept the event filter argument as below. ---- # ./perf probe -l vfs\* probe:vfs_read (on vfs_read@ksrc/linux-3/fs/read_write.c) # ./perf probe -l \*libc:\* probe_libc:malloc (on __libc_malloc@malloc/malloc.c in /usr/lib64/libc-2.17.so) ---- Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20150424094750.23967.53868.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-probe.txt | 6 +-- tools/perf/builtin-probe.c | 70 ++++++++++++++++++++++----------- tools/perf/util/probe-event.c | 27 +++++++++++-- tools/perf/util/probe-event.h | 2 +- 4 files changed, 73 insertions(+), 32 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index a4a3cc76b36b..d0feb8e6eeef 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -14,7 +14,7 @@ or or 'perf probe' [options] --del='[GROUP:]EVENT' [...] or -'perf probe' --list +'perf probe' --list[=[GROUP:]EVENT] or 'perf probe' [options] --line='LINE' or @@ -66,8 +66,8 @@ OPTIONS classes(e.g. [a-z], [!A-Z]). -l:: ---list:: - List up current probe events. +--list[=[GROUP:]EVENT]:: + List up current probe events. This can also accept filtering patterns of event names. -L:: --line=:: diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index be170757ebd6..feca3165f957 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -44,6 +44,7 @@ #define DEFAULT_VAR_FILTER "!__k???tab_* & !__crc_*" #define DEFAULT_FUNC_FILTER "!_*" +#define DEFAULT_LIST_FILTER "*:*" /* Session management structure */ static struct { @@ -93,6 +94,28 @@ static int parse_probe_event(const char *str) return ret; } +static int params_add_filter(const char *str) +{ + const char *err = NULL; + int ret = 0; + + pr_debug2("Add filter: %s\n", str); + if (!params.filter) { + params.filter = strfilter__new(str, &err); + if (!params.filter) + ret = err ? -EINVAL : -ENOMEM; + } else + ret = strfilter__or(params.filter, str, &err); + + if (ret == -EINVAL) { + pr_err("Filter parse error at %td.\n", err - str + 1); + pr_err("Source: \"%s\"\n", str); + pr_err(" %*c\n", (int)(err - str + 1), '^'); + } + + return ret; +} + static int set_target(const char *ptr) { int found = 0; @@ -180,6 +203,18 @@ static int opt_del_probe_event(const struct option *opt __maybe_unused, return 0; } +static int opt_list_probe_event(const struct option *opt __maybe_unused, + const char *str, int unset) +{ + if (!unset) + params.list_events = true; + + if (str) + return params_add_filter(str); + + return 0; +} + static int opt_set_target(const struct option *opt, const char *str, int unset __maybe_unused) { @@ -261,26 +296,10 @@ static int opt_show_vars(const struct option *opt __maybe_unused, static int opt_set_filter(const struct option *opt __maybe_unused, const char *str, int unset __maybe_unused) { - const char *err; - int ret = 0; + if (str) + return params_add_filter(str); - if (str) { - pr_debug2("Set filter: %s\n", str); - if (!params.filter) { - params.filter = strfilter__new(str, &err); - if (!params.filter) - ret = err ? -EINVAL : -ENOMEM; - } else - ret = strfilter__or(params.filter, str, &err); - - if (ret == -EINVAL) { - pr_err("Filter parse error at %td.\n", err - str + 1); - pr_err("Source: \"%s\"\n", str); - pr_err(" %*c\n", (int)(err - str + 1), '^'); - } - } - - return ret; + return 0; } static int init_params(void) @@ -320,21 +339,22 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) "perf probe [] 'PROBEDEF' ['PROBEDEF' ...]", "perf probe [] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", "perf probe [] --del '[GROUP:]EVENT' ...", - "perf probe --list", + "perf probe --list [GROUP:]EVENT ...", #ifdef HAVE_DWARF_SUPPORT "perf probe [] --line 'LINEDESC'", "perf probe [] --vars 'PROBEPOINT'", #endif "perf probe [] --funcs", NULL -}; + }; struct option options[] = { OPT_INCR('v', "verbose", &verbose, "be more verbose (show parsed arguments, etc)"), OPT_BOOLEAN('q', "quiet", ¶ms.quiet, "be quiet (do not show any mesages)"), - OPT_BOOLEAN('l', "list", ¶ms.list_events, - "list up current probe events"), + OPT_CALLBACK_DEFAULT('l', "list", NULL, "[GROUP:]EVENT", + "list up probe events", opt_list_probe_event, + DEFAULT_LIST_FILTER), OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.", opt_del_probe_event), OPT_CALLBACK('a', "add", NULL, @@ -448,7 +468,9 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) pr_warning(" Error: Don't use --list with --exec.\n"); usage_with_options(probe_usage, options); } - ret = show_perf_probe_events(); + ret = show_perf_probe_events(params.filter); + strfilter__delete(params.filter); + params.filter = NULL; if (ret < 0) pr_err_with_code(" Error: Failed to show event list.", ret); return ret; diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 416c10f8fd2a..5995d81d2bad 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -2146,7 +2146,23 @@ static int show_perf_probe_event(struct perf_probe_event *pev, return ret; } -static int __show_perf_probe_events(int fd, bool is_kprobe) +static bool filter_probe_trace_event(struct probe_trace_event *tev, + struct strfilter *filter) +{ + char tmp[128]; + + /* At first, check the event name itself */ + if (strfilter__compare(filter, tev->event)) + return true; + + /* Next, check the combination of name and group */ + if (e_snprintf(tmp, 128, "%s:%s", tev->group, tev->event) < 0) + return false; + return strfilter__compare(filter, tmp); +} + +static int __show_perf_probe_events(int fd, bool is_kprobe, + struct strfilter *filter) { int ret = 0; struct probe_trace_event tev; @@ -2164,12 +2180,15 @@ static int __show_perf_probe_events(int fd, bool is_kprobe) strlist__for_each(ent, rawlist) { ret = parse_probe_trace_command(ent->s, &tev); if (ret >= 0) { + if (!filter_probe_trace_event(&tev, filter)) + goto next; ret = convert_to_perf_probe_event(&tev, &pev, is_kprobe); if (ret >= 0) ret = show_perf_probe_event(&pev, tev.point.module); } +next: clear_perf_probe_event(&pev); clear_probe_trace_event(&tev); if (ret < 0) @@ -2181,7 +2200,7 @@ static int __show_perf_probe_events(int fd, bool is_kprobe) } /* List up current perf-probe events */ -int show_perf_probe_events(void) +int show_perf_probe_events(struct strfilter *filter) { int kp_fd, up_fd, ret; @@ -2193,7 +2212,7 @@ int show_perf_probe_events(void) kp_fd = open_kprobe_events(false); if (kp_fd >= 0) { - ret = __show_perf_probe_events(kp_fd, true); + ret = __show_perf_probe_events(kp_fd, true, filter); close(kp_fd); if (ret < 0) goto out; @@ -2207,7 +2226,7 @@ int show_perf_probe_events(void) } if (up_fd >= 0) { - ret = __show_perf_probe_events(up_fd, false); + ret = __show_perf_probe_events(up_fd, false, filter); close(up_fd); } out: diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 180f142cc170..ec13362d882f 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -127,7 +127,7 @@ extern const char *kernel_get_module_path(const char *module); extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, int max_probe_points, bool force_add); extern int del_perf_probe_events(struct strlist *dellist); -extern int show_perf_probe_events(void); +extern int show_perf_probe_events(struct strfilter *filter); extern int show_line_range(struct line_range *lr, const char *module, bool user); extern int show_available_vars(struct perf_probe_event *pevs, int npevs, -- cgit v1.2.3-59-g8ed1b From 307a464b2342a502da492f0ada8cefd6ab7f63a7 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 5 May 2015 11:29:48 +0900 Subject: perf probe: Allow to use filter on --del command This makes perf-probe --del option to accept filter rules not only simple glob pattern. This simplifies the code and improve the flexibility. E.g. if we remove 2 different pattern events, we need 2 -d options. ---- # ./perf probe -d vfs\* -d malloc Removed event: probe_libc:malloc Removed event: probe:vfs_read ---- This allows you to joint the 2 patterns with '|'. ---- # ./perf probe -d 'vfs*|malloc' Removed event: probe:vfs_read Removed event: probe_libc:malloc ---- Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20150505022948.23399.4197.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-probe.c | 16 +++---- tools/perf/util/probe-event.c | 102 +++++++++++++----------------------------- tools/perf/util/probe-event.h | 2 +- 3 files changed, 39 insertions(+), 81 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index feca3165f957..1f41b4eb0c5a 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -55,12 +55,12 @@ static struct { bool show_ext_vars; bool show_funcs; bool mod_events; + bool del_events; bool uprobes; bool quiet; bool target_used; int nevents; struct perf_probe_event events[MAX_PROBES]; - struct strlist *dellist; struct line_range line_range; char *target; int max_probe_points; @@ -195,10 +195,8 @@ static int opt_del_probe_event(const struct option *opt __maybe_unused, const char *str, int unset __maybe_unused) { if (str) { - params.mod_events = true; - if (!params.dellist) - params.dellist = strlist__new(true, NULL); - strlist__add(params.dellist, str); + params.del_events = true; + return params_add_filter(str); } return 0; } @@ -313,8 +311,6 @@ static void cleanup_params(void) for (i = 0; i < params.nevents; i++) clear_perf_probe_event(params.events + i); - if (params.dellist) - strlist__delete(params.dellist); line_range__clear(¶ms.line_range); free(params.target); if (params.filter) @@ -454,7 +450,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) if (params.max_probe_points == 0) params.max_probe_points = MAX_PROBES; - if ((!params.nevents && !params.dellist && !params.list_events && + if ((!params.nevents && !params.del_events && !params.list_events && !params.show_lines && !params.show_funcs)) usage_with_options(probe_usage, options); @@ -514,8 +510,8 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) } #endif - if (params.dellist) { - ret = del_perf_probe_events(params.dellist); + if (params.del_events) { + ret = del_perf_probe_events(params.filter); if (ret < 0) { pr_err_with_code(" Error: Failed to delete events.", ret); return ret; diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 5995d81d2bad..abf5845a2acc 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -2734,40 +2734,39 @@ error: return ret; } -static int del_trace_probe_event(int fd, const char *buf, - struct strlist *namelist) +static int del_trace_probe_events(int fd, struct strfilter *filter, + struct strlist *namelist) { - struct str_node *ent, *n; + struct str_node *ent; + const char *p; int ret = -ENOENT; - if (strpbrk(buf, "*?")) { /* Glob-exp */ - strlist__for_each_safe(ent, n, namelist) - if (strglobmatch(ent->s, buf)) { - ret = __del_trace_probe_event(fd, ent); - if (ret < 0) - break; - strlist__remove(namelist, ent); - } - } else { - ent = strlist__find(namelist, buf); - if (ent) { + if (!namelist) + return -ENOENT; + + strlist__for_each(ent, namelist) { + p = strchr(ent->s, ':'); + if ((p && strfilter__compare(filter, p + 1)) || + strfilter__compare(filter, ent->s)) { ret = __del_trace_probe_event(fd, ent); - if (ret >= 0) - strlist__remove(namelist, ent); + if (ret < 0) + break; } } return ret; } -int del_perf_probe_events(struct strlist *dellist) +int del_perf_probe_events(struct strfilter *filter) { - int ret = -1, ret2, ufd = -1, kfd = -1; - char buf[128]; - const char *group, *event; - char *p, *str; - struct str_node *ent; + int ret, ret2, ufd = -1, kfd = -1; struct strlist *namelist = NULL, *unamelist = NULL; + char *str = strfilter__string(filter); + + if (!str) + return -EINVAL; + + pr_debug("Delete filter: \'%s\'\n", str); /* Get current event names */ kfd = open_kprobe_events(true); @@ -2780,59 +2779,21 @@ int del_perf_probe_events(struct strlist *dellist) if (kfd < 0 && ufd < 0) { print_both_open_warning(kfd, ufd); + ret = kfd; goto error; } - if (namelist == NULL && unamelist == NULL) { - ret = -ENOENT; + ret = del_trace_probe_events(kfd, filter, namelist); + if (ret < 0 && ret != -ENOENT) goto error; - } - - strlist__for_each(ent, dellist) { - str = strdup(ent->s); - if (str == NULL) { - ret = -ENOMEM; - goto error; - } - pr_debug("Parsing: %s\n", str); - p = strchr(str, ':'); - if (p) { - group = str; - *p = '\0'; - event = p + 1; - } else { - group = "*"; - event = str; - } - if (event && *event == '.') - event++; - - 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); - free(str); - - ret = ret2 = -ENOENT; - if (namelist) - ret = del_trace_probe_event(kfd, buf, namelist); - - if ((ret >= 0 || ret == -ENOENT) && unamelist) - ret2 = del_trace_probe_event(ufd, buf, unamelist); - - /* Since we can remove probes which already removed, don't check it */ - if (ret == -ENOENT && ret2 == -ENOENT) - pr_debug("Event \"%s\" does not exist.\n", buf); - else if (ret < 0 || ret2 < 0) { - if (ret >= 0) - ret = ret2; - break; - } + ret2 = del_trace_probe_events(ufd, filter, unamelist); + if (ret2 < 0 && ret2 != -ENOENT) + ret = ret2; + else if (ret == -ENOENT && ret2 == -ENOENT) { + pr_debug("\"%s\" does not hit any event.\n", str); + /* Note that this is silently ignored */ + ret = 0; } error: @@ -2845,6 +2806,7 @@ error: strlist__delete(unamelist); close(ufd); } + free(str); return ret; } diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index ec13362d882f..e10aedc34570 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -126,7 +126,7 @@ extern const char *kernel_get_module_path(const char *module); extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, int max_probe_points, bool force_add); -extern int del_perf_probe_events(struct strlist *dellist); +extern int del_perf_probe_events(struct strfilter *filter); extern int show_perf_probe_events(struct strfilter *filter); extern int show_line_range(struct line_range *lr, const char *module, bool user); -- cgit v1.2.3-59-g8ed1b From ae2cb1ac60758e99cec15e9edd68e0d22bfd310e Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 6 May 2015 21:46:40 +0900 Subject: perf probe: Fix to close probe_events file in error Fix perf-probe to close probe_events file if it failed to get existing probe's name. This also fix the return error code to -ENOMEM. Signed-off-by: Masami Hiramatsu Cc: Ananth N Mavinakayanahalli Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: hemant@linux.vnet.ibm.com Link: http://lkml.kernel.org/r/20150506124640.4961.26062.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index abf5845a2acc..230353f1bea2 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -2384,7 +2384,8 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, namelist = get_probe_trace_event_names(fd, false); if (!namelist) { pr_debug("Failed to get current event list.\n"); - return -EIO; + ret = -ENOMEM; + goto close_out; } /* Get kprobe blacklist if exists */ if (!pev->uprobes) { @@ -2467,6 +2468,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, kprobe_blacklist__delete(&blacklist); strlist__delete(namelist); +close_out: close(fd); return ret; } -- cgit v1.2.3-59-g8ed1b From b8dc3984c1fce87a36d3247c9f722229692bec72 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 6 May 2015 21:46:42 +0900 Subject: perf probe: Fix a typo for the flags of open Fix to pass O_APPEND by using bit-or with other flags, instead of passing it as mode. Signed-off-by: Masami Hiramatsu Cc: Ananth N Mavinakayanahalli Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: hemant@linux.vnet.ibm.com Link: http://lkml.kernel.org/r/20150506124642.4961.97878.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 230353f1bea2..63cb7c589105 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -1969,7 +1969,7 @@ static int open_probe_events(const char *trace_file, bool readwrite) 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); + ret = open(buf, O_RDWR | O_APPEND, 0); else ret = open(buf, O_RDONLY, 0); -- cgit v1.2.3-59-g8ed1b From 573709fdfd668423ba4202c4f1016e3cd7bdd134 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 6 May 2015 21:46:47 +0900 Subject: perf probe: Make --line checks validate C-style function name Fix --line to check valid C-style function name and returns a semantic error if it is not. For example, previously, --line doesn't support lazy pattern but it doesn't recognized as a semantic error. ---- # perf probe -L 'func;return*:0-10' Specified source line is not found. Error: Failed to show lines. ---- With this patch, it is correctly handled as a semantic error. ---- # perf probe -L 'func;return*:0-10' Semantic error :'func;return*' is not a valid function name. ... ---- Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Cc: Ananth N Mavinakayanahalli Cc: David Ahern Cc: Hemant Kumar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20150506124647.4961.99473.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 63cb7c589105..4265f2e3bd14 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -980,6 +980,18 @@ static int parse_line_num(char **ptr, int *val, const char *what) return 0; } +/* Check the name is good for event, group or function */ +static bool is_c_func_name(const char *name) +{ + if (!isalpha(*name) && *name != '_') + return false; + while (*++name != '\0') { + if (!isalpha(*name) && !isdigit(*name) && *name != '_') + return false; + } + return true; +} + /* * Stuff 'lr' according to the line range described by 'arg'. * The line range syntax is described by: @@ -1048,10 +1060,15 @@ int parse_line_range_desc(const char *arg, struct line_range *lr) goto err; } lr->function = name; - } else if (strchr(name, '.')) + } else if (strchr(name, '/') || strchr(name, '.')) lr->file = name; - else + else if (is_c_func_name(name))/* We reuse it for checking funcname */ lr->function = name; + else { /* Invalid name */ + semantic_error("'%s' is not a valid function name.\n", name); + err = -EINVAL; + goto err; + } return 0; err: @@ -1059,18 +1076,6 @@ err: return err; } -/* Check the name is good for event/group */ -static bool check_event_name(const char *name) -{ - if (!isalpha(*name) && *name != '_') - return false; - while (*++name != '\0') { - if (!isalpha(*name) && !isdigit(*name) && *name != '_') - return false; - } - return true; -} - /* Parse probepoint definition. */ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) { @@ -1094,7 +1099,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) semantic_error("Group name is not supported yet.\n"); return -ENOTSUP; } - if (!check_event_name(arg)) { + if (!is_c_func_name(arg)) { semantic_error("%s is bad for event name -it must " "follow C symbol-naming rule.\n", arg); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 5a51fcd1f30c0f93bb54cec7201a3690032470cb Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 6 May 2015 21:46:49 +0900 Subject: perf probe: Skip kernel symbols which is out of .text Skip the kernel symbols which is out of .text, e.g. the functions in .inittext. Those are found in debuginfo/kallsyms, but already freed from memory. e.g. ---- # perf probe vfs_caches_init vfs_caches_init+0 is out of .text, skip it. Probe point 'vfs_caches_init' not found. Error: Failed to add events. ---- Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Cc: Ananth N Mavinakayanahalli Cc: David Ahern Cc: Hemant Kumar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20150506124649.4961.56249.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 4265f2e3bd14..37a3a8b04a69 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -557,8 +557,9 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs, bool uprobe) { struct ref_reloc_sym *reloc_sym; + u64 etext_addr; char *tmp; - int i; + int i, skipped = 0; if (uprobe) return add_exec_to_probe_trace_events(tevs, ntevs, module); @@ -572,19 +573,29 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs, pr_warning("Relocated base symbol is not found!\n"); return -EINVAL; } + /* Get the address of _etext for checking non-probable text symbol */ + etext_addr = kernel_get_symbol_address_by_name("_etext", false); for (i = 0; i < ntevs; i++) { if (tevs[i].point.address && !tevs[i].point.retprobe) { - tmp = strdup(reloc_sym->name); - if (!tmp) - return -ENOMEM; + /* If we found a wrong one, mark it by NULL symbol */ + if (etext_addr < tevs[i].point.address) { + pr_warning("%s+%lu is out of .text, skip it.\n", + tevs[i].point.symbol, tevs[i].point.offset); + tmp = NULL; + skipped++; + } else { + tmp = strdup(reloc_sym->name); + if (!tmp) + return -ENOMEM; + } free(tevs[i].point.symbol); tevs[i].point.symbol = tmp; tevs[i].point.offset = tevs[i].point.address - reloc_sym->unrelocated_addr; } } - return 0; + return skipped; } /* Try to find perf_probe_event with debuginfo */ @@ -630,11 +641,14 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, pr_debug("Found %d probe_trace_events.\n", ntevs); ret = post_process_probe_trace_events(*tevs, ntevs, target, pev->uprobes); - if (ret < 0) { + if (ret < 0 || ret == ntevs) { clear_probe_trace_events(*tevs, ntevs); zfree(tevs); } - return ret < 0 ? ret : ntevs; + if (ret != ntevs) + return ret < 0 ? ret : ntevs; + ntevs = 0; + /* Fall through */ } if (ntevs == 0) { /* No error but failed to find probe point. */ @@ -2403,6 +2417,9 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":"); for (i = 0; i < ntevs; i++) { tev = &tevs[i]; + /* Skip if the symbol is out of .text (marked previously) */ + if (!tev->point.symbol) + continue; /* Ensure that the address is NOT blacklisted */ node = kprobe_blacklist__find_by_address(&blacklist, tev->point.address); -- cgit v1.2.3-59-g8ed1b From 442255215cb9651668cb09350b0d51e111219f17 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 8 May 2015 10:03:28 +0900 Subject: perf probe: Use perf_probe_event.target instead of passing as an argument Use perf_probe_event.target field for the target binary instead of passing it as an argument. Signed-off-by: Masami Hiramatsu Cc: Ananth N Mavinakayanahalli Cc: David Ahern Cc: Hemant Kumar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20150508010328.24812.67887.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-probe.c | 1 - tools/perf/util/probe-event.c | 55 ++++++++++++++++++++----------------------- tools/perf/util/probe-event.h | 4 ++-- 3 files changed, 27 insertions(+), 33 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 9c4cf5e5aad4..233f9413ec8c 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -478,7 +478,6 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) ret = show_available_vars(params.events, params.nevents, params.max_probe_points, - params.target, params.filter, params.show_ext_vars); if (ret < 0) diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 37a3a8b04a69..b8fd48d4eff8 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -344,15 +344,14 @@ out: static int get_alternative_probe_event(struct debuginfo *dinfo, struct perf_probe_event *pev, - struct perf_probe_point *tmp, - const char *target) + struct perf_probe_point *tmp) { int ret; memcpy(tmp, &pev->point, sizeof(*tmp)); memset(&pev->point, 0, sizeof(pev->point)); ret = find_alternative_probe_point(dinfo, tmp, &pev->point, - target, pev->uprobes); + pev->target, pev->uprobes); if (ret < 0) memcpy(&pev->point, tmp, sizeof(*tmp)); @@ -601,15 +600,14 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs, /* Try to find perf_probe_event with debuginfo */ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, struct probe_trace_event **tevs, - int max_tevs, const char *target) + int max_tevs) { bool need_dwarf = perf_probe_event_need_dwarf(pev); struct perf_probe_point tmp; struct debuginfo *dinfo; int ntevs, ret = 0; - dinfo = open_debuginfo(target, !need_dwarf); - + dinfo = open_debuginfo(pev->target, !need_dwarf); if (!dinfo) { if (need_dwarf) return -ENOENT; @@ -622,7 +620,7 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs); if (ntevs == 0) { /* Not found, retry with an alternative */ - ret = get_alternative_probe_event(dinfo, pev, &tmp, target); + ret = get_alternative_probe_event(dinfo, pev, &tmp); if (!ret) { ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs); @@ -640,7 +638,7 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, if (ntevs > 0) { /* Succeeded to find trace events */ pr_debug("Found %d probe_trace_events.\n", ntevs); ret = post_process_probe_trace_events(*tevs, ntevs, - target, pev->uprobes); + pev->target, pev->uprobes); if (ret < 0 || ret == ntevs) { clear_probe_trace_events(*tevs, ntevs); zfree(tevs); @@ -824,7 +822,7 @@ int show_line_range(struct line_range *lr, const char *module, bool user) static int show_available_vars_at(struct debuginfo *dinfo, struct perf_probe_event *pev, int max_vls, struct strfilter *_filter, - bool externs, const char *target) + bool externs) { char *buf; int ret, i, nvars; @@ -841,7 +839,7 @@ static int show_available_vars_at(struct debuginfo *dinfo, ret = debuginfo__find_available_vars_at(dinfo, pev, &vls, max_vls, externs); if (!ret) { /* Not found, retry with an alternative */ - ret = get_alternative_probe_event(dinfo, pev, &tmp, target); + ret = get_alternative_probe_event(dinfo, pev, &tmp); if (!ret) { ret = debuginfo__find_available_vars_at(dinfo, pev, &vls, max_vls, externs); @@ -891,8 +889,7 @@ end: /* Show available variables on given probe point */ int show_available_vars(struct perf_probe_event *pevs, int npevs, - int max_vls, const char *module, - struct strfilter *_filter, bool externs) + int max_vls, struct strfilter *_filter, bool externs) { int i, ret = 0; struct debuginfo *dinfo; @@ -901,7 +898,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, if (ret < 0) return ret; - dinfo = open_debuginfo(module, false); + dinfo = open_debuginfo(pevs->target, false); if (!dinfo) { ret = -ENOENT; goto out; @@ -911,7 +908,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, for (i = 0; i < npevs && ret >= 0; i++) ret = show_available_vars_at(dinfo, &pevs[i], max_vls, _filter, - externs, module); + externs); debuginfo__delete(dinfo); out: @@ -931,8 +928,7 @@ find_perf_probe_point_from_dwarf(struct probe_trace_point *tp __maybe_unused, static int try_to_find_probe_trace_events(struct perf_probe_event *pev, struct probe_trace_event **tevs __maybe_unused, - int max_tevs __maybe_unused, - const char *target __maybe_unused) + int max_tevs __maybe_unused) { if (perf_probe_event_need_dwarf(pev)) { pr_warning("Debuginfo-analysis is not supported.\n"); @@ -952,7 +948,6 @@ int show_line_range(struct line_range *lr __maybe_unused, int show_available_vars(struct perf_probe_event *pevs __maybe_unused, int npevs __maybe_unused, int max_vls __maybe_unused, - const char *module __maybe_unused, struct strfilter *filter __maybe_unused, bool externs __maybe_unused) { @@ -2520,7 +2515,7 @@ void __weak arch__fix_tev_from_maps(struct perf_probe_event *pev __maybe_unused, */ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, struct probe_trace_event **tevs, - int max_tevs, const char *target) + int max_tevs) { struct map *map = NULL; struct ref_reloc_sym *reloc_sym = NULL; @@ -2531,7 +2526,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, int num_matched_functions; int ret, i; - map = get_target_map(target, pev->uprobes); + map = get_target_map(pev->target, pev->uprobes); if (!map) { ret = -EINVAL; goto out; @@ -2544,12 +2539,12 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, num_matched_functions = find_probe_functions(map, pp->function); if (num_matched_functions == 0) { pr_err("Failed to find symbol %s in %s\n", pp->function, - target ? : "kernel"); + pev->target ? : "kernel"); ret = -ENOENT; goto out; } else if (num_matched_functions > max_tevs) { pr_err("Too many functions matched in %s\n", - target ? : "kernel"); + pev->target ? : "kernel"); ret = -E2BIG; goto out; } @@ -2597,8 +2592,9 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, tp->offset = pp->offset; } tp->retprobe = pp->retprobe; - if (target) - tev->point.module = strdup_or_goto(target, nomem_out); + if (pev->target) + tev->point.module = strdup_or_goto(pev->target, + nomem_out); tev->uprobes = pev->uprobes; tev->nargs = pev->nargs; if (tev->nargs) { @@ -2639,13 +2635,13 @@ bool __weak arch__prefers_symtab(void) { return false; } static int convert_to_probe_trace_events(struct perf_probe_event *pev, struct probe_trace_event **tevs, - int max_tevs, const char *target) + int max_tevs) { int ret; if (pev->uprobes && !pev->group) { /* Replace group name if not given */ - ret = convert_exec_to_group(target, &pev->group); + ret = convert_exec_to_group(pev->target, &pev->group); if (ret != 0) { pr_warning("Failed to make a group name.\n"); return ret; @@ -2653,17 +2649,17 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, } if (arch__prefers_symtab() && !perf_probe_event_need_dwarf(pev)) { - ret = find_probe_trace_events_from_map(pev, tevs, max_tevs, target); + ret = find_probe_trace_events_from_map(pev, tevs, max_tevs); if (ret > 0) return ret; /* Found in symbol table */ } /* Convert perf_probe_event with debuginfo */ - ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target); + ret = try_to_find_probe_trace_events(pev, tevs, max_tevs); if (ret != 0) return ret; /* Found in debuginfo or got an error */ - return find_probe_trace_events_from_map(pev, tevs, max_tevs, target); + return find_probe_trace_events_from_map(pev, tevs, max_tevs); } struct __event_package { @@ -2696,8 +2692,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, /* Convert with or without debuginfo */ ret = convert_to_probe_trace_events(pkgs[i].pev, &pkgs[i].tevs, - max_tevs, - pkgs[i].pev->target); + max_tevs); if (ret < 0) goto end; pkgs[i].ntevs = ret; diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index e10aedc34570..e3d9bb1400e8 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -131,8 +131,8 @@ extern int show_perf_probe_events(struct strfilter *filter); extern int show_line_range(struct line_range *lr, const char *module, bool user); extern int show_available_vars(struct perf_probe_event *pevs, int npevs, - int max_probe_points, const char *module, - struct strfilter *filter, bool externs); + int max_probe_points, struct strfilter *filter, + bool externs); extern int show_available_funcs(const char *module, struct strfilter *filter, bool user); bool arch__prefers_symtab(void); -- cgit v1.2.3-59-g8ed1b From ddb2f58f9f8febaf817496a010130f108bb9a431 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 8 May 2015 10:03:31 +0900 Subject: perf probe: Introduce probe_conf global configs Introduce probe_conf global configuration parameters for probe-event and probe-finder, and removes related parameters from APIs. Signed-off-by: Masami Hiramatsu Cc: Ananth N Mavinakayanahalli Cc: David Ahern Cc: Hemant Kumar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20150508010330.24812.21095.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-probe.c | 21 ++++++----------- tools/perf/util/probe-event.c | 53 ++++++++++++++++++------------------------ tools/perf/util/probe-event.h | 13 +++++++---- tools/perf/util/probe-finder.c | 17 +++++++------- tools/perf/util/probe-finder.h | 7 ++---- 5 files changed, 48 insertions(+), 63 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 233f9413ec8c..dbc998f21281 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -50,8 +50,6 @@ static struct { int command; /* Command short_name */ bool list_events; - bool force_add; - bool show_ext_vars; bool uprobes; bool quiet; bool target_used; @@ -59,7 +57,6 @@ static struct { struct perf_probe_event events[MAX_PROBES]; struct line_range line_range; char *target; - int max_probe_points; struct strfilter *filter; } params; @@ -364,7 +361,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) "\t\tARG:\tProbe argument (kprobe-tracer argument format.)\n", #endif opt_add_probe_event), - OPT_BOOLEAN('f', "force", ¶ms.force_add, "forcibly add events" + OPT_BOOLEAN('f', "force", &probe_conf.force_add, "forcibly add events" " with existing name"), #ifdef HAVE_DWARF_SUPPORT OPT_CALLBACK('L', "line", NULL, @@ -373,7 +370,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) OPT_CALLBACK('V', "vars", NULL, "FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT", "Show accessible variables on PROBEDEF", opt_show_vars), - OPT_BOOLEAN('\0', "externs", ¶ms.show_ext_vars, + OPT_BOOLEAN('\0', "externs", &probe_conf.show_ext_vars, "Show external variables too (with --vars only)"), OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, "file", "vmlinux pathname"), @@ -384,7 +381,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) opt_set_target), #endif OPT__DRY_RUN(&probe_event_dry_run), - OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, + OPT_INTEGER('\0', "max-probes", &probe_conf.max_probes, "Set how many probe points can be found for a probe."), OPT_CALLBACK_DEFAULT('F', "funcs", NULL, "[FILTER]", "Show potential probe-able functions.", @@ -440,8 +437,8 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) verbose = -1; } - if (params.max_probe_points == 0) - params.max_probe_points = MAX_PROBES; + if (probe_conf.max_probes == 0) + probe_conf.max_probes = MAX_PROBES; /* * Only consider the user's kernel image path if given. @@ -477,9 +474,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) NULL); ret = show_available_vars(params.events, params.nevents, - params.max_probe_points, - params.filter, - params.show_ext_vars); + params.filter); if (ret < 0) pr_err_with_code(" Error: Failed to show vars.", ret); return ret; @@ -498,9 +493,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) usage_with_options(probe_usage, options); } - ret = add_perf_probe_events(params.events, params.nevents, - params.max_probe_points, - params.force_add); + ret = add_perf_probe_events(params.events, params.nevents); if (ret < 0) { pr_err_with_code(" Error: Failed to add events.", ret); return ret; diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index b8fd48d4eff8..a7deda450875 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -51,6 +51,7 @@ #define PERFPROBE_GROUP "probe" bool probe_event_dry_run; /* Dry run flag */ +struct probe_conf probe_conf; #define semantic_error(msg ...) pr_err("Semantic error :" msg) @@ -599,8 +600,7 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs, /* Try to find perf_probe_event with debuginfo */ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, - struct probe_trace_event **tevs, - int max_tevs) + struct probe_trace_event **tevs) { bool need_dwarf = perf_probe_event_need_dwarf(pev); struct perf_probe_point tmp; @@ -617,13 +617,12 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, pr_debug("Try to find probe point from debuginfo.\n"); /* Searching trace events corresponding to a probe event */ - ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs); + ntevs = debuginfo__find_trace_events(dinfo, pev, tevs); if (ntevs == 0) { /* Not found, retry with an alternative */ ret = get_alternative_probe_event(dinfo, pev, &tmp); if (!ret) { - ntevs = debuginfo__find_trace_events(dinfo, pev, - tevs, max_tevs); + ntevs = debuginfo__find_trace_events(dinfo, pev, tevs); /* * Write back to the original probe_event for * setting appropriate (user given) event name @@ -821,8 +820,7 @@ int show_line_range(struct line_range *lr, const char *module, bool user) static int show_available_vars_at(struct debuginfo *dinfo, struct perf_probe_event *pev, - int max_vls, struct strfilter *_filter, - bool externs) + struct strfilter *_filter) { char *buf; int ret, i, nvars; @@ -836,13 +834,12 @@ static int show_available_vars_at(struct debuginfo *dinfo, return -EINVAL; pr_debug("Searching variables at %s\n", buf); - ret = debuginfo__find_available_vars_at(dinfo, pev, &vls, - max_vls, externs); + ret = debuginfo__find_available_vars_at(dinfo, pev, &vls); if (!ret) { /* Not found, retry with an alternative */ ret = get_alternative_probe_event(dinfo, pev, &tmp); if (!ret) { ret = debuginfo__find_available_vars_at(dinfo, pev, - &vls, max_vls, externs); + &vls); /* Release the old probe_point */ clear_perf_probe_point(&tmp); } @@ -889,7 +886,7 @@ end: /* Show available variables on given probe point */ int show_available_vars(struct perf_probe_event *pevs, int npevs, - int max_vls, struct strfilter *_filter, bool externs) + struct strfilter *_filter) { int i, ret = 0; struct debuginfo *dinfo; @@ -907,8 +904,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, setup_pager(); for (i = 0; i < npevs && ret >= 0; i++) - ret = show_available_vars_at(dinfo, &pevs[i], max_vls, _filter, - externs); + ret = show_available_vars_at(dinfo, &pevs[i], _filter); debuginfo__delete(dinfo); out: @@ -927,8 +923,7 @@ find_perf_probe_point_from_dwarf(struct probe_trace_point *tp __maybe_unused, } static int try_to_find_probe_trace_events(struct perf_probe_event *pev, - struct probe_trace_event **tevs __maybe_unused, - int max_tevs __maybe_unused) + struct probe_trace_event **tevs __maybe_unused) { if (perf_probe_event_need_dwarf(pev)) { pr_warning("Debuginfo-analysis is not supported.\n"); @@ -947,9 +942,8 @@ int show_line_range(struct line_range *lr __maybe_unused, } int show_available_vars(struct perf_probe_event *pevs __maybe_unused, - int npevs __maybe_unused, int max_vls __maybe_unused, - struct strfilter *filter __maybe_unused, - bool externs __maybe_unused) + int npevs __maybe_unused, + struct strfilter *filter __maybe_unused) { pr_warning("Debuginfo-analysis is not supported.\n"); return -ENOSYS; @@ -2514,8 +2508,7 @@ void __weak arch__fix_tev_from_maps(struct perf_probe_event *pev __maybe_unused, * Return an error or the number of found probe_trace_event */ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, - struct probe_trace_event **tevs, - int max_tevs) + struct probe_trace_event **tevs) { struct map *map = NULL; struct ref_reloc_sym *reloc_sym = NULL; @@ -2542,7 +2535,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, pev->target ? : "kernel"); ret = -ENOENT; goto out; - } else if (num_matched_functions > max_tevs) { + } else if (num_matched_functions > probe_conf.max_probes) { pr_err("Too many functions matched in %s\n", pev->target ? : "kernel"); ret = -E2BIG; @@ -2634,8 +2627,7 @@ err_out: bool __weak arch__prefers_symtab(void) { return false; } static int convert_to_probe_trace_events(struct perf_probe_event *pev, - struct probe_trace_event **tevs, - int max_tevs) + struct probe_trace_event **tevs) { int ret; @@ -2649,17 +2641,17 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, } if (arch__prefers_symtab() && !perf_probe_event_need_dwarf(pev)) { - ret = find_probe_trace_events_from_map(pev, tevs, max_tevs); + ret = find_probe_trace_events_from_map(pev, tevs); if (ret > 0) return ret; /* Found in symbol table */ } /* Convert perf_probe_event with debuginfo */ - ret = try_to_find_probe_trace_events(pev, tevs, max_tevs); + ret = try_to_find_probe_trace_events(pev, tevs); if (ret != 0) return ret; /* Found in debuginfo or got an error */ - return find_probe_trace_events_from_map(pev, tevs, max_tevs); + return find_probe_trace_events_from_map(pev, tevs); } struct __event_package { @@ -2668,8 +2660,7 @@ struct __event_package { int ntevs; }; -int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, - int max_tevs, bool force_add) +int add_perf_probe_events(struct perf_probe_event *pevs, int npevs) { int i, j, ret; struct __event_package *pkgs; @@ -2691,8 +2682,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, pkgs[i].pev = &pevs[i]; /* Convert with or without debuginfo */ ret = convert_to_probe_trace_events(pkgs[i].pev, - &pkgs[i].tevs, - max_tevs); + &pkgs[i].tevs); if (ret < 0) goto end; pkgs[i].ntevs = ret; @@ -2701,7 +2691,8 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, /* Loop 2: add all events */ for (i = 0; i < npevs; i++) { ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, - pkgs[i].ntevs, force_add); + pkgs[i].ntevs, + probe_conf.force_add); if (ret < 0) break; } diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index e3d9bb1400e8..db9a9cb2d2af 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -6,6 +6,13 @@ #include "strlist.h" #include "strfilter.h" +/* Probe related configurations */ +struct probe_conf { + bool show_ext_vars; + bool force_add; + int max_probes; +}; +extern struct probe_conf probe_conf; extern bool probe_event_dry_run; /* kprobe-tracer and uprobe-tracer tracing point */ @@ -124,15 +131,13 @@ extern int line_range__init(struct line_range *lr); /* Internal use: Return kernel/module path */ extern const char *kernel_get_module_path(const char *module); -extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, - int max_probe_points, bool force_add); +extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs); extern int del_perf_probe_events(struct strfilter *filter); extern int show_perf_probe_events(struct strfilter *filter); extern int show_line_range(struct line_range *lr, const char *module, bool user); extern int show_available_vars(struct perf_probe_event *pevs, int npevs, - int max_probe_points, struct strfilter *filter, - bool externs); + struct strfilter *filter); extern int show_available_funcs(const char *module, struct strfilter *filter, bool user); bool arch__prefers_symtab(void); diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 63d33893d853..f4f5eed00c7d 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -1214,15 +1214,15 @@ end: /* Find probe_trace_events specified by perf_probe_event from debuginfo */ int debuginfo__find_trace_events(struct debuginfo *dbg, struct perf_probe_event *pev, - struct probe_trace_event **tevs, int max_tevs) + struct probe_trace_event **tevs) { struct trace_event_finder tf = { .pf = {.pev = pev, .callback = add_probe_trace_event}, - .mod = dbg->mod, .max_tevs = max_tevs}; + .max_tevs = probe_conf.max_probes, .mod = dbg->mod}; int ret; /* Allocate result tevs array */ - *tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs); + *tevs = zalloc(sizeof(struct probe_trace_event) * tf.max_tevs); if (*tevs == NULL) return -ENOMEM; @@ -1303,9 +1303,9 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) die_find_child(sc_die, collect_variables_cb, (void *)af, &die_mem); /* Find external variables */ - if (!af->externs) + if (!probe_conf.show_ext_vars) goto out; - /* Don't need to search child DIE for externs. */ + /* Don't need to search child DIE for external vars. */ af->child = false; die_find_child(&pf->cu_die, collect_variables_cb, (void *)af, &die_mem); @@ -1325,17 +1325,16 @@ out: */ int debuginfo__find_available_vars_at(struct debuginfo *dbg, struct perf_probe_event *pev, - struct variable_list **vls, - int max_vls, bool externs) + struct variable_list **vls) { struct available_var_finder af = { .pf = {.pev = pev, .callback = add_available_vars}, .mod = dbg->mod, - .max_vls = max_vls, .externs = externs}; + .max_vls = probe_conf.max_probes}; int ret; /* Allocate result vls array */ - *vls = zalloc(sizeof(struct variable_list) * max_vls); + *vls = zalloc(sizeof(struct variable_list) * af.max_vls); if (*vls == NULL) return -ENOMEM; diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index f53553d38845..bed82716e1b4 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -40,8 +40,7 @@ extern void debuginfo__delete(struct debuginfo *dbg); /* Find probe_trace_events specified by perf_probe_event from debuginfo */ extern int debuginfo__find_trace_events(struct debuginfo *dbg, struct perf_probe_event *pev, - struct probe_trace_event **tevs, - int max_tevs); + struct probe_trace_event **tevs); /* Find a perf_probe_point from debuginfo */ extern int debuginfo__find_probe_point(struct debuginfo *dbg, @@ -55,8 +54,7 @@ extern int debuginfo__find_line_range(struct debuginfo *dbg, /* Find available variables */ extern int debuginfo__find_available_vars_at(struct debuginfo *dbg, struct perf_probe_event *pev, - struct variable_list **vls, - int max_points, bool externs); + struct variable_list **vls); /* Find a src file from a DWARF tag path */ int get_real_path(const char *raw_path, const char *comp_dir, @@ -99,7 +97,6 @@ struct available_var_finder { struct variable_list *vls; /* Found variable lists */ int nvls; /* Number of variable lists */ int max_vls; /* Max no. of variable lists */ - bool externs; /* Find external vars too */ bool child; /* Search child scopes */ }; -- cgit v1.2.3-59-g8ed1b From 4c859351226c920b227fec040a3b447f0d482af3 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 8 May 2015 10:03:35 +0900 Subject: perf probe: Support glob wildcards for function name Support glob wildcards for function name when adding new probes. This will allow us to build caches of function-entry level information with $params. e.g. ---- # perf probe --no-inlines --add 'kmalloc* $params' Added new events: probe:kmalloc_slab (on kmalloc* with $params) probe:kmalloc_large_node (on kmalloc* with $params) probe:kmalloc_order_trace (on kmalloc* with $params) You can now use it in all perf tools, such as: perf record -e probe:kmalloc_order_trace -aR sleep 1 # perf probe --list probe:kmalloc_large_node (on kmalloc_large_node@mm/slub.c with size flags node) probe:kmalloc_order_trace (on kmalloc_order_trace@mm/slub.c with size flags order) probe:kmalloc_slab (on kmalloc_slab@mm/slab_common.c with size flags) ---- Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Cc: Ananth N Mavinakayanahalli Cc: David Ahern Cc: Hemant Kumar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20150508010335.24812.19972.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dwarf-aux.c | 16 ++++++++++++++++ tools/perf/util/dwarf-aux.h | 3 +++ tools/perf/util/probe-event.c | 19 ++++++++++++++----- tools/perf/util/probe-event.h | 1 + tools/perf/util/probe-finder.c | 27 +++++++++++++++++++++------ tools/perf/util/util.h | 4 ++++ 6 files changed, 59 insertions(+), 11 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index c34e024020c7..16d46e26edac 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -139,10 +139,26 @@ int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, bool die_compare_name(Dwarf_Die *dw_die, const char *tname) { const char *name; + name = dwarf_diename(dw_die); return name ? (strcmp(tname, name) == 0) : false; } +/** + * die_match_name - Match diename and glob + * @dw_die: a DIE + * @glob: a string of target glob pattern + * + * Glob matching the name of @dw_die and @glob. Return false if matching fail. + */ +bool die_match_name(Dwarf_Die *dw_die, const char *glob) +{ + const char *name; + + name = dwarf_diename(dw_die); + return name ? strglobmatch(name, glob) : false; +} + /** * die_get_call_lineno - Get callsite line number of inline-function instance * @in_die: a DIE of an inlined function instance diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index af7dbcd5f929..50a3cdc55fd7 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h @@ -47,6 +47,9 @@ extern bool die_is_func_instance(Dwarf_Die *dw_die); /* Compare diename and tname */ extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname); +/* Matching diename with glob pattern */ +extern bool die_match_name(Dwarf_Die *dw_die, const char *glob); + /* Get callsite line number of inline-function instance */ extern int die_get_call_lineno(Dwarf_Die *in_die); diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index a7deda450875..a2d8cefc597c 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -589,7 +589,11 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs, if (!tmp) return -ENOMEM; } - free(tevs[i].point.symbol); + /* If we have no realname, use symbol for it */ + if (!tevs[i].point.realname) + tevs[i].point.realname = tevs[i].point.symbol; + else + free(tevs[i].point.symbol); tevs[i].point.symbol = tmp; tevs[i].point.offset = tevs[i].point.address - reloc_sym->unrelocated_addr; @@ -1900,6 +1904,7 @@ static void clear_probe_trace_event(struct probe_trace_event *tev) free(tev->event); free(tev->group); free(tev->point.symbol); + free(tev->point.realname); free(tev->point.module); for (i = 0; i < tev->nargs; i++) { free(tev->args[i].name); @@ -2377,6 +2382,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, struct strlist *namelist; LIST_HEAD(blacklist); struct kprobe_blacklist_node *node; + bool safename; if (pev->uprobes) fd = open_uprobe_events(true); @@ -2402,6 +2408,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, pr_debug("No kprobe blacklist support, ignored\n"); } + safename = (pev->point.function && !strisglob(pev->point.function)); ret = 0; pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":"); for (i = 0; i < ntevs; i++) { @@ -2420,10 +2427,10 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, if (pev->event) event = pev->event; else - if (pev->point.function) + if (safename) event = pev->point.function; else - event = tev->point.symbol; + event = tev->point.realname; if (pev->group) group = pev->group; else @@ -2488,9 +2495,11 @@ static int find_probe_functions(struct map *map, char *name) { int found = 0; struct symbol *sym; + struct rb_node *tmp; - map__for_each_symbol_by_name(map, name, sym) { - found++; + map__for_each_symbol(map, sym, tmp) { + if (strglobmatch(sym->name, name)) + found++; } return found; diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 633aba77e0cb..1e2faa3559d2 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -18,6 +18,7 @@ extern bool probe_event_dry_run; /* kprobe-tracer and uprobe-tracer tracing point */ struct probe_trace_point { + char *realname; /* function real name (if needed) */ char *symbol; /* Base symbol */ char *module; /* Module name */ unsigned long offset; /* Offset from symbol */ diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 1713421112f8..d5f60c055554 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -717,7 +717,7 @@ static int find_best_scope_cb(Dwarf_Die *fn_die, void *data) } /* If the function name is given, that's what user expects */ if (fsp->function) { - if (die_compare_name(fn_die, fsp->function)) { + if (die_match_name(fn_die, fsp->function)) { memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); fsp->found = true; return 1; @@ -920,13 +920,14 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) /* Check tag and diename */ if (!die_is_func_def(sp_die) || - !die_compare_name(sp_die, pp->function)) + !die_match_name(sp_die, pp->function)) return DWARF_CB_OK; /* Check declared file */ if (pp->file && strtailcmp(pp->file, dwarf_decl_file(sp_die))) return DWARF_CB_OK; + pr_debug("Matched function: %s\n", dwarf_diename(sp_die)); pf->fname = dwarf_decl_file(sp_die); if (pp->line) { /* Function relative line */ dwarf_decl_line(sp_die, &pf->lno); @@ -943,10 +944,20 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) /* TODO: Check the address in this function */ param->retval = call_probe_finder(sp_die, pf); } - } else if (!probe_conf.no_inlines) + } else if (!probe_conf.no_inlines) { /* Inlined function: search instances */ param->retval = die_walk_instances(sp_die, probe_point_inline_cb, (void *)pf); + /* This could be a non-existed inline definition */ + if (param->retval == -ENOENT && strisglob(pp->function)) + param->retval = 0; + } + + /* We need to find other candidates */ + if (strisglob(pp->function) && param->retval >= 0) { + param->retval = 0; /* We have to clear the result */ + return DWARF_CB_OK; + } return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */ } @@ -975,7 +986,7 @@ static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data) if (dwarf_tag(param->sp_die) != DW_TAG_subprogram) return DWARF_CB_OK; - if (die_compare_name(param->sp_die, param->function)) { + if (die_match_name(param->sp_die, param->function)) { if (!dwarf_offdie(dbg, gl->cu_offset, param->cu_die)) return DWARF_CB_OK; @@ -1028,7 +1039,7 @@ static int debuginfo__find_probes(struct debuginfo *dbg, return -ENOMEM; /* Fastpath: lookup by function name from .debug_pubnames section */ - if (pp->function) { + if (pp->function && !strisglob(pp->function)) { struct pubname_callback_param pubname_param = { .function = pp->function, .file = pp->file, @@ -1177,6 +1188,10 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) if (ret < 0) return ret; + tev->point.realname = strdup(dwarf_diename(sc_die)); + if (!tev->point.realname) + return -ENOMEM; + pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, tev->point.offset); @@ -1535,7 +1550,7 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) return DWARF_CB_OK; if (die_is_func_def(sp_die) && - die_compare_name(sp_die, lr->function)) { + die_match_name(sp_die, lr->function)) { lf->fname = dwarf_decl_file(sp_die); dwarf_decl_line(sp_die, &lr->offset); pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 1ff23e04ad27..3601ffd3d8b4 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -257,6 +257,10 @@ char **argv_split(const char *str, int *argcp); void argv_free(char **argv); bool strglobmatch(const char *str, const char *pat); bool strlazymatch(const char *str, const char *pat); +static inline bool strisglob(const char *str) +{ + return strpbrk(str, "*?[") != NULL; +} int strtailcmp(const char *s1, const char *s2); char *strxfrchar(char *s, char from, char to); unsigned long convert_unit(unsigned long value, char *unit); -- cgit v1.2.3-59-g8ed1b From 75e4a2a6af15e956993913314ced2582b350a647 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Fri, 15 May 2015 12:14:44 +0000 Subject: perf probe: Load map before glob matching Commit 4c859351226c920b227fec040a3b447f0d482af3 ("perf probe: Support glob wildcards for function name") introduces a problem: # /root/perf probe kmem_cache_free Failed to find symbol kmem_cache_free in kernel Error: Failed to add events. The reason is the replacement of map__for_each_symbol_by_name() (by map__for_each_symbol()). Although their names are similar, map__for_each_symbol doesn't call map__load() and dso__sort_by_name() before searching. The missing of map__load() causes this problem because it search symbol before load dso map. This patch ensures map__load() is called before using map__for_each_symbol(). After this patch: # /root/perf probe kmem_cache_free Added new event: probe:kmem_cache_free (on kmem_cache_free%return) You can now use it in all perf tools, such as: perf record -e probe:kmem_cache_free -aR sleep 1 Signed-off-by: Wang Nan Acked-by: Masami Hiramatsu Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1431692084-46287-1-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 2399dc4f6089..1faa1e67398b 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -2499,6 +2499,9 @@ static int find_probe_functions(struct map *map, char *name) struct symbol *sym; struct rb_node *tmp; + if (map__load(map, NULL) < 0) + return 0; + map__for_each_symbol(map, sym, tmp) { if (strglobmatch(sym->name, name)) found++; -- cgit v1.2.3-59-g8ed1b From 614c6b570d5157c2cf835d334bc89af071fc2e44 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 25 May 2015 16:21:53 -0300 Subject: perf tools: Leave DSO destruction to the map destruction As the way DSOs are created are normally via dsos__findnew, so that we don't have to load the same dso multiple times for multiple maps (think about /lib64/libc.so.6), so they may be shared and dso__delete() should be left to be done as part of the map destruction process. This will all be properly solved by reference counting struct dso, which will be done soon. Cc: Adrian Hunter Cc: Borislav Petkov Cc: David Ahern Cc: Don Zickus Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-gbrohe1nvkjxw3u5a1bgj3yh@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 1faa1e67398b..db6021834e8f 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -195,7 +195,6 @@ static void put_target_map(struct map *map, bool user) { if (map && user) { /* Only the user map needs to be released */ - dso__delete(map->dso); map__delete(map); } } @@ -1791,7 +1790,6 @@ static int find_perf_probe_point_from_map(struct probe_trace_point *tp, out: if (map && !is_kprobe) { - dso__delete(map->dso); map__delete(map); } @@ -2884,7 +2882,6 @@ int show_available_funcs(const char *target, struct strfilter *_filter, dso__fprintf_symbols_by_name(map->dso, map->type, stdout); end: if (user) { - dso__delete(map->dso); map__delete(map); } exit_symbol_maps(); -- cgit v1.2.3-59-g8ed1b From 4bb7123dcfa7aa1d963ad4a8f01b88d54a2bb873 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 22 May 2015 11:52:22 -0300 Subject: perf tools: Use maps__first()/map__next() In a few more remaining places, for consistency. Cc: Adrian Hunter Cc: Borislav Petkov Cc: David Ahern Cc: Don Zickus Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-c2n7slwtto29wndfttdrhfrx@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/vmlinux-kallsyms.c | 34 +++++++++++++++++----------------- tools/perf/util/event.c | 7 +++---- tools/perf/util/map.c | 7 ++++--- tools/perf/util/probe-event.c | 6 +++--- tools/perf/util/symbol.c | 23 ++++++++++------------- 5 files changed, 37 insertions(+), 40 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c index 3d9088003a5b..94ac6924df65 100644 --- a/tools/perf/tests/vmlinux-kallsyms.c +++ b/tools/perf/tests/vmlinux-kallsyms.c @@ -23,9 +23,10 @@ int test__vmlinux_matches_kallsyms(void) int err = -1; struct rb_node *nd; struct symbol *sym; - struct map *kallsyms_map, *vmlinux_map; + struct map *kallsyms_map, *vmlinux_map, *map; struct machine kallsyms, vmlinux; enum map_type type = MAP__FUNCTION; + struct rb_root *maps = &vmlinux.kmaps.maps[type]; u64 mem_start, mem_end; /* @@ -184,8 +185,8 @@ detour: pr_info("Maps only in vmlinux:\n"); - for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) { - struct map *pos = rb_entry(nd, struct map, rb_node), *pair; + for (map = maps__first(maps); map; map = map__next(map)) { + struct map * /* * If it is the kernel, kallsyms is always "[kernel.kallsyms]", while * the kernel will have the path for the vmlinux file being used, @@ -193,22 +194,22 @@ detour: * both cases. */ pair = map_groups__find_by_name(&kallsyms.kmaps, type, - (pos->dso->kernel ? - pos->dso->short_name : - pos->dso->name)); + (map->dso->kernel ? + map->dso->short_name : + map->dso->name)); if (pair) pair->priv = 1; else - map__fprintf(pos, stderr); + map__fprintf(map, stderr); } pr_info("Maps in vmlinux with a different name in kallsyms:\n"); - for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) { - struct map *pos = rb_entry(nd, struct map, rb_node), *pair; + for (map = maps__first(maps); map; map = map__next(map)) { + struct map *pair; - mem_start = vmlinux_map->unmap_ip(vmlinux_map, pos->start); - mem_end = vmlinux_map->unmap_ip(vmlinux_map, pos->end); + mem_start = vmlinux_map->unmap_ip(vmlinux_map, map->start); + mem_end = vmlinux_map->unmap_ip(vmlinux_map, map->end); pair = map_groups__find(&kallsyms.kmaps, type, mem_start); if (pair == NULL || pair->priv) @@ -217,7 +218,7 @@ detour: if (pair->start == mem_start) { pair->priv = 1; pr_info(" %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as", - pos->start, pos->end, pos->pgoff, pos->dso->name); + map->start, map->end, map->pgoff, map->dso->name); if (mem_end != pair->end) pr_info(":\n*%" PRIx64 "-%" PRIx64 " %" PRIx64, pair->start, pair->end, pair->pgoff); @@ -228,12 +229,11 @@ detour: pr_info("Maps only in kallsyms:\n"); - for (nd = rb_first(&kallsyms.kmaps.maps[type]); - nd; nd = rb_next(nd)) { - struct map *pos = rb_entry(nd, struct map, rb_node); + maps = &kallsyms.kmaps.maps[type]; - if (!pos->priv) - map__fprintf(pos, stderr); + for (map = maps__first(maps); map; map = map__next(map)) { + if (!map->priv) + map__fprintf(map, stderr); } out: machine__exit(&kallsyms); diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index a513a51f7330..9d3bba175423 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -329,8 +329,9 @@ int perf_event__synthesize_modules(struct perf_tool *tool, struct machine *machine) { int rc = 0; - struct rb_node *nd; + struct map *pos; struct map_groups *kmaps = &machine->kmaps; + struct rb_root *maps = &kmaps->maps[MAP__FUNCTION]; union perf_event *event = zalloc((sizeof(event->mmap) + machine->id_hdr_size)); if (event == NULL) { @@ -350,10 +351,8 @@ int perf_event__synthesize_modules(struct perf_tool *tool, else event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL; - for (nd = rb_first(&kmaps->maps[MAP__FUNCTION]); - nd; nd = rb_next(nd)) { + for (pos = maps__first(maps); pos; pos = map__next(pos)) { size_t size; - struct map *pos = rb_entry(nd, struct map, rb_node); if (pos->dso->kernel) continue; diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index c1bfd0a12a94..898ab92a98dd 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -688,9 +688,10 @@ move_map: int map_groups__clone(struct map_groups *mg, struct map_groups *parent, enum map_type type) { - struct rb_node *nd; - for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) { - struct map *map = rb_entry(nd, struct map, rb_node); + struct map *map; + struct rb_root *maps = &parent->maps[type]; + + for (map = maps__first(maps); map; map = map__next(map)) { struct map *new = map__clone(map); if (new == NULL) return -ENOMEM; diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index db6021834e8f..092256516262 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -162,8 +162,9 @@ static u64 kernel_get_symbol_address_by_name(const char *name, bool reloc) static struct map *kernel_get_module_map(const char *module) { - struct rb_node *nd; struct map_groups *grp = &host_machine->kmaps; + struct rb_root *maps = &grp->maps[MAP__FUNCTION]; + struct map *pos; /* A file path -- this is an offline module */ if (module && strchr(module, '/')) @@ -172,8 +173,7 @@ static struct map *kernel_get_module_map(const char *module) if (!module) module = "kernel"; - for (nd = rb_first(&grp->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { - struct map *pos = rb_entry(nd, struct map, rb_node); + for (pos = maps__first(maps); pos; pos = map__next(pos)) { if (strncmp(pos->dso->short_name + 1, module, pos->dso->short_name_len - 2) == 0) { return pos; diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 82a31fd0fcf5..00b6b17e74a7 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -202,18 +202,16 @@ void symbols__fixup_end(struct rb_root *symbols) void __map_groups__fixup_end(struct map_groups *mg, enum map_type type) { - struct map *prev, *curr; - struct rb_node *nd, *prevnd = rb_first(&mg->maps[type]); + struct rb_root *maps = &mg->maps[type]; + struct map *next, *curr; - if (prevnd == NULL) + curr = maps__first(maps); + if (curr == NULL) return; - curr = rb_entry(prevnd, struct map, rb_node); - - for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { - prev = curr; - curr = rb_entry(nd, struct map, rb_node); - prev->end = curr->start; + for (next = map__next(curr); next; next = map__next(curr)) { + curr->end = next->start; + curr = next; } /* @@ -1522,11 +1520,10 @@ out: struct map *map_groups__find_by_name(struct map_groups *mg, enum map_type type, const char *name) { - struct rb_node *nd; - - for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) { - struct map *map = rb_entry(nd, struct map, rb_node); + struct rb_root *maps = &mg->maps[type]; + struct map *map; + for (map = maps__first(maps); map; map = map__next(map)) { if (map->dso && strcmp(map->dso->short_name, name) == 0) return map; } -- cgit v1.2.3-59-g8ed1b From 419e87382873b11b17cb31e2f21859570a32e0d1 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 27 May 2015 17:37:18 +0900 Subject: perf probe: Show the error reason comes from invalid DSO Show the reason of error when dso__load* fails. This shows when user gives wrong kernel image or wrong path. Without this, perf probe shows an obscure message: ---- $ perf probe -k ~/kbin/linux-3.x86_64/vmlinux -L vfs_read Failed to find path of kernel module. Error: Failed to show lines. ---- With this, perf shows appropriate error message: ---- $ perf probe -k ~/kbin/linux-3.x86_64/vmlinux -L vfs_read Failed to find the path for kernel: Mismatching build id Error: Failed to show lines. ---- And: ---- $ perf probe -k /non-exist/kernel/vmlinux -L vfs_read Failed to find the path for kernel: No such file or directory Error: Failed to show lines. ---- Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Richard Weinberger Link: http://lkml.kernel.org/r/20150527083718.23880.84100.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 47 ++++++++++++++++++++++--------------------- tools/perf/util/probe-event.h | 3 --- 2 files changed, 24 insertions(+), 26 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 092256516262..f5be411bc69c 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -200,11 +200,12 @@ static void put_target_map(struct map *map, bool user) } -static struct dso *kernel_get_module_dso(const char *module) +static int kernel_get_module_dso(const char *module, struct dso **pdso) { struct dso *dso; struct map *map; const char *vmlinux_name; + int ret = 0; if (module) { list_for_each_entry(dso, &host_machine->kernel_dsos.head, @@ -214,30 +215,21 @@ static struct dso *kernel_get_module_dso(const char *module) goto found; } pr_debug("Failed to find module %s.\n", module); - return NULL; + return -ENOENT; } map = host_machine->vmlinux_maps[MAP__FUNCTION]; dso = map->dso; vmlinux_name = symbol_conf.vmlinux_name; - if (vmlinux_name) { - if (dso__load_vmlinux(dso, map, vmlinux_name, false, NULL) <= 0) - return NULL; - } else { - if (dso__load_vmlinux_path(dso, map, NULL) <= 0) { - pr_debug("Failed to load kernel map.\n"); - return NULL; - } - } + dso->load_errno = 0; + if (vmlinux_name) + ret = dso__load_vmlinux(dso, map, vmlinux_name, false, NULL); + else + ret = dso__load_vmlinux_path(dso, map, NULL); found: - return dso; -} - -const char *kernel_get_module_path(const char *module) -{ - struct dso *dso = kernel_get_module_dso(module); - return (dso) ? dso->long_name : NULL; + *pdso = dso; + return ret; } static int convert_exec_to_group(const char *exec, char **result) @@ -389,16 +381,25 @@ static int get_alternative_line_range(struct debuginfo *dinfo, static struct debuginfo *open_debuginfo(const char *module, bool silent) { const char *path = module; - struct debuginfo *ret; + char reason[STRERR_BUFSIZE]; + struct debuginfo *ret = NULL; + struct dso *dso = NULL; + int err; if (!module || !strchr(module, '/')) { - path = kernel_get_module_path(module); - if (!path) { + err = kernel_get_module_dso(module, &dso); + if (err < 0) { + if (!dso || dso->load_errno == 0) { + if (!strerror_r(-err, reason, STRERR_BUFSIZE)) + strcpy(reason, "(unknown)"); + } else + dso__strerror_load(dso, reason, STRERR_BUFSIZE); if (!silent) - pr_err("Failed to find path of %s module.\n", - module ?: "kernel"); + pr_err("Failed to find the path for %s: %s\n", + module ?: "kernel", reason); return NULL; } + path = dso->long_name; } ret = debuginfo__new(path); if (!ret && !silent) { diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 537eb329c2cf..31db6ee7db54 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -131,9 +131,6 @@ extern void line_range__clear(struct line_range *lr); /* Initialize line range */ extern int line_range__init(struct line_range *lr); -/* Internal use: Return kernel/module path */ -extern const char *kernel_get_module_path(const char *module); - extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs); extern int del_perf_probe_events(struct strfilter *filter); extern int show_perf_probe_events(struct strfilter *filter); -- cgit v1.2.3-59-g8ed1b From dddc7ee32fa13efc66afa71ebd83bce545c8392a Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 27 May 2015 17:37:25 +0900 Subject: perf probe: Fix an error when deleting probes successfully Fix a bug in del_perf_probe_events() which returns an error (-ENOENT) even if the probes are successfully deleted. This happens only if the probes are on user-apps and not on kernel, simply because it doesn't clear the previous error. So, without this fix, we get an error even though events are being successfully removed. ------ # ./perf probe -x ./perf del_perf_probe_events Added new event: probe_perf:del_perf_probe_events (on del_perf_probe_events in ... You can now use it in all perf tools, such as: perf record -e probe_perf:del_perf_probe_events -aR sleep 1 # ./perf probe -d \*:\* Removed event: probe_perf:del_perf_probe_events Error: Failed to delete events. ------ This fixes the above error. ------ # ./perf probe -d \*:\* Removed event: probe_perf:del_perf_probe_events ------ Reported-by: Arnaldo Carvalho de Melo Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Richard Weinberger Link: http://lkml.kernel.org/r/20150527083725.23880.45209.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index f5be411bc69c..97da98481d89 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -2811,13 +2811,14 @@ int del_perf_probe_events(struct strfilter *filter) goto error; ret2 = del_trace_probe_events(ufd, filter, unamelist); - if (ret2 < 0 && ret2 != -ENOENT) + if (ret2 < 0 && ret2 != -ENOENT) { ret = ret2; - else if (ret == -ENOENT && ret2 == -ENOENT) { + goto error; + } + if (ret == -ENOENT && ret2 == -ENOENT) pr_debug("\"%s\" does not hit any event.\n", str); /* Note that this is silently ignored */ - ret = 0; - } + ret = 0; error: if (kfd >= 0) { -- cgit v1.2.3-59-g8ed1b From 1eee78aea9252fabcd333805d5d9fa42a1bf9427 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 22 May 2015 12:58:53 -0300 Subject: perf tools: Introduce struct maps That for now has the maps rbtree and the list for the dead maps, that may be still referenced from some hist_entry, etc. This paves the way for protecting the rbtree with a lock, then refcount the maps and finally remove the removed_maps list, as it'll not ne anymore needed. Cc: Adrian Hunter Cc: Borislav Petkov Cc: David Ahern Cc: Don Zickus Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-fl0fa6142pj8khj97fow3uw0@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/vmlinux-kallsyms.c | 2 +- tools/perf/util/event.c | 2 +- tools/perf/util/map.c | 64 +++++++++++++++++++++---------------- tools/perf/util/map.h | 16 ++++++---- tools/perf/util/probe-event.c | 2 +- tools/perf/util/symbol.c | 4 +-- 6 files changed, 52 insertions(+), 38 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c index 94ac6924df65..b34c5fc829ae 100644 --- a/tools/perf/tests/vmlinux-kallsyms.c +++ b/tools/perf/tests/vmlinux-kallsyms.c @@ -26,7 +26,7 @@ int test__vmlinux_matches_kallsyms(void) struct map *kallsyms_map, *vmlinux_map, *map; struct machine kallsyms, vmlinux; enum map_type type = MAP__FUNCTION; - struct rb_root *maps = &vmlinux.kmaps.maps[type]; + struct maps *maps = &vmlinux.kmaps.maps[type]; u64 mem_start, mem_end; /* diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 9d3bba175423..c1925968a8af 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -331,7 +331,7 @@ int perf_event__synthesize_modules(struct perf_tool *tool, int rc = 0; struct map *pos; struct map_groups *kmaps = &machine->kmaps; - struct rb_root *maps = &kmaps->maps[MAP__FUNCTION]; + struct maps *maps = &kmaps->maps[MAP__FUNCTION]; union perf_event *event = zalloc((sizeof(event->mmap) + machine->id_hdr_size)); if (event == NULL) { diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 898ab92a98dd..adf012c4d650 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -418,48 +418,58 @@ u64 map__objdump_2mem(struct map *map, u64 ip) return ip + map->reloc; } +static void maps__init(struct maps *maps) +{ + maps->entries = RB_ROOT; + INIT_LIST_HEAD(&maps->removed_maps); +} + void map_groups__init(struct map_groups *mg, struct machine *machine) { int i; for (i = 0; i < MAP__NR_TYPES; ++i) { - mg->maps[i] = RB_ROOT; - INIT_LIST_HEAD(&mg->removed_maps[i]); + maps__init(&mg->maps[i]); } mg->machine = machine; atomic_set(&mg->refcnt, 1); } -static void maps__delete(struct rb_root *maps) +static void maps__purge(struct maps *maps) { - struct rb_node *next = rb_first(maps); + struct rb_root *root = &maps->entries; + struct rb_node *next = rb_first(root); while (next) { struct map *pos = rb_entry(next, struct map, rb_node); next = rb_next(&pos->rb_node); - rb_erase(&pos->rb_node, maps); + rb_erase(&pos->rb_node, root); map__delete(pos); } } -static void maps__delete_removed(struct list_head *maps) +static void maps__purge_removed_maps(struct maps *maps) { struct map *pos, *n; - list_for_each_entry_safe(pos, n, maps, node) { + list_for_each_entry_safe(pos, n, &maps->removed_maps, node) { list_del(&pos->node); map__delete(pos); } } +static void maps__exit(struct maps *maps) +{ + maps__purge(maps); + maps__purge_removed_maps(maps); +} + void map_groups__exit(struct map_groups *mg) { int i; - for (i = 0; i < MAP__NR_TYPES; ++i) { - maps__delete(&mg->maps[i]); - maps__delete_removed(&mg->removed_maps[i]); - } + for (i = 0; i < MAP__NR_TYPES; ++i) + maps__exit(&mg->maps[i]); } bool map_groups__empty(struct map_groups *mg) @@ -469,7 +479,7 @@ bool map_groups__empty(struct map_groups *mg) for (i = 0; i < MAP__NR_TYPES; ++i) { if (maps__first(&mg->maps[i])) return false; - if (!list_empty(&mg->removed_maps[i])) + if (!list_empty(&mg->maps[i].removed_maps)) return false; } @@ -523,7 +533,7 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, { struct rb_node *nd; - for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) { + for (nd = rb_first(&mg->maps[type].entries); nd; nd = rb_next(nd)) { struct map *pos = rb_entry(nd, struct map, rb_node); struct symbol *sym = map__find_symbol_by_name(pos, name, filter); @@ -560,7 +570,7 @@ size_t __map_groups__fprintf_maps(struct map_groups *mg, enum map_type type, size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); struct rb_node *nd; - for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) { + for (nd = rb_first(&mg->maps[type].entries); nd; nd = rb_next(nd)) { struct map *pos = rb_entry(nd, struct map, rb_node); printed += fprintf(fp, "Map:"); printed += map__fprintf(pos, fp); @@ -587,7 +597,7 @@ static size_t __map_groups__fprintf_removed_maps(struct map_groups *mg, struct map *pos; size_t printed = 0; - list_for_each_entry(pos, &mg->removed_maps[type], node) { + list_for_each_entry(pos, &mg->maps[type].removed_maps, node) { printed += fprintf(fp, "Map:"); printed += map__fprintf(pos, fp); if (verbose > 1) { @@ -617,7 +627,7 @@ size_t map_groups__fprintf(struct map_groups *mg, FILE *fp) int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, FILE *fp) { - struct rb_root *root = &mg->maps[map->type]; + struct rb_root *root = &mg->maps[map->type].entries; struct rb_node *next = rb_first(root); int err = 0; @@ -671,7 +681,7 @@ move_map: * If we have references, just move them to a separate list. */ if (pos->referenced) - list_add_tail(&pos->node, &mg->removed_maps[map->type]); + list_add_tail(&pos->node, &mg->maps[map->type].removed_maps); else map__delete(pos); @@ -689,7 +699,7 @@ int map_groups__clone(struct map_groups *mg, struct map_groups *parent, enum map_type type) { struct map *map; - struct rb_root *maps = &parent->maps[type]; + struct maps *maps = &parent->maps[type]; for (map = maps__first(maps); map; map = map__next(map)) { struct map *new = map__clone(map); @@ -700,9 +710,9 @@ int map_groups__clone(struct map_groups *mg, return 0; } -void maps__insert(struct rb_root *maps, struct map *map) +void maps__insert(struct maps *maps, struct map *map) { - struct rb_node **p = &maps->rb_node; + struct rb_node **p = &maps->entries.rb_node; struct rb_node *parent = NULL; const u64 ip = map->start; struct map *m; @@ -717,17 +727,17 @@ void maps__insert(struct rb_root *maps, struct map *map) } rb_link_node(&map->rb_node, parent, p); - rb_insert_color(&map->rb_node, maps); + rb_insert_color(&map->rb_node, &maps->entries); } -void maps__remove(struct rb_root *maps, struct map *map) +void maps__remove(struct maps *maps, struct map *map) { - rb_erase(&map->rb_node, maps); + rb_erase(&map->rb_node, &maps->entries); } -struct map *maps__find(struct rb_root *maps, u64 ip) +struct map *maps__find(struct maps *maps, u64 ip) { - struct rb_node **p = &maps->rb_node; + struct rb_node **p = &maps->entries.rb_node; struct rb_node *parent = NULL; struct map *m; @@ -745,9 +755,9 @@ struct map *maps__find(struct rb_root *maps, u64 ip) return NULL; } -struct map *maps__first(struct rb_root *maps) +struct map *maps__first(struct maps *maps) { - struct rb_node *first = rb_first(maps); + struct rb_node *first = rb_first(&maps->entries); if (first) return rb_entry(first, struct map, rb_node); diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index f2b27566d986..e3702fd468c5 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -58,9 +58,13 @@ struct kmap { struct map_groups *kmaps; }; +struct maps { + struct rb_root entries; + struct list_head removed_maps; +}; + struct map_groups { - struct rb_root maps[MAP__NR_TYPES]; - struct list_head removed_maps[MAP__NR_TYPES]; + struct maps maps[MAP__NR_TYPES]; struct machine *machine; atomic_t refcnt; }; @@ -162,10 +166,10 @@ void map__reloc_vmlinux(struct map *map); size_t __map_groups__fprintf_maps(struct map_groups *mg, enum map_type type, FILE *fp); -void maps__insert(struct rb_root *maps, struct map *map); -void maps__remove(struct rb_root *maps, struct map *map); -struct map *maps__find(struct rb_root *maps, u64 addr); -struct map *maps__first(struct rb_root *maps); +void maps__insert(struct maps *maps, struct map *map); +void maps__remove(struct maps *maps, struct map *map); +struct map *maps__find(struct maps *maps, u64 addr); +struct map *maps__first(struct maps *maps); struct map *map__next(struct map *map); void map_groups__init(struct map_groups *mg, struct machine *machine); void map_groups__exit(struct map_groups *mg); diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 97da98481d89..32471d0839d1 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -163,7 +163,7 @@ static u64 kernel_get_symbol_address_by_name(const char *name, bool reloc) static struct map *kernel_get_module_map(const char *module) { struct map_groups *grp = &host_machine->kmaps; - struct rb_root *maps = &grp->maps[MAP__FUNCTION]; + struct maps *maps = &grp->maps[MAP__FUNCTION]; struct map *pos; /* A file path -- this is an offline module */ diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index b9e3eb581884..c8a3e79c5da2 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -202,7 +202,7 @@ void symbols__fixup_end(struct rb_root *symbols) void __map_groups__fixup_end(struct map_groups *mg, enum map_type type) { - struct rb_root *maps = &mg->maps[type]; + struct maps *maps = &mg->maps[type]; struct map *next, *curr; curr = maps__first(maps); @@ -1520,7 +1520,7 @@ out: struct map *map_groups__find_by_name(struct map_groups *mg, enum map_type type, const char *name) { - struct rb_root *maps = &mg->maps[type]; + struct maps *maps = &mg->maps[type]; struct map *map; for (map = maps__first(maps); map; map = map__next(map)) { -- cgit v1.2.3-59-g8ed1b From 84c2cafa288939e11d21c7830e32b2aee21b723e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 25 May 2015 16:59:56 -0300 Subject: perf tools: Reference count struct map We have pointers to struct map instances in several places, like in the hist_entry instances, so we need a way to know when we can destroy them, otherwise we may either keep leaking them or end up referencing deleted instances. Start fixing it by reference counting them. This patch puts the reference count for struct map in place, replacing direct map__delete() calls with map__put() ones and then grabbing a reference count when adding it to the maps struct where maps for a struct thread are kept. Next we'll grab reference counts when setting pointers to struct map instances, in places like in the hist_entry code. Cc: Adrian Hunter Cc: Borislav Petkov Cc: David Ahern Cc: Don Zickus Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-wi19xczk0t2a41r1i2chuio5@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 3 ++- tools/perf/util/map.c | 21 +++++++++++++++------ tools/perf/util/map.h | 11 +++++++++++ tools/perf/util/probe-event.c | 6 +++--- tools/perf/util/symbol-elf.c | 2 ++ tools/perf/util/symbol.c | 7 +++++-- 6 files changed, 38 insertions(+), 12 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 6bf845758ae3..0c0e61cce577 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -759,7 +759,6 @@ void machine__destroy_kernel_maps(struct machine *machine) kmap->ref_reloc_sym = NULL; } - map__delete(machine->vmlinux_maps[type]); machine->vmlinux_maps[type] = NULL; } } @@ -1247,6 +1246,7 @@ int machine__process_mmap2_event(struct machine *machine, thread__insert_map(thread, map); thread__put(thread); + map__put(map); return 0; out_problem_map: @@ -1297,6 +1297,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event thread__insert_map(thread, map); thread__put(thread); + map__put(map); return 0; out_problem_map: diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 4d3a92d5dff3..af572322586d 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -139,6 +139,7 @@ void map__init(struct map *map, enum map_type type, map->groups = NULL; map->referenced = false; map->erange_warned = false; + atomic_set(&map->refcnt, 1); } struct map *map__new(struct machine *machine, u64 start, u64 len, @@ -229,6 +230,12 @@ void map__delete(struct map *map) free(map); } +void map__put(struct map *map) +{ + if (map && atomic_dec_and_test(&map->refcnt)) + map__delete(map); +} + void map__fixup_start(struct map *map) { struct rb_root *symbols = &map->dso->symbols[map->type]; @@ -448,7 +455,7 @@ static void __maps__purge(struct maps *maps) next = rb_next(&pos->rb_node); rb_erase_init(&pos->rb_node, root); - map__delete(pos); + map__put(pos); } } @@ -458,7 +465,7 @@ static void __maps__purge_removed_maps(struct maps *maps) list_for_each_entry_safe(pos, n, &maps->removed_maps, node) { list_del_init(&pos->node); - map__delete(pos); + map__put(pos); } } @@ -682,7 +689,7 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp if (before == NULL) { err = -ENOMEM; - goto move_map; + goto put_map; } before->end = map->start; @@ -696,7 +703,7 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp if (after == NULL) { err = -ENOMEM; - goto move_map; + goto put_map; } after->start = map->end; @@ -704,14 +711,14 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp if (verbose >= 2) map__fprintf(after, fp); } -move_map: +put_map: /* * If we have references, just move them to a separate list. */ if (pos->referenced) list_add_tail(&pos->node, &maps->removed_maps); else - map__delete(pos); + map__put(pos); if (err) goto out; @@ -772,6 +779,7 @@ static void __maps__insert(struct maps *maps, struct map *map) rb_link_node(&map->rb_node, parent, p); rb_insert_color(&map->rb_node, &maps->entries); + map__get(map); } void maps__insert(struct maps *maps, struct map *map) @@ -784,6 +792,7 @@ void maps__insert(struct maps *maps, struct map *map) static void __maps__remove(struct maps *maps, struct map *map) { rb_erase_init(&map->rb_node, &maps->entries); + map__put(map); } void maps__remove(struct maps *maps, struct map *map) diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 6796f2785649..b8df09d94aca 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -52,6 +52,7 @@ struct map { struct dso *dso; struct map_groups *groups; + atomic_t refcnt; }; struct kmap { @@ -150,6 +151,16 @@ struct map *map__new(struct machine *machine, u64 start, u64 len, struct map *map__new2(u64 start, struct dso *dso, enum map_type type); void map__delete(struct map *map); struct map *map__clone(struct map *map); + +static inline struct map *map__get(struct map *map) +{ + if (map) + atomic_inc(&map->refcnt); + return map; +} + +void map__put(struct map *map); + int map__overlap(struct map *l, struct map *r); size_t map__fprintf(struct map *map, FILE *fp); size_t map__fprintf_dsoname(struct map *map, FILE *fp); diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 32471d0839d1..b0b8a8080009 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -195,7 +195,7 @@ static void put_target_map(struct map *map, bool user) { if (map && user) { /* Only the user map needs to be released */ - map__delete(map); + map__put(map); } } @@ -1791,7 +1791,7 @@ static int find_perf_probe_point_from_map(struct probe_trace_point *tp, out: if (map && !is_kprobe) { - map__delete(map); + map__put(map); } return ret; @@ -2884,7 +2884,7 @@ int show_available_funcs(const char *target, struct strfilter *_filter, dso__fprintf_symbols_by_name(map->dso, map->type, stdout); end: if (user) { - map__delete(map); + map__put(map); } exit_symbol_maps(); diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 9d526a5312b1..fa10116a12ab 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -972,8 +972,10 @@ int dso__load_sym(struct dso *dso, struct map *map, map->unmap_ip = map__unmap_ip; /* Ensure maps are correctly ordered */ if (kmaps) { + map__get(map); map_groups__remove(kmaps, map); map_groups__insert(kmaps, map); + map__put(map); } } diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 743a9b360e3d..a3e80d6ad70a 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1180,13 +1180,16 @@ static int dso__load_kcore(struct dso *dso, struct map *map, map->pgoff = new_map->pgoff; map->map_ip = new_map->map_ip; map->unmap_ip = new_map->unmap_ip; - map__delete(new_map); /* Ensure maps are correctly ordered */ + map__get(map); map_groups__remove(kmaps, map); map_groups__insert(kmaps, map); + map__put(map); } else { map_groups__insert(kmaps, new_map); } + + map__put(new_map); } /* @@ -1212,7 +1215,7 @@ out_err: while (!list_empty(&md.maps)) { map = list_entry(md.maps.next, struct map, node); list_del_init(&map->node); - map__delete(map); + map__put(map); } close(fd); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 60fb7742928dab3c6a0fec7f2d2cce26d9366a3c Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Thu, 28 May 2015 02:25:05 +0000 Subject: perf probe: Fix 'function unused' warning By 'make build-test' a warning is found in probe-event.c that, after commit 419e873828 (perf probe: Show the error reason comes from invalid DSO) the only user of kernel_get_module_dso() is open_debuginfo(). Which is not compiled if HAVE_DWARF_SUPPORT not set. 'make build-test' found this problem when make_minimal. This patch moves kernel_get_module_dso() to HAVE_DWARF_SUPPORT ifdef section. Signed-off-by: Wang Nan Acked-by: Masami Hiramatsu Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1432779905-206143-1-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 65 ++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 32 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index b0b8a8080009..e6a02b1ffd6d 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -200,38 +200,6 @@ static void put_target_map(struct map *map, bool user) } -static int kernel_get_module_dso(const char *module, struct dso **pdso) -{ - struct dso *dso; - struct map *map; - const char *vmlinux_name; - int ret = 0; - - if (module) { - list_for_each_entry(dso, &host_machine->kernel_dsos.head, - node) { - if (strncmp(dso->short_name + 1, module, - dso->short_name_len - 2) == 0) - goto found; - } - pr_debug("Failed to find module %s.\n", module); - return -ENOENT; - } - - map = host_machine->vmlinux_maps[MAP__FUNCTION]; - dso = map->dso; - - vmlinux_name = symbol_conf.vmlinux_name; - dso->load_errno = 0; - if (vmlinux_name) - ret = dso__load_vmlinux(dso, map, vmlinux_name, false, NULL); - else - ret = dso__load_vmlinux_path(dso, map, NULL); -found: - *pdso = dso; - return ret; -} - static int convert_exec_to_group(const char *exec, char **result) { char *ptr1, *ptr2, *exec_copy; @@ -279,6 +247,39 @@ static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs) } #ifdef HAVE_DWARF_SUPPORT + +static int kernel_get_module_dso(const char *module, struct dso **pdso) +{ + struct dso *dso; + struct map *map; + const char *vmlinux_name; + int ret = 0; + + if (module) { + list_for_each_entry(dso, &host_machine->kernel_dsos.head, + node) { + if (strncmp(dso->short_name + 1, module, + dso->short_name_len - 2) == 0) + goto found; + } + pr_debug("Failed to find module %s.\n", module); + return -ENOENT; + } + + map = host_machine->vmlinux_maps[MAP__FUNCTION]; + dso = map->dso; + + vmlinux_name = symbol_conf.vmlinux_name; + dso->load_errno = 0; + if (vmlinux_name) + ret = dso__load_vmlinux(dso, map, vmlinux_name, false, NULL); + else + ret = dso__load_vmlinux_path(dso, map, NULL); +found: + *pdso = dso; + return ret; +} + /* * Some binaries like glibc have special symbols which are on the symbol * table, but not in the debuginfo. If we can find the address of the -- cgit v1.2.3-59-g8ed1b From 3d39ac538629e4f00a6e1c38d46346f1b8e69505 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 28 May 2015 13:06:42 -0300 Subject: perf machine: No need to have two DSOs lists We can, given a DSO, figure out if it is a kernel, a kernel module or a userlevel DSO, so stop having to process two lists in several functions. If searching becomes an issue at some point, we can have them in a rbtree, etc. Cc: Adrian Hunter Cc: Borislav Petkov Cc: David Ahern Cc: Don Zickus Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-s4yb0onpdywu6dj2xl9lxi4t@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/hists_common.c | 2 +- tools/perf/util/build-id.c | 59 +++++++++++------------------------------ tools/perf/util/dso.c | 2 +- tools/perf/util/header.c | 6 +---- tools/perf/util/machine.c | 28 ++++++++----------- tools/perf/util/machine.h | 3 +-- tools/perf/util/map.c | 2 +- tools/perf/util/probe-event.c | 5 ++-- tools/perf/util/symbol-elf.c | 6 +---- tools/perf/util/vdso.c | 6 ++--- 10 files changed, 39 insertions(+), 80 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/tests/hists_common.c b/tools/perf/tests/hists_common.c index 456f884eb27b..bcde1d27919c 100644 --- a/tools/perf/tests/hists_common.c +++ b/tools/perf/tests/hists_common.c @@ -121,7 +121,7 @@ struct machine *setup_fake_machine(struct machines *machines) size_t k; struct dso *dso; - dso = __dsos__findnew(&machine->user_dsos, + dso = __dsos__findnew(&machine->dsos, fake_symbols[i].dso_name); if (dso == NULL) goto out; diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index ad8cfcbaa25d..1f6fc2323ef9 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -162,15 +162,20 @@ static int write_buildid(const char *name, size_t name_len, u8 *build_id, return write_padded(fd, name, name_len + 1, len); } -static int __dsos__write_buildid_table(struct list_head *head, - struct machine *machine, - pid_t pid, u16 misc, int fd) +static int machine__write_buildid_table(struct machine *machine, int fd) { + int err = 0; char nm[PATH_MAX]; struct dso *pos; + u16 kmisc = PERF_RECORD_MISC_KERNEL, + umisc = PERF_RECORD_MISC_USER; + + if (!machine__is_host(machine)) { + kmisc = PERF_RECORD_MISC_GUEST_KERNEL; + umisc = PERF_RECORD_MISC_GUEST_USER; + } - dsos__for_each_with_build_id(pos, head) { - int err; + dsos__for_each_with_build_id(pos, &machine->dsos.head) { const char *name; size_t name_len; @@ -189,32 +194,12 @@ static int __dsos__write_buildid_table(struct list_head *head, name_len = pos->long_name_len + 1; } - err = write_buildid(name, name_len, pos->build_id, - pid, misc, fd); + err = write_buildid(name, name_len, pos->build_id, machine->pid, + pos->kernel ? kmisc : umisc, fd); if (err) - return err; - } - - return 0; -} - -static int machine__write_buildid_table(struct machine *machine, int fd) -{ - int err; - u16 kmisc = PERF_RECORD_MISC_KERNEL, - umisc = PERF_RECORD_MISC_USER; - - if (!machine__is_host(machine)) { - kmisc = PERF_RECORD_MISC_GUEST_KERNEL; - umisc = PERF_RECORD_MISC_GUEST_USER; + break; } - err = __dsos__write_buildid_table(&machine->kernel_dsos.head, machine, - machine->pid, kmisc, fd); - if (err == 0) - err = __dsos__write_buildid_table(&machine->user_dsos.head, - machine, machine->pid, umisc, - fd); return err; } @@ -247,13 +232,7 @@ static int __dsos__hit_all(struct list_head *head) static int machine__hit_all_dsos(struct machine *machine) { - int err; - - err = __dsos__hit_all(&machine->kernel_dsos.head); - if (err) - return err; - - return __dsos__hit_all(&machine->user_dsos.head); + return __dsos__hit_all(&machine->dsos.head); } int dsos__hit_all(struct perf_session *session) @@ -493,9 +472,7 @@ static int __dsos__cache_build_ids(struct list_head *head, static int machine__cache_build_ids(struct machine *machine) { - int ret = __dsos__cache_build_ids(&machine->kernel_dsos.head, machine); - ret |= __dsos__cache_build_ids(&machine->user_dsos.head, machine); - return ret; + return __dsos__cache_build_ids(&machine->dsos.head, machine); } int perf_session__cache_build_ids(struct perf_session *session) @@ -520,11 +497,7 @@ int perf_session__cache_build_ids(struct perf_session *session) static bool machine__read_build_ids(struct machine *machine, bool with_hits) { - bool ret; - - ret = __dsos__read_build_ids(&machine->kernel_dsos.head, with_hits); - ret |= __dsos__read_build_ids(&machine->user_dsos.head, with_hits); - return ret; + return __dsos__read_build_ids(&machine->dsos.head, with_hits); } bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index fe5236833164..ff040b0569d6 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -833,7 +833,7 @@ struct dso *machine__findnew_kernel(struct machine *machine, const char *name, /* * The kernel dso could be created by build_id processing. */ - struct dso *dso = __dsos__findnew(&machine->kernel_dsos, name); + struct dso *dso = __dsos__findnew(&machine->dsos, name); /* * We need to run this in all cases, since during the build_id diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 3f0d809d853a..a900e9441fb5 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1238,7 +1238,6 @@ static int __event_process_build_id(struct build_id_event *bev, struct perf_session *session) { int err = -1; - struct dsos *dsos; struct machine *machine; u16 misc; struct dso *dso; @@ -1253,22 +1252,19 @@ static int __event_process_build_id(struct build_id_event *bev, switch (misc) { case PERF_RECORD_MISC_KERNEL: dso_type = DSO_TYPE_KERNEL; - dsos = &machine->kernel_dsos; break; case PERF_RECORD_MISC_GUEST_KERNEL: dso_type = DSO_TYPE_GUEST_KERNEL; - dsos = &machine->kernel_dsos; break; case PERF_RECORD_MISC_USER: case PERF_RECORD_MISC_GUEST_USER: dso_type = DSO_TYPE_USER; - dsos = &machine->user_dsos; break; default: goto out; } - dso = __dsos__findnew(dsos, filename); + dso = __dsos__findnew(&machine->dsos, filename); if (dso != NULL) { char sbuild_id[BUILD_ID_SIZE * 2 + 1]; diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 8934dc4345fe..ffd31079d447 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -26,8 +26,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) { map_groups__init(&machine->kmaps, machine); RB_CLEAR_NODE(&machine->rb_node); - dsos__init(&machine->user_dsos); - dsos__init(&machine->kernel_dsos); + dsos__init(&machine->dsos); machine->threads = RB_ROOT; pthread_rwlock_init(&machine->threads_lock, NULL); @@ -111,8 +110,7 @@ void machine__delete_threads(struct machine *machine) void machine__exit(struct machine *machine) { map_groups__exit(&machine->kmaps); - dsos__delete(&machine->user_dsos); - dsos__delete(&machine->kernel_dsos); + dsos__delete(&machine->dsos); vdso__exit(machine); zfree(&machine->root_dir); zfree(&machine->current_tid); @@ -490,9 +488,9 @@ machine__module_dso(struct machine *machine, struct kmod_path *m, { struct dso *dso; - dso = dsos__find(&machine->kernel_dsos, m->name, true); + dso = dsos__find(&machine->dsos, m->name, true); if (!dso) { - dso = dsos__addnew(&machine->kernel_dsos, m->name); + dso = dsos__addnew(&machine->dsos, m->name); if (dso == NULL) return NULL; @@ -561,13 +559,11 @@ out: size_t machines__fprintf_dsos(struct machines *machines, FILE *fp) { struct rb_node *nd; - size_t ret = __dsos__fprintf(&machines->host.kernel_dsos.head, fp) + - __dsos__fprintf(&machines->host.user_dsos.head, fp); + size_t ret = __dsos__fprintf(&machines->host.dsos.head, fp); for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); - ret += __dsos__fprintf(&pos->kernel_dsos.head, fp); - ret += __dsos__fprintf(&pos->user_dsos.head, fp); + ret += __dsos__fprintf(&pos->dsos.head, fp); } return ret; @@ -576,8 +572,7 @@ size_t machines__fprintf_dsos(struct machines *machines, FILE *fp) size_t machine__fprintf_dsos_buildid(struct machine *m, FILE *fp, bool (skip)(struct dso *dso, int parm), int parm) { - return __dsos__fprintf_buildid(&m->kernel_dsos.head, fp, skip, parm) + - __dsos__fprintf_buildid(&m->user_dsos.head, fp, skip, parm); + return __dsos__fprintf_buildid(&m->dsos.head, fp, skip, parm); } size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp, @@ -1106,7 +1101,7 @@ static bool machine__uses_kcore(struct machine *machine) { struct dso *dso; - list_for_each_entry(dso, &machine->kernel_dsos.head, node) { + list_for_each_entry(dso, &machine->dsos.head, node) { if (dso__is_kcore(dso)) return true; } @@ -1153,8 +1148,8 @@ static int machine__process_kernel_mmap_event(struct machine *machine, struct dso *kernel = NULL; struct dso *dso; - list_for_each_entry(dso, &machine->kernel_dsos.head, node) { - if (is_kernel_module(dso->long_name)) + list_for_each_entry(dso, &machine->dsos.head, node) { + if (dso->kernel && is_kernel_module(dso->long_name)) continue; kernel = dso; @@ -1162,8 +1157,7 @@ static int machine__process_kernel_mmap_event(struct machine *machine, } if (kernel == NULL) - kernel = __dsos__findnew(&machine->kernel_dsos, - kmmap_prefix); + kernel = __dsos__findnew(&machine->dsos, kmmap_prefix); if (kernel == NULL) goto out_problem; diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index c7963c63c474..aabca583e655 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -34,8 +34,7 @@ struct machine { struct list_head dead_threads; struct thread *last_match; struct vdso_info *vdso_info; - struct dsos user_dsos; - struct dsos kernel_dsos; + struct dsos dsos; struct map_groups kmaps; struct map *vmlinux_maps[MAP__NR_TYPES]; u64 kernel_start; diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index af572322586d..57ff0256c22c 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -180,7 +180,7 @@ struct map *map__new(struct machine *machine, u64 start, u64 len, pgoff = 0; dso = vdso__dso_findnew(machine, thread); } else - dso = __dsos__findnew(&machine->user_dsos, filename); + dso = __dsos__findnew(&machine->dsos, filename); if (dso == NULL) goto out_delete; diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index e6a02b1ffd6d..d27edef5eb5b 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -256,8 +256,9 @@ static int kernel_get_module_dso(const char *module, struct dso **pdso) int ret = 0; if (module) { - list_for_each_entry(dso, &host_machine->kernel_dsos.head, - node) { + list_for_each_entry(dso, &host_machine->dsos.head, node) { + if (!dso->kernel) + continue; if (strncmp(dso->short_name + 1, module, dso->short_name_len - 2) == 0) goto found; diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index fa10116a12ab..a93ba85509b2 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1031,11 +1031,7 @@ int dso__load_sym(struct dso *dso, struct map *map, } curr_dso->symtab_type = dso->symtab_type; map_groups__insert(kmaps, curr_map); - /* - * The new DSO should go to the kernel DSOS - */ - dsos__add(&map->groups->machine->kernel_dsos, - curr_dso); + dsos__add(&map->groups->machine->dsos, curr_dso); dso__set_loaded(curr_dso, map->type); } else curr_dso = curr_map->dso; diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c index 5c7dd796979d..d3651b43e945 100644 --- a/tools/perf/util/vdso.c +++ b/tools/perf/util/vdso.c @@ -127,7 +127,7 @@ static struct dso *vdso__new(struct machine *machine, const char *short_name, dso = dso__new(short_name); if (dso != NULL) { - dsos__add(&machine->user_dsos, dso); + dsos__add(&machine->dsos, dso); dso__set_long_name(dso, long_name, false); } @@ -236,7 +236,7 @@ static struct dso *vdso__findnew_compat(struct machine *machine, const char *file_name; struct dso *dso; - dso = dsos__find(&machine->user_dsos, vdso_file->dso_name, true); + dso = dsos__find(&machine->dsos, vdso_file->dso_name, true); if (dso) return dso; @@ -299,7 +299,7 @@ struct dso *vdso__dso_findnew(struct machine *machine, return dso; #endif - dso = dsos__find(&machine->user_dsos, DSO__NAME_VDSO, true); + dso = dsos__find(&machine->dsos, DSO__NAME_VDSO, true); if (!dso) { char *file; -- cgit v1.2.3-59-g8ed1b From 6bb536cc4b06df70012cec6168cc1788e200bc9d Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Fri, 29 May 2015 09:45:47 +0000 Subject: perf probe: Fix segfault when glob matching function without debuginfo Commit 4c859351226c920b227fec040a3b447f0d482af3 ("perf probe: Support glob wildcards for function name") introduces segfault problems when debuginfo is not available: # perf probe 'sys_w*' Added new events: Segmentation fault The first problem resides in find_probe_trace_events_from_map(). In that function, find_probe_functions() is called to match each symbol against glob to find the number of matching functions, but still use map__for_each_symbol_by_name() to find 'struct symbol' for matching functions. Unfortunately, map__for_each_symbol_by_name() does exact matching by searching in an rbtree. It doesn't know glob matching, and not easy for it to support it because it use rbtree based binary search, but we are unable to ensure all names matched by the glob (any glob passed by user) reside in one subtree. This patch drops map__for_each_symbol_by_name(). Since there is no rbtree again, re-matching all symbols costs a lot. This patch avoid it by saving all matching results into an array (syms). The second problem is the lost of tp->realname. In __add_probe_trace_events(), if pev->point.function is glob, the event name should be set to tev->point.realname. This patch ensures its existence by strdup sym->name instead of leaving a NULL pointer there. After this patch: # perf probe 'sys_w*' Added new events: probe:sys_waitid (on sys_w*) probe:sys_wait4 (on sys_w*) probe:sys_waitpid (on sys_w*) probe:sys_write (on sys_w*) probe:sys_writev (on sys_w*) You can now use it in all perf tools, such as: perf record -e probe:sys_writev -aR sleep 1 Signed-off-by: Wang Nan Acked-by: Masami Hiramatsu Cc: Jiri Olsa Cc: Namhyung Kim Cc: Zefan Li Link: http://lkml.kernel.org/r/1432892747-232506-1-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index d27edef5eb5b..e6f215b7a052 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -2494,7 +2494,8 @@ close_out: return ret; } -static int find_probe_functions(struct map *map, char *name) +static int find_probe_functions(struct map *map, char *name, + struct symbol **syms) { int found = 0; struct symbol *sym; @@ -2504,8 +2505,11 @@ static int find_probe_functions(struct map *map, char *name) return 0; map__for_each_symbol(map, sym, tmp) { - if (strglobmatch(sym->name, name)) + if (strglobmatch(sym->name, name)) { found++; + if (syms && found < probe_conf.max_probes) + syms[found - 1] = sym; + } } return found; @@ -2528,11 +2532,12 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, struct map *map = NULL; struct ref_reloc_sym *reloc_sym = NULL; struct symbol *sym; + struct symbol **syms = NULL; struct probe_trace_event *tev; struct perf_probe_point *pp = &pev->point; struct probe_trace_point *tp; int num_matched_functions; - int ret, i; + int ret, i, j; map = get_target_map(pev->target, pev->uprobes); if (!map) { @@ -2540,11 +2545,17 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, goto out; } + syms = malloc(sizeof(struct symbol *) * probe_conf.max_probes); + if (!syms) { + ret = -ENOMEM; + goto out; + } + /* * Load matched symbols: Since the different local symbols may have * same name but different addresses, this lists all the symbols. */ - num_matched_functions = find_probe_functions(map, pp->function); + num_matched_functions = find_probe_functions(map, pp->function, syms); if (num_matched_functions == 0) { pr_err("Failed to find symbol %s in %s\n", pp->function, pev->target ? : "kernel"); @@ -2575,7 +2586,9 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, ret = 0; - map__for_each_symbol_by_name(map, pp->function, sym) { + for (j = 0; j < num_matched_functions; j++) { + sym = syms[j]; + tev = (*tevs) + ret; tp = &tev->point; if (ret == num_matched_functions) { @@ -2599,6 +2612,8 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, tp->symbol = strdup_or_goto(sym->name, nomem_out); tp->offset = pp->offset; } + tp->realname = strdup_or_goto(sym->name, nomem_out); + tp->retprobe = pp->retprobe; if (pev->target) tev->point.module = strdup_or_goto(pev->target, @@ -2629,6 +2644,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, out: put_target_map(map, pev->uprobes); + free(syms); return ret; nomem_out: -- cgit v1.2.3-59-g8ed1b From 9f2de31542f1ac38a15117f90ee6b8449951d86e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 1 Jun 2015 12:01:02 -0300 Subject: perf machine: Fix up some more method names Calling the function 'machine__new_module' implies a new 'module' will be allocated, when in fact what is returned is a 'struct map' instance, that not necessarily will be instantiated, as if one already exists with the given module name, it will be returned instead. So be consistent with other "find and if not there, create" like functions, like machine__findnew_thread, machine__findnew_dso, etc, and rename it to machine__findnew_module_map(), that in turn will call machine__findnew_module_dso(). Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Link: http://lkml.kernel.org/n/tip-acv830vd3hwww2ih5vjtbmu3@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 18 +++++++++--------- tools/perf/util/machine.h | 4 ++-- tools/perf/util/probe-event.c | 2 +- tools/perf/util/symbol.c | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index f15ed24a22ac..dfd419797e6e 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -490,9 +490,9 @@ int machine__process_lost_samples_event(struct machine *machine __maybe_unused, return 0; } -static struct dso* -machine__module_dso(struct machine *machine, struct kmod_path *m, - const char *filename) +static struct dso *machine__findnew_module_dso(struct machine *machine, + struct kmod_path *m, + const char *filename) { struct dso *dso; @@ -534,8 +534,8 @@ int machine__process_itrace_start_event(struct machine *machine __maybe_unused, return 0; } -struct map *machine__new_module(struct machine *machine, u64 start, - const char *filename) +struct map *machine__findnew_module_map(struct machine *machine, u64 start, + const char *filename) { struct map *map = NULL; struct dso *dso; @@ -549,7 +549,7 @@ struct map *machine__new_module(struct machine *machine, u64 start, if (map) goto out; - dso = machine__module_dso(machine, &m, filename); + dso = machine__findnew_module_dso(machine, &m, filename); if (dso == NULL) goto out; @@ -1017,7 +1017,7 @@ static int machine__create_module(void *arg, const char *name, u64 start) struct machine *machine = arg; struct map *map; - map = machine__new_module(machine, start, name); + map = machine__findnew_module_map(machine, start, name); if (map == NULL) return -1; @@ -1140,8 +1140,8 @@ static int machine__process_kernel_mmap_event(struct machine *machine, strlen(kmmap_prefix) - 1) == 0; if (event->mmap.filename[0] == '/' || (!is_kernel_mmap && event->mmap.filename[0] == '[')) { - map = machine__new_module(machine, event->mmap.start, - event->mmap.filename); + map = machine__findnew_module_map(machine, event->mmap.start, + event->mmap.filename); if (map == NULL) goto out_problem; diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 8e1f796fd137..ca267c41f28d 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -189,8 +189,8 @@ struct symbol *machine__find_kernel_function_by_name(struct machine *machine, filter); } -struct map *machine__new_module(struct machine *machine, u64 start, - const char *filename); +struct map *machine__findnew_module_map(struct machine *machine, u64 start, + const char *filename); int machine__load_kallsyms(struct machine *machine, const char *filename, enum map_type type, symbol_filter_t filter); diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index e6f215b7a052..d4cf50b91839 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -168,7 +168,7 @@ static struct map *kernel_get_module_map(const char *module) /* A file path -- this is an offline module */ if (module && strchr(module, '/')) - return machine__new_module(host_machine, 0, module); + return machine__findnew_module_map(host_machine, 0, module); if (!module) module = "kernel"; diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index a3e80d6ad70a..eaee5d32d39d 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1364,7 +1364,7 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod, case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP: /* * kernel modules know their symtab type - it's set when - * creating a module dso in machine__new_module(). + * creating a module dso in machine__findnew_module_map(). */ return kmod && dso->symtab_type == type; -- cgit v1.2.3-59-g8ed1b From 35a23ff928b066b00a826d0a9ed9411b8ab479ef Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 12 Jun 2015 14:08:20 +0900 Subject: perf probe: Cut off the gcc optimization postfixes from function name Cut off the postfixes which gcc added for optimized routines from the event name automatically generated from symbol name, since *probe-events doesn't accept it. Those symbols will be used if we don't use debuginfo to find target functions. E.g. without this fix; ----- # perf probe -va alloc_buf.isra.23 probe-definition(0): alloc_buf.isra.23 symbol:alloc_buf.isra.23 file:(null) line:0 offset:0 return:0 lazy:(null) [...] Opening /sys/kernel/debug/tracing/kprobe_events write=1 Added new event: Writing event: p:probe/alloc_buf.isra.23 _text+4869328 Failed to write event: Invalid argument Error: Failed to add events. Reason: Invalid argument (Code: -22) ----- With this fix; ----- perf probe -va alloc_buf.isra.23 probe-definition(0): alloc_buf.isra.23 symbol:alloc_buf.isra.23 file:(null) line:0 offset:0 return:0 lazy:(null) [...] Opening /sys/kernel/debug/tracing/kprobe_events write=1 Added new event: Writing event: p:probe/alloc_buf _text+4869328 probe:alloc_buf (on alloc_buf.isra.23) You can now use it in all perf tools, such as: perf record -e probe:alloc_buf -aR sleep 1 ----- Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Naohiro Aota Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20150612050820.20548.41625.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index d4cf50b91839..daa24a249e05 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -2316,6 +2316,7 @@ static int get_new_event_name(char *buf, size_t len, const char *base, struct strlist *namelist, bool allow_suffix) { int i, ret; + char *p; if (*base == '.') base++; @@ -2326,6 +2327,10 @@ static int get_new_event_name(char *buf, size_t len, const char *base, pr_debug("snprintf() failed: %d\n", ret); return ret; } + /* Cut off the postfixes (e.g. .const, .isra)*/ + p = strchr(buf, '.'); + if (p && p != buf) + *p = '\0'; if (!strlist__has_entry(namelist, buf)) return 0; -- cgit v1.2.3-59-g8ed1b From ba7ecb02e7b89b09d8cdf4c1514a386af8916c4b Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Sat, 13 Jun 2015 10:31:16 +0900 Subject: perf probe: List probes in stdout Since commit 5e17b28f1e24 ("perf probe: Add --quiet option to suppress output result message") have replaced printf with pr_info, perf probe -l outputs its result in stderr. However, that is not what the commit expected. E.g.: # perf probe -l > /dev/null probe:vfs_read (on vfs_read@ksrc/linux-3/fs/read_write.c) With this fix: # perf probe -l > list # cat list probe:vfs_read (on vfs_read@ksrc/linux-3/fs/read_write.c) Of course, --quiet(-q) still works on --add/--del. # perf probe -q vfs_write # perf probe -l probe:vfs_read (on vfs_read@ksrc/linux-3/fs/read_write.c) probe:vfs_write (on vfs_write@ksrc/linux-3/fs/read_write.c) ----- Reported-by: Naohiro Aota Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Naohiro Aota Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20150613013116.24402.2923.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 49 ++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 14 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index daa24a249e05..c4ab58870fcc 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -2126,9 +2126,9 @@ kprobe_blacklist__find_by_address(struct list_head *blacklist, return NULL; } -/* Show an event */ -static int show_perf_probe_event(struct perf_probe_event *pev, - const char *module) +static int perf_probe_event__sprintf(struct perf_probe_event *pev, + const char *module, + struct strbuf *result) { int i, ret; char buf[128]; @@ -2141,27 +2141,47 @@ static int show_perf_probe_event(struct perf_probe_event *pev, ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event); if (ret < 0) - return ret; + goto out; - pr_info(" %-20s (on %s", buf, place); + strbuf_addf(result, " %-20s (on %s", buf, place); if (module) - pr_info(" in %s", module); + strbuf_addf(result, " in %s", module); if (pev->nargs > 0) { - pr_info(" with"); + strbuf_addstr(result, " with"); for (i = 0; i < pev->nargs; i++) { ret = synthesize_perf_probe_arg(&pev->args[i], buf, 128); if (ret < 0) - break; - pr_info(" %s", buf); + goto out; + strbuf_addf(result, " %s", buf); } } - pr_info(")\n"); + strbuf_addch(result, ')'); +out: free(place); return ret; } +/* Show an event */ +static int show_perf_probe_event(struct perf_probe_event *pev, + const char *module, bool use_stdout) +{ + struct strbuf buf = STRBUF_INIT; + int ret; + + ret = perf_probe_event__sprintf(pev, module, &buf); + if (ret >= 0) { + if (use_stdout) + printf("%s\n", buf.buf); + else + pr_info("%s\n", buf.buf); + } + strbuf_release(&buf); + + return ret; +} + static bool filter_probe_trace_event(struct probe_trace_event *tev, struct strfilter *filter) { @@ -2200,9 +2220,10 @@ static int __show_perf_probe_events(int fd, bool is_kprobe, goto next; ret = convert_to_perf_probe_event(&tev, &pev, is_kprobe); - if (ret >= 0) - ret = show_perf_probe_event(&pev, - tev.point.module); + if (ret < 0) + goto next; + ret = show_perf_probe_event(&pev, tev.point.module, + true); } next: clear_perf_probe_event(&pev); @@ -2468,7 +2489,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, group = pev->group; pev->event = tev->event; pev->group = tev->group; - show_perf_probe_event(pev, tev->point.module); + show_perf_probe_event(pev, tev->point.module, false); /* Trick here - restore current event/group */ pev->event = (char *)event; pev->group = (char *)group; -- cgit v1.2.3-59-g8ed1b From b031220d520238075bd99513a420e65cf37866ad Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 16 Jun 2015 20:50:55 +0900 Subject: perf probe: Fix to return error if no probe is added Fix perf probe to return an error if no probe is added due to the given probe point being on the blacklist. To fix this problem, this moves the blacklist checking to right after finding symbols/probe-points and marks them as skipped. If all the symbols are skipped, "perf probe" returns an error as it fails to find the corresponding probe address. E.g. currently if a blacklisted probe is given: # perf probe do_trap && echo 'succeed' Added new event: Warning: Skipped probing on blacklisted function: sync_regs succeed No! It must fail! With this patch, it correctly fails: # perf probe do_trap && echo 'succeed' do_trap is blacklisted function, skip it. Probe point 'do_trap' not found. Error: Failed to add events. Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Naohiro Aota Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20150616115055.19906.31359.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 113 ++++++++++++++++++++++++++---------------- 1 file changed, 71 insertions(+), 42 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index c4ab58870fcc..85c8207c25cc 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -246,6 +246,20 @@ static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs) clear_probe_trace_event(tevs + i); } +static bool kprobe_blacklist__listed(unsigned long address); +static bool kprobe_warn_out_range(const char *symbol, unsigned long address) +{ + /* Get the address of _etext for checking non-probable text symbol */ + if (kernel_get_symbol_address_by_name("_etext", false) < address) + pr_warning("%s is out of .text, skip it.\n", symbol); + else if (kprobe_blacklist__listed(address)) + pr_warning("%s is blacklisted function, skip it.\n", symbol); + else + return false; + + return true; +} + #ifdef HAVE_DWARF_SUPPORT static int kernel_get_module_dso(const char *module, struct dso **pdso) @@ -559,7 +573,6 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs, bool uprobe) { struct ref_reloc_sym *reloc_sym; - u64 etext_addr; char *tmp; int i, skipped = 0; @@ -575,31 +588,28 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs, pr_warning("Relocated base symbol is not found!\n"); return -EINVAL; } - /* Get the address of _etext for checking non-probable text symbol */ - etext_addr = kernel_get_symbol_address_by_name("_etext", false); for (i = 0; i < ntevs; i++) { - if (tevs[i].point.address && !tevs[i].point.retprobe) { - /* If we found a wrong one, mark it by NULL symbol */ - if (etext_addr < tevs[i].point.address) { - pr_warning("%s+%lu is out of .text, skip it.\n", - tevs[i].point.symbol, tevs[i].point.offset); - tmp = NULL; - skipped++; - } else { - tmp = strdup(reloc_sym->name); - if (!tmp) - return -ENOMEM; - } - /* If we have no realname, use symbol for it */ - if (!tevs[i].point.realname) - tevs[i].point.realname = tevs[i].point.symbol; - else - free(tevs[i].point.symbol); - tevs[i].point.symbol = tmp; - tevs[i].point.offset = tevs[i].point.address - - reloc_sym->unrelocated_addr; + if (!tevs[i].point.address || tevs[i].point.retprobe) + continue; + /* If we found a wrong one, mark it by NULL symbol */ + if (kprobe_warn_out_range(tevs[i].point.symbol, + tevs[i].point.address)) { + tmp = NULL; + skipped++; + } else { + tmp = strdup(reloc_sym->name); + if (!tmp) + return -ENOMEM; } + /* If we have no realname, use symbol for it */ + if (!tevs[i].point.realname) + tevs[i].point.realname = tevs[i].point.symbol; + else + free(tevs[i].point.symbol); + tevs[i].point.symbol = tmp; + tevs[i].point.offset = tevs[i].point.address - + reloc_sym->unrelocated_addr; } return skipped; } @@ -2126,6 +2136,27 @@ kprobe_blacklist__find_by_address(struct list_head *blacklist, return NULL; } +static LIST_HEAD(kprobe_blacklist); + +static void kprobe_blacklist__init(void) +{ + if (!list_empty(&kprobe_blacklist)) + return; + + if (kprobe_blacklist__load(&kprobe_blacklist) < 0) + pr_debug("No kprobe blacklist support, ignored\n"); +} + +static void kprobe_blacklist__release(void) +{ + kprobe_blacklist__delete(&kprobe_blacklist); +} + +static bool kprobe_blacklist__listed(unsigned long address) +{ + return !!kprobe_blacklist__find_by_address(&kprobe_blacklist, address); +} + static int perf_probe_event__sprintf(struct perf_probe_event *pev, const char *module, struct strbuf *result) @@ -2409,8 +2440,6 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, char buf[64]; const char *event, *group; struct strlist *namelist; - LIST_HEAD(blacklist); - struct kprobe_blacklist_node *node; bool safename; if (pev->uprobes) @@ -2430,28 +2459,15 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, ret = -ENOMEM; goto close_out; } - /* Get kprobe blacklist if exists */ - if (!pev->uprobes) { - ret = kprobe_blacklist__load(&blacklist); - if (ret < 0) - pr_debug("No kprobe blacklist support, ignored\n"); - } safename = (pev->point.function && !strisglob(pev->point.function)); ret = 0; pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":"); for (i = 0; i < ntevs; i++) { tev = &tevs[i]; - /* Skip if the symbol is out of .text (marked previously) */ + /* Skip if the symbol is out of .text or blacklisted */ if (!tev->point.symbol) continue; - /* Ensure that the address is NOT blacklisted */ - node = kprobe_blacklist__find_by_address(&blacklist, - tev->point.address); - if (node) { - pr_warning("Warning: Skipped probing on blacklisted function: %s\n", node->symbol); - continue; - } if (pev->event) event = pev->event; @@ -2513,7 +2529,6 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, tev->event); } - kprobe_blacklist__delete(&blacklist); strlist__delete(namelist); close_out: close(fd); @@ -2563,7 +2578,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, struct perf_probe_point *pp = &pev->point; struct probe_trace_point *tp; int num_matched_functions; - int ret, i, j; + int ret, i, j, skipped = 0; map = get_target_map(pev->target, pev->uprobes); if (!map) { @@ -2631,7 +2646,12 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, } /* Add one probe point */ tp->address = map->unmap_ip(map, sym->start) + pp->offset; - if (reloc_sym) { + /* If we found a wrong one, mark it by NULL symbol */ + if (!pev->uprobes && + kprobe_warn_out_range(sym->name, tp->address)) { + tp->symbol = NULL; /* Skip it */ + skipped++; + } else if (reloc_sym) { tp->symbol = strdup_or_goto(reloc_sym->name, nomem_out); tp->offset = tp->address - reloc_sym->addr; } else { @@ -2667,6 +2687,10 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, } arch__fix_tev_from_maps(pev, tev, map); } + if (ret == skipped) { + ret = -ENOENT; + goto err_out; + } out: put_target_map(map, pev->uprobes); @@ -2737,6 +2761,9 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs) /* Loop 1: convert all events */ for (i = 0; i < npevs; i++) { pkgs[i].pev = &pevs[i]; + /* Init kprobe blacklist if needed */ + if (!pkgs[i].pev->uprobes) + kprobe_blacklist__init(); /* Convert with or without debuginfo */ ret = convert_to_probe_trace_events(pkgs[i].pev, &pkgs[i].tevs); @@ -2744,6 +2771,8 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs) goto end; pkgs[i].ntevs = ret; } + /* This just release blacklist only if allocated */ + kprobe_blacklist__release(); /* Loop 2: add all events */ for (i = 0; i < npevs; i++) { -- cgit v1.2.3-59-g8ed1b From d350bd571ffa89fc3bd07cfa9685d5210f459be8 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 16 Jun 2015 20:50:57 +0900 Subject: perf probe: Show usage even if the last event is skipped When the last part of converted events are blacklisted or out-of-text, those are skipped and perf probe doesn't show usage examples. This fixes it to show the example even if the last part of event list is skipped. E.g. without this patch, events are added, but suddenly end: # perf probe vfs_* vfs_caches_init_early is out of .text, skip it. vfs_caches_init is out of .text, skip it. Added new events: probe:vfs_fallocate (on vfs_*) probe:vfs_open (on vfs_*) ... probe:vfs_dentry_acceptable (on vfs_*) probe:vfs_load_quota_inode (on vfs_*) # With this fix: # perf probe vfs_* vfs_caches_init_early is out of .text, skip it. vfs_caches_init is out of .text, skip it. Added new events: probe:vfs_fallocate (on vfs_*) ... probe:vfs_load_quota_inode (on vfs_*) You can now use it in all perf tools, such as: perf record -e probe:vfs_load_quota_inode -aR sleep 1 Note that this can be reproduced ONLY IF the vfs_caches_init* is the last part of matched symbol list. I've checked this happens on "3.19.0-generic #18-Ubuntu" kernel binary. Signed-off-by: Masami Hiramatsu Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Naohiro Aota Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20150616115057.19906.5502.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 85c8207c25cc..65a1c8252270 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -2157,7 +2157,8 @@ static bool kprobe_blacklist__listed(unsigned long address) return !!kprobe_blacklist__find_by_address(&kprobe_blacklist, address); } -static int perf_probe_event__sprintf(struct perf_probe_event *pev, +static int perf_probe_event__sprintf(const char *group, const char *event, + struct perf_probe_event *pev, const char *module, struct strbuf *result) { @@ -2170,7 +2171,7 @@ static int perf_probe_event__sprintf(struct perf_probe_event *pev, if (!place) return -EINVAL; - ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event); + ret = e_snprintf(buf, 128, "%s:%s", group, event); if (ret < 0) goto out; @@ -2195,13 +2196,14 @@ out: } /* Show an event */ -static int show_perf_probe_event(struct perf_probe_event *pev, +static int show_perf_probe_event(const char *group, const char *event, + struct perf_probe_event *pev, const char *module, bool use_stdout) { struct strbuf buf = STRBUF_INIT; int ret; - ret = perf_probe_event__sprintf(pev, module, &buf); + ret = perf_probe_event__sprintf(group, event, pev, module, &buf); if (ret >= 0) { if (use_stdout) printf("%s\n", buf.buf); @@ -2253,7 +2255,8 @@ static int __show_perf_probe_events(int fd, bool is_kprobe, is_kprobe); if (ret < 0) goto next; - ret = show_perf_probe_event(&pev, tev.point.module, + ret = show_perf_probe_event(pev.group, pev.event, + &pev, tev.point.module, true); } next: @@ -2438,7 +2441,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, int i, fd, ret; struct probe_trace_event *tev = NULL; char buf[64]; - const char *event, *group; + const char *event = NULL, *group = NULL; struct strlist *namelist; bool safename; @@ -2500,15 +2503,12 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, /* Add added event name to namelist */ strlist__add(namelist, event); - /* Trick here - save current event/group */ - event = pev->event; - group = pev->group; - pev->event = tev->event; - pev->group = tev->group; - show_perf_probe_event(pev, tev->point.module, false); - /* Trick here - restore current event/group */ - pev->event = (char *)event; - pev->group = (char *)group; + /* We use tev's name for showing new events */ + show_perf_probe_event(tev->group, tev->event, pev, + tev->point.module, false); + /* Save the last valid name */ + event = tev->event; + group = tev->group; /* * Probes after the first probe which comes from same @@ -2522,11 +2522,10 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, warn_uprobe_event_compat(tev); /* Note that it is possible to skip all events because of blacklist */ - if (ret >= 0 && tev->event) { + if (ret >= 0 && event) { /* Show how to use the event. */ pr_info("\nYou can now use it in all perf tools, such as:\n\n"); - pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group, - tev->event); + pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", group, event); } strlist__delete(namelist); -- cgit v1.2.3-59-g8ed1b From 7737af010b097f3c1e2aeded21774d58b4aa2698 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 17 Jun 2015 23:58:54 +0900 Subject: perf probe: Speed up perf probe --list by caching debuginfo Speed up the "perf probe --list" by caching the last used debuginfo. perf probe --list always open and load debuginfo for each entry of probe list. This takes very a long time. E.g. with vfs_* events (total 96 probes) [root@localhost perf]# time ./perf probe -l &> /dev/null real 0m25.376s user 0m24.381s sys 0m1.012s To solve this issue, this adds debuginfo_cache to cache the last used debuginfo on memory. With this fix, the perf-probe --list significantly improves its speed. [root@localhost perf]# time ./perf probe -l &> /dev/null real 0m0.161s user 0m0.136s sys 0m0.025s Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Naohiro Aota Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20150617145854.19715.15314.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 48 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 65a1c8252270..076527b639bd 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -429,6 +429,41 @@ static struct debuginfo *open_debuginfo(const char *module, bool silent) return ret; } +/* For caching the last debuginfo */ +static struct debuginfo *debuginfo_cache; +static char *debuginfo_cache_path; + +static struct debuginfo *debuginfo_cache__open(const char *module, bool silent) +{ + if ((debuginfo_cache_path && !strcmp(debuginfo_cache_path, module)) || + (!debuginfo_cache_path && !module && debuginfo_cache)) + goto out; + + /* Copy module path */ + free(debuginfo_cache_path); + if (module) { + debuginfo_cache_path = strdup(module); + if (!debuginfo_cache_path) { + debuginfo__delete(debuginfo_cache); + debuginfo_cache = NULL; + goto out; + } + } + + debuginfo_cache = open_debuginfo(module, silent); + if (!debuginfo_cache) + zfree(&debuginfo_cache_path); +out: + return debuginfo_cache; +} + +static void debuginfo_cache__exit(void) +{ + debuginfo__delete(debuginfo_cache); + debuginfo_cache = NULL; + zfree(&debuginfo_cache_path); +} + static int get_text_start_address(const char *exec, unsigned long *address) { @@ -490,12 +525,11 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp, pr_debug("try to find information at %" PRIx64 " in %s\n", addr, tp->module ? : "kernel"); - dinfo = open_debuginfo(tp->module, verbose == 0); - if (dinfo) { + dinfo = debuginfo_cache__open(tp->module, verbose == 0); + if (dinfo) ret = debuginfo__find_probe_point(dinfo, (unsigned long)addr, pp); - debuginfo__delete(dinfo); - } else + else ret = -ENOENT; if (ret > 0) { @@ -930,6 +964,10 @@ out: #else /* !HAVE_DWARF_SUPPORT */ +static void debuginfo_cache__exit(void) +{ +} + static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp __maybe_unused, struct perf_probe_point *pp __maybe_unused, @@ -2266,6 +2304,8 @@ next: break; } strlist__delete(rawlist); + /* Cleanup cached debuginfo if needed */ + debuginfo_cache__exit(); return ret; } -- cgit v1.2.3-59-g8ed1b