diff options
Diffstat (limited to 'tools/perf/util/probe-event.c')
-rw-r--r-- | tools/perf/util/probe-event.c | 180 |
1 files changed, 135 insertions, 45 deletions
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 128aaab0aeda..f0223166e761 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <string.h> | 31 | #include <string.h> |
32 | #include <stdarg.h> | 32 | #include <stdarg.h> |
33 | #include <limits.h> | 33 | #include <limits.h> |
34 | #include <elf.h> | ||
34 | 35 | ||
35 | #undef _GNU_SOURCE | 36 | #undef _GNU_SOURCE |
36 | #include "util.h" | 37 | #include "util.h" |
@@ -111,7 +112,25 @@ static struct symbol *__find_kernel_function_by_name(const char *name, | |||
111 | NULL); | 112 | NULL); |
112 | } | 113 | } |
113 | 114 | ||
114 | const char *kernel_get_module_path(const char *module) | 115 | static struct map *kernel_get_module_map(const char *module) |
116 | { | ||
117 | struct rb_node *nd; | ||
118 | struct map_groups *grp = &machine.kmaps; | ||
119 | |||
120 | if (!module) | ||
121 | module = "kernel"; | ||
122 | |||
123 | for (nd = rb_first(&grp->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { | ||
124 | struct map *pos = rb_entry(nd, struct map, rb_node); | ||
125 | if (strncmp(pos->dso->short_name + 1, module, | ||
126 | pos->dso->short_name_len - 2) == 0) { | ||
127 | return pos; | ||
128 | } | ||
129 | } | ||
130 | return NULL; | ||
131 | } | ||
132 | |||
133 | static struct dso *kernel_get_module_dso(const char *module) | ||
115 | { | 134 | { |
116 | struct dso *dso; | 135 | struct dso *dso; |
117 | struct map *map; | 136 | struct map *map; |
@@ -141,7 +160,13 @@ const char *kernel_get_module_path(const char *module) | |||
141 | } | 160 | } |
142 | } | 161 | } |
143 | found: | 162 | found: |
144 | return dso->long_name; | 163 | return dso; |
164 | } | ||
165 | |||
166 | const char *kernel_get_module_path(const char *module) | ||
167 | { | ||
168 | struct dso *dso = kernel_get_module_dso(module); | ||
169 | return (dso) ? dso->long_name : NULL; | ||
145 | } | 170 | } |
146 | 171 | ||
147 | #ifdef DWARF_SUPPORT | 172 | #ifdef DWARF_SUPPORT |
@@ -172,7 +197,7 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | |||
172 | sym = __find_kernel_function_by_name(tp->symbol, &map); | 197 | sym = __find_kernel_function_by_name(tp->symbol, &map); |
173 | if (sym) { | 198 | if (sym) { |
174 | addr = map->unmap_ip(map, sym->start + tp->offset); | 199 | addr = map->unmap_ip(map, sym->start + tp->offset); |
175 | pr_debug("try to find %s+%ld@%llx\n", tp->symbol, | 200 | pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol, |
176 | tp->offset, addr); | 201 | tp->offset, addr); |
177 | ret = find_perf_probe_point((unsigned long)addr, pp); | 202 | ret = find_perf_probe_point((unsigned long)addr, pp); |
178 | } | 203 | } |
@@ -209,7 +234,6 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | |||
209 | 234 | ||
210 | /* Searching trace events corresponding to probe event */ | 235 | /* Searching trace events corresponding to probe event */ |
211 | ntevs = find_probe_trace_events(fd, pev, tevs, max_tevs); | 236 | ntevs = find_probe_trace_events(fd, pev, tevs, max_tevs); |
212 | close(fd); | ||
213 | 237 | ||
214 | if (ntevs > 0) { /* Succeeded to find trace events */ | 238 | if (ntevs > 0) { /* Succeeded to find trace events */ |
215 | pr_debug("find %d probe_trace_events.\n", ntevs); | 239 | pr_debug("find %d probe_trace_events.\n", ntevs); |
@@ -363,7 +387,6 @@ int show_line_range(struct line_range *lr, const char *module) | |||
363 | } | 387 | } |
364 | 388 | ||
365 | ret = find_line_range(fd, lr); | 389 | ret = find_line_range(fd, lr); |
366 | close(fd); | ||
367 | if (ret == 0) { | 390 | if (ret == 0) { |
368 | pr_warning("Specified source line is not found.\n"); | 391 | pr_warning("Specified source line is not found.\n"); |
369 | return -ENOENT; | 392 | return -ENOENT; |
@@ -384,7 +407,7 @@ int show_line_range(struct line_range *lr, const char *module) | |||
384 | setup_pager(); | 407 | setup_pager(); |
385 | 408 | ||
386 | if (lr->function) | 409 | if (lr->function) |
387 | fprintf(stdout, "<%s:%d>\n", lr->function, | 410 | fprintf(stdout, "<%s@%s:%d>\n", lr->function, lr->path, |
388 | lr->start - lr->offset); | 411 | lr->start - lr->offset); |
389 | else | 412 | else |
390 | fprintf(stdout, "<%s:%d>\n", lr->path, lr->start); | 413 | fprintf(stdout, "<%s:%d>\n", lr->path, lr->start); |
@@ -426,12 +449,14 @@ end: | |||
426 | } | 449 | } |
427 | 450 | ||
428 | static int show_available_vars_at(int fd, struct perf_probe_event *pev, | 451 | static int show_available_vars_at(int fd, struct perf_probe_event *pev, |
429 | int max_vls, bool externs) | 452 | int max_vls, struct strfilter *_filter, |
453 | bool externs) | ||
430 | { | 454 | { |
431 | char *buf; | 455 | char *buf; |
432 | int ret, i; | 456 | int ret, i, nvars; |
433 | struct str_node *node; | 457 | struct str_node *node; |
434 | struct variable_list *vls = NULL, *vl; | 458 | struct variable_list *vls = NULL, *vl; |
459 | const char *var; | ||
435 | 460 | ||
436 | buf = synthesize_perf_probe_point(&pev->point); | 461 | buf = synthesize_perf_probe_point(&pev->point); |
437 | if (!buf) | 462 | if (!buf) |
@@ -439,36 +464,45 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev, | |||
439 | pr_debug("Searching variables at %s\n", buf); | 464 | pr_debug("Searching variables at %s\n", buf); |
440 | 465 | ||
441 | ret = find_available_vars_at(fd, pev, &vls, max_vls, externs); | 466 | ret = find_available_vars_at(fd, pev, &vls, max_vls, externs); |
442 | if (ret > 0) { | 467 | if (ret <= 0) { |
443 | /* Some variables were found */ | 468 | pr_err("Failed to find variables at %s (%d)\n", buf, ret); |
444 | fprintf(stdout, "Available variables at %s\n", buf); | 469 | goto end; |
445 | for (i = 0; i < ret; i++) { | 470 | } |
446 | vl = &vls[i]; | 471 | /* Some variables are found */ |
447 | /* | 472 | fprintf(stdout, "Available variables at %s\n", buf); |
448 | * A probe point might be converted to | 473 | for (i = 0; i < ret; i++) { |
449 | * several trace points. | 474 | vl = &vls[i]; |
450 | */ | 475 | /* |
451 | fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol, | 476 | * A probe point might be converted to |
452 | vl->point.offset); | 477 | * several trace points. |
453 | free(vl->point.symbol); | 478 | */ |
454 | if (vl->vars) { | 479 | fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol, |
455 | strlist__for_each(node, vl->vars) | 480 | vl->point.offset); |
481 | free(vl->point.symbol); | ||
482 | nvars = 0; | ||
483 | if (vl->vars) { | ||
484 | strlist__for_each(node, vl->vars) { | ||
485 | var = strchr(node->s, '\t') + 1; | ||
486 | if (strfilter__compare(_filter, var)) { | ||
456 | fprintf(stdout, "\t\t%s\n", node->s); | 487 | fprintf(stdout, "\t\t%s\n", node->s); |
457 | strlist__delete(vl->vars); | 488 | nvars++; |
458 | } else | 489 | } |
459 | fprintf(stdout, "(No variables)\n"); | 490 | } |
491 | strlist__delete(vl->vars); | ||
460 | } | 492 | } |
461 | free(vls); | 493 | if (nvars == 0) |
462 | } else | 494 | fprintf(stdout, "\t\t(No matched variables)\n"); |
463 | pr_err("Failed to find variables at %s (%d)\n", buf, ret); | 495 | } |
464 | 496 | free(vls); | |
497 | end: | ||
465 | free(buf); | 498 | free(buf); |
466 | return ret; | 499 | return ret; |
467 | } | 500 | } |
468 | 501 | ||
469 | /* Show available variables on given probe point */ | 502 | /* Show available variables on given probe point */ |
470 | int show_available_vars(struct perf_probe_event *pevs, int npevs, | 503 | int show_available_vars(struct perf_probe_event *pevs, int npevs, |
471 | int max_vls, const char *module, bool externs) | 504 | int max_vls, const char *module, |
505 | struct strfilter *_filter, bool externs) | ||
472 | { | 506 | { |
473 | int i, fd, ret = 0; | 507 | int i, fd, ret = 0; |
474 | 508 | ||
@@ -476,18 +510,18 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, | |||
476 | if (ret < 0) | 510 | if (ret < 0) |
477 | return ret; | 511 | return ret; |
478 | 512 | ||
479 | fd = open_vmlinux(module); | ||
480 | if (fd < 0) { | ||
481 | pr_warning("Failed to open debug information file.\n"); | ||
482 | return fd; | ||
483 | } | ||
484 | |||
485 | setup_pager(); | 513 | setup_pager(); |
486 | 514 | ||
487 | for (i = 0; i < npevs && ret >= 0; i++) | 515 | for (i = 0; i < npevs && ret >= 0; i++) { |
488 | ret = show_available_vars_at(fd, &pevs[i], max_vls, externs); | 516 | fd = open_vmlinux(module); |
489 | 517 | if (fd < 0) { | |
490 | close(fd); | 518 | pr_warning("Failed to open debug information file.\n"); |
519 | ret = fd; | ||
520 | break; | ||
521 | } | ||
522 | ret = show_available_vars_at(fd, &pevs[i], max_vls, _filter, | ||
523 | externs); | ||
524 | } | ||
491 | return ret; | 525 | return ret; |
492 | } | 526 | } |
493 | 527 | ||
@@ -531,7 +565,9 @@ int show_line_range(struct line_range *lr __unused, const char *module __unused) | |||
531 | 565 | ||
532 | int show_available_vars(struct perf_probe_event *pevs __unused, | 566 | int show_available_vars(struct perf_probe_event *pevs __unused, |
533 | int npevs __unused, int max_vls __unused, | 567 | int npevs __unused, int max_vls __unused, |
534 | const char *module __unused, bool externs __unused) | 568 | const char *module __unused, |
569 | struct strfilter *filter __unused, | ||
570 | bool externs __unused) | ||
535 | { | 571 | { |
536 | pr_warning("Debuginfo-analysis is not supported.\n"); | 572 | pr_warning("Debuginfo-analysis is not supported.\n"); |
537 | return -ENOSYS; | 573 | return -ENOSYS; |
@@ -556,11 +592,11 @@ static int parse_line_num(char **ptr, int *val, const char *what) | |||
556 | * The line range syntax is described by: | 592 | * The line range syntax is described by: |
557 | * | 593 | * |
558 | * SRC[:SLN[+NUM|-ELN]] | 594 | * SRC[:SLN[+NUM|-ELN]] |
559 | * FNC[:SLN[+NUM|-ELN]] | 595 | * FNC[@SRC][:SLN[+NUM|-ELN]] |
560 | */ | 596 | */ |
561 | int parse_line_range_desc(const char *arg, struct line_range *lr) | 597 | int parse_line_range_desc(const char *arg, struct line_range *lr) |
562 | { | 598 | { |
563 | char *range, *name = strdup(arg); | 599 | char *range, *file, *name = strdup(arg); |
564 | int err; | 600 | int err; |
565 | 601 | ||
566 | if (!name) | 602 | if (!name) |
@@ -610,7 +646,16 @@ int parse_line_range_desc(const char *arg, struct line_range *lr) | |||
610 | } | 646 | } |
611 | } | 647 | } |
612 | 648 | ||
613 | if (strchr(name, '.')) | 649 | file = strchr(name, '@'); |
650 | if (file) { | ||
651 | *file = '\0'; | ||
652 | lr->file = strdup(++file); | ||
653 | if (lr->file == NULL) { | ||
654 | err = -ENOMEM; | ||
655 | goto err; | ||
656 | } | ||
657 | lr->function = name; | ||
658 | } else if (strchr(name, '.')) | ||
614 | lr->file = name; | 659 | lr->file = name; |
615 | else | 660 | else |
616 | lr->function = name; | 661 | lr->function = name; |
@@ -1784,9 +1829,12 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | |||
1784 | } | 1829 | } |
1785 | 1830 | ||
1786 | /* Loop 2: add all events */ | 1831 | /* Loop 2: add all events */ |
1787 | for (i = 0; i < npevs && ret >= 0; i++) | 1832 | for (i = 0; i < npevs; i++) { |
1788 | ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, | 1833 | ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, |
1789 | pkgs[i].ntevs, force_add); | 1834 | pkgs[i].ntevs, force_add); |
1835 | if (ret < 0) | ||
1836 | break; | ||
1837 | } | ||
1790 | end: | 1838 | end: |
1791 | /* Loop 3: cleanup and free trace events */ | 1839 | /* Loop 3: cleanup and free trace events */ |
1792 | for (i = 0; i < npevs; i++) { | 1840 | for (i = 0; i < npevs; i++) { |
@@ -1912,4 +1960,46 @@ int del_perf_probe_events(struct strlist *dellist) | |||
1912 | 1960 | ||
1913 | return ret; | 1961 | return ret; |
1914 | } | 1962 | } |
1963 | /* TODO: don't use a global variable for filter ... */ | ||
1964 | static struct strfilter *available_func_filter; | ||
1965 | |||
1966 | /* | ||
1967 | * If a symbol corresponds to a function with global binding and | ||
1968 | * matches filter return 0. For all others return 1. | ||
1969 | */ | ||
1970 | static int filter_available_functions(struct map *map __unused, | ||
1971 | struct symbol *sym) | ||
1972 | { | ||
1973 | if (sym->binding == STB_GLOBAL && | ||
1974 | strfilter__compare(available_func_filter, sym->name)) | ||
1975 | return 0; | ||
1976 | return 1; | ||
1977 | } | ||
1915 | 1978 | ||
1979 | int show_available_funcs(const char *module, struct strfilter *_filter) | ||
1980 | { | ||
1981 | struct map *map; | ||
1982 | int ret; | ||
1983 | |||
1984 | setup_pager(); | ||
1985 | |||
1986 | ret = init_vmlinux(); | ||
1987 | if (ret < 0) | ||
1988 | return ret; | ||
1989 | |||
1990 | map = kernel_get_module_map(module); | ||
1991 | if (!map) { | ||
1992 | pr_err("Failed to find %s map.\n", (module) ? : "kernel"); | ||
1993 | return -EINVAL; | ||
1994 | } | ||
1995 | available_func_filter = _filter; | ||
1996 | if (map__load(map, filter_available_functions)) { | ||
1997 | pr_err("Failed to load map.\n"); | ||
1998 | return -EINVAL; | ||
1999 | } | ||
2000 | if (!dso__sorted_by_name(map->dso, map->type)) | ||
2001 | dso__sort_by_name(map->dso, map->type); | ||
2002 | |||
2003 | dso__fprintf_symbols_by_name(map->dso, map->type, stdout); | ||
2004 | return 0; | ||
2005 | } | ||