diff options
-rw-r--r-- | tools/perf/util/probe-event.c | 140 |
1 files changed, 124 insertions, 16 deletions
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 1c570c2fa7cc..b8f45782126a 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -178,6 +178,25 @@ static struct map *kernel_get_module_map(const char *module) | |||
178 | return NULL; | 178 | return NULL; |
179 | } | 179 | } |
180 | 180 | ||
181 | static struct map *get_target_map(const char *target, bool user) | ||
182 | { | ||
183 | /* Init maps of given executable or kernel */ | ||
184 | if (user) | ||
185 | return dso__new_map(target); | ||
186 | else | ||
187 | return kernel_get_module_map(target); | ||
188 | } | ||
189 | |||
190 | static void put_target_map(struct map *map, bool user) | ||
191 | { | ||
192 | if (map && user) { | ||
193 | /* Only the user map needs to be released */ | ||
194 | dso__delete(map->dso); | ||
195 | map__delete(map); | ||
196 | } | ||
197 | } | ||
198 | |||
199 | |||
181 | static struct dso *kernel_get_module_dso(const char *module) | 200 | static struct dso *kernel_get_module_dso(const char *module) |
182 | { | 201 | { |
183 | struct dso *dso; | 202 | struct dso *dso; |
@@ -249,6 +268,13 @@ out: | |||
249 | return ret; | 268 | return ret; |
250 | } | 269 | } |
251 | 270 | ||
271 | static void clear_perf_probe_point(struct perf_probe_point *pp) | ||
272 | { | ||
273 | free(pp->file); | ||
274 | free(pp->function); | ||
275 | free(pp->lazy_line); | ||
276 | } | ||
277 | |||
252 | static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs) | 278 | static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs) |
253 | { | 279 | { |
254 | int i; | 280 | int i; |
@@ -258,6 +284,74 @@ static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs) | |||
258 | } | 284 | } |
259 | 285 | ||
260 | #ifdef HAVE_DWARF_SUPPORT | 286 | #ifdef HAVE_DWARF_SUPPORT |
287 | /* | ||
288 | * Some binaries like glibc have special symbols which are on the symbol | ||
289 | * table, but not in the debuginfo. If we can find the address of the | ||
290 | * symbol from map, we can translate the address back to the probe point. | ||
291 | */ | ||
292 | static int find_alternative_probe_point(struct debuginfo *dinfo, | ||
293 | struct perf_probe_point *pp, | ||
294 | struct perf_probe_point *result, | ||
295 | const char *target, bool uprobes) | ||
296 | { | ||
297 | struct map *map = NULL; | ||
298 | struct symbol *sym; | ||
299 | u64 address = 0; | ||
300 | int ret = -ENOENT; | ||
301 | |||
302 | /* This can work only for function-name based one */ | ||
303 | if (!pp->function || pp->file) | ||
304 | return -ENOTSUP; | ||
305 | |||
306 | map = get_target_map(target, uprobes); | ||
307 | if (!map) | ||
308 | return -EINVAL; | ||
309 | |||
310 | /* Find the address of given function */ | ||
311 | map__for_each_symbol_by_name(map, pp->function, sym) { | ||
312 | if (sym->binding == STB_GLOBAL || sym->binding == STB_LOCAL) { | ||
313 | address = sym->start; | ||
314 | break; | ||
315 | } | ||
316 | } | ||
317 | if (!address) { | ||
318 | ret = -ENOENT; | ||
319 | goto out; | ||
320 | } | ||
321 | pr_debug("Symbol %s address found : %lx\n", pp->function, address); | ||
322 | |||
323 | ret = debuginfo__find_probe_point(dinfo, (unsigned long)address, | ||
324 | result); | ||
325 | if (ret <= 0) | ||
326 | ret = (!ret) ? -ENOENT : ret; | ||
327 | else { | ||
328 | result->offset += pp->offset; | ||
329 | result->line += pp->line; | ||
330 | ret = 0; | ||
331 | } | ||
332 | |||
333 | out: | ||
334 | put_target_map(map, uprobes); | ||
335 | return ret; | ||
336 | |||
337 | } | ||
338 | |||
339 | static int get_alternative_probe_event(struct debuginfo *dinfo, | ||
340 | struct perf_probe_event *pev, | ||
341 | struct perf_probe_point *tmp, | ||
342 | const char *target) | ||
343 | { | ||
344 | int ret; | ||
345 | |||
346 | memcpy(tmp, &pev->point, sizeof(*tmp)); | ||
347 | memset(&pev->point, 0, sizeof(pev->point)); | ||
348 | ret = find_alternative_probe_point(dinfo, tmp, &pev->point, | ||
349 | target, pev->uprobes); | ||
350 | if (ret < 0) | ||
351 | memcpy(&pev->point, tmp, sizeof(*tmp)); | ||
352 | |||
353 | return ret; | ||
354 | } | ||
261 | 355 | ||
262 | /* Open new debuginfo of given module */ | 356 | /* Open new debuginfo of given module */ |
263 | static struct debuginfo *open_debuginfo(const char *module, bool silent) | 357 | static struct debuginfo *open_debuginfo(const char *module, bool silent) |
@@ -466,6 +560,7 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | |||
466 | int max_tevs, const char *target) | 560 | int max_tevs, const char *target) |
467 | { | 561 | { |
468 | bool need_dwarf = perf_probe_event_need_dwarf(pev); | 562 | bool need_dwarf = perf_probe_event_need_dwarf(pev); |
563 | struct perf_probe_point tmp; | ||
469 | struct debuginfo *dinfo; | 564 | struct debuginfo *dinfo; |
470 | int ntevs, ret = 0; | 565 | int ntevs, ret = 0; |
471 | 566 | ||
@@ -482,6 +577,20 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | |||
482 | /* Searching trace events corresponding to a probe event */ | 577 | /* Searching trace events corresponding to a probe event */ |
483 | ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs); | 578 | ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs); |
484 | 579 | ||
580 | if (ntevs == 0) { /* Not found, retry with an alternative */ | ||
581 | ret = get_alternative_probe_event(dinfo, pev, &tmp, target); | ||
582 | if (!ret) { | ||
583 | ntevs = debuginfo__find_trace_events(dinfo, pev, | ||
584 | tevs, max_tevs); | ||
585 | /* | ||
586 | * Write back to the original probe_event for | ||
587 | * setting appropriate (user given) event name | ||
588 | */ | ||
589 | clear_perf_probe_point(&pev->point); | ||
590 | memcpy(&pev->point, &tmp, sizeof(tmp)); | ||
591 | } | ||
592 | } | ||
593 | |||
485 | debuginfo__delete(dinfo); | 594 | debuginfo__delete(dinfo); |
486 | 595 | ||
487 | if (ntevs > 0) { /* Succeeded to find trace events */ | 596 | if (ntevs > 0) { /* Succeeded to find trace events */ |
@@ -719,12 +828,13 @@ int show_line_range(struct line_range *lr, const char *module, bool user) | |||
719 | static int show_available_vars_at(struct debuginfo *dinfo, | 828 | static int show_available_vars_at(struct debuginfo *dinfo, |
720 | struct perf_probe_event *pev, | 829 | struct perf_probe_event *pev, |
721 | int max_vls, struct strfilter *_filter, | 830 | int max_vls, struct strfilter *_filter, |
722 | bool externs) | 831 | bool externs, const char *target) |
723 | { | 832 | { |
724 | char *buf; | 833 | char *buf; |
725 | int ret, i, nvars; | 834 | int ret, i, nvars; |
726 | struct str_node *node; | 835 | struct str_node *node; |
727 | struct variable_list *vls = NULL, *vl; | 836 | struct variable_list *vls = NULL, *vl; |
837 | struct perf_probe_point tmp; | ||
728 | const char *var; | 838 | const char *var; |
729 | 839 | ||
730 | buf = synthesize_perf_probe_point(&pev->point); | 840 | buf = synthesize_perf_probe_point(&pev->point); |
@@ -734,6 +844,15 @@ static int show_available_vars_at(struct debuginfo *dinfo, | |||
734 | 844 | ||
735 | ret = debuginfo__find_available_vars_at(dinfo, pev, &vls, | 845 | ret = debuginfo__find_available_vars_at(dinfo, pev, &vls, |
736 | max_vls, externs); | 846 | max_vls, externs); |
847 | if (!ret) { /* Not found, retry with an alternative */ | ||
848 | ret = get_alternative_probe_event(dinfo, pev, &tmp, target); | ||
849 | if (!ret) { | ||
850 | ret = debuginfo__find_available_vars_at(dinfo, pev, | ||
851 | &vls, max_vls, externs); | ||
852 | /* Release the old probe_point */ | ||
853 | clear_perf_probe_point(&tmp); | ||
854 | } | ||
855 | } | ||
737 | if (ret <= 0) { | 856 | if (ret <= 0) { |
738 | if (ret == 0 || ret == -ENOENT) { | 857 | if (ret == 0 || ret == -ENOENT) { |
739 | pr_err("Failed to find the address of %s\n", buf); | 858 | pr_err("Failed to find the address of %s\n", buf); |
@@ -796,7 +915,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, | |||
796 | 915 | ||
797 | for (i = 0; i < npevs && ret >= 0; i++) | 916 | for (i = 0; i < npevs && ret >= 0; i++) |
798 | ret = show_available_vars_at(dinfo, &pevs[i], max_vls, _filter, | 917 | ret = show_available_vars_at(dinfo, &pevs[i], max_vls, _filter, |
799 | externs); | 918 | externs, module); |
800 | 919 | ||
801 | debuginfo__delete(dinfo); | 920 | debuginfo__delete(dinfo); |
802 | out: | 921 | out: |
@@ -1742,15 +1861,12 @@ static int convert_to_perf_probe_event(struct probe_trace_event *tev, | |||
1742 | 1861 | ||
1743 | void clear_perf_probe_event(struct perf_probe_event *pev) | 1862 | void clear_perf_probe_event(struct perf_probe_event *pev) |
1744 | { | 1863 | { |
1745 | struct perf_probe_point *pp = &pev->point; | ||
1746 | struct perf_probe_arg_field *field, *next; | 1864 | struct perf_probe_arg_field *field, *next; |
1747 | int i; | 1865 | int i; |
1748 | 1866 | ||
1749 | free(pev->event); | 1867 | free(pev->event); |
1750 | free(pev->group); | 1868 | free(pev->group); |
1751 | free(pp->file); | 1869 | clear_perf_probe_point(&pev->point); |
1752 | free(pp->function); | ||
1753 | free(pp->lazy_line); | ||
1754 | 1870 | ||
1755 | for (i = 0; i < pev->nargs; i++) { | 1871 | for (i = 0; i < pev->nargs; i++) { |
1756 | free(pev->args[i].name); | 1872 | free(pev->args[i].name); |
@@ -2367,11 +2483,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, | |||
2367 | int num_matched_functions; | 2483 | int num_matched_functions; |
2368 | int ret, i; | 2484 | int ret, i; |
2369 | 2485 | ||
2370 | /* Init maps of given executable or kernel */ | 2486 | map = get_target_map(target, pev->uprobes); |
2371 | if (pev->uprobes) | ||
2372 | map = dso__new_map(target); | ||
2373 | else | ||
2374 | map = kernel_get_module_map(target); | ||
2375 | if (!map) { | 2487 | if (!map) { |
2376 | ret = -EINVAL; | 2488 | ret = -EINVAL; |
2377 | goto out; | 2489 | goto out; |
@@ -2464,11 +2576,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, | |||
2464 | } | 2576 | } |
2465 | 2577 | ||
2466 | out: | 2578 | out: |
2467 | if (map && pev->uprobes) { | 2579 | put_target_map(map, pev->uprobes); |
2468 | /* Only when using uprobe(exec) map needs to be released */ | ||
2469 | dso__delete(map->dso); | ||
2470 | map__delete(map); | ||
2471 | } | ||
2472 | return ret; | 2580 | return ret; |
2473 | 2581 | ||
2474 | nomem_out: | 2582 | nomem_out: |