Commit 6bb536cc authored by Wang Nan's avatar Wang Nan Committed by Arnaldo Carvalho de Melo

perf probe: Fix segfault when glob matching function without debuginfo

Commit 4c859351 ("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: default avatarWang Nan <wangnan0@huawei.com>
Acked-by: default avatarMasami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Zefan Li <lizefan@huawei.com>
Link: http://lkml.kernel.org/r/1432892747-232506-1-git-send-email-wangnan0@huawei.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 5c9b9bc6
...@@ -2494,7 +2494,8 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, ...@@ -2494,7 +2494,8 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
return ret; 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; int found = 0;
struct symbol *sym; struct symbol *sym;
...@@ -2504,8 +2505,11 @@ static int find_probe_functions(struct map *map, char *name) ...@@ -2504,8 +2505,11 @@ static int find_probe_functions(struct map *map, char *name)
return 0; return 0;
map__for_each_symbol(map, sym, tmp) { map__for_each_symbol(map, sym, tmp) {
if (strglobmatch(sym->name, name)) if (strglobmatch(sym->name, name)) {
found++; found++;
if (syms && found < probe_conf.max_probes)
syms[found - 1] = sym;
}
} }
return found; return found;
...@@ -2528,11 +2532,12 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, ...@@ -2528,11 +2532,12 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
struct map *map = NULL; struct map *map = NULL;
struct ref_reloc_sym *reloc_sym = NULL; struct ref_reloc_sym *reloc_sym = NULL;
struct symbol *sym; struct symbol *sym;
struct symbol **syms = NULL;
struct probe_trace_event *tev; struct probe_trace_event *tev;
struct perf_probe_point *pp = &pev->point; struct perf_probe_point *pp = &pev->point;
struct probe_trace_point *tp; struct probe_trace_point *tp;
int num_matched_functions; int num_matched_functions;
int ret, i; int ret, i, j;
map = get_target_map(pev->target, pev->uprobes); map = get_target_map(pev->target, pev->uprobes);
if (!map) { if (!map) {
...@@ -2540,11 +2545,17 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, ...@@ -2540,11 +2545,17 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
goto out; 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 * Load matched symbols: Since the different local symbols may have
* same name but different addresses, this lists all the symbols. * 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) { if (num_matched_functions == 0) {
pr_err("Failed to find symbol %s in %s\n", pp->function, pr_err("Failed to find symbol %s in %s\n", pp->function,
pev->target ? : "kernel"); pev->target ? : "kernel");
...@@ -2575,7 +2586,9 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, ...@@ -2575,7 +2586,9 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
ret = 0; 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; tev = (*tevs) + ret;
tp = &tev->point; tp = &tev->point;
if (ret == num_matched_functions) { if (ret == num_matched_functions) {
...@@ -2599,6 +2612,8 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, ...@@ -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->symbol = strdup_or_goto(sym->name, nomem_out);
tp->offset = pp->offset; tp->offset = pp->offset;
} }
tp->realname = strdup_or_goto(sym->name, nomem_out);
tp->retprobe = pp->retprobe; tp->retprobe = pp->retprobe;
if (pev->target) if (pev->target)
tev->point.module = strdup_or_goto(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, ...@@ -2629,6 +2644,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
out: out:
put_target_map(map, pev->uprobes); put_target_map(map, pev->uprobes);
free(syms);
return ret; return ret;
nomem_out: nomem_out:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment