diff options
Diffstat (limited to 'tools/perf/util/probe-event.c')
| -rw-r--r-- | tools/perf/util/probe-event.c | 159 |
1 files changed, 126 insertions, 33 deletions
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 6e29d9c9dcc..5ddee66020a 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 |
| @@ -384,7 +409,7 @@ int show_line_range(struct line_range *lr, const char *module) | |||
| 384 | setup_pager(); | 409 | setup_pager(); |
| 385 | 410 | ||
| 386 | if (lr->function) | 411 | if (lr->function) |
| 387 | fprintf(stdout, "<%s:%d>\n", lr->function, | 412 | fprintf(stdout, "<%s@%s:%d>\n", lr->function, lr->path, |
| 388 | lr->start - lr->offset); | 413 | lr->start - lr->offset); |
| 389 | else | 414 | else |
| 390 | fprintf(stdout, "<%s:%d>\n", lr->path, lr->start); | 415 | fprintf(stdout, "<%s:%d>\n", lr->path, lr->start); |
| @@ -426,12 +451,14 @@ end: | |||
| 426 | } | 451 | } |
| 427 | 452 | ||
| 428 | static int show_available_vars_at(int fd, struct perf_probe_event *pev, | 453 | static int show_available_vars_at(int fd, struct perf_probe_event *pev, |
| 429 | int max_vls, bool externs) | 454 | int max_vls, struct strfilter *_filter, |
| 455 | bool externs) | ||
| 430 | { | 456 | { |
| 431 | char *buf; | 457 | char *buf; |
| 432 | int ret, i; | 458 | int ret, i, nvars; |
| 433 | struct str_node *node; | 459 | struct str_node *node; |
| 434 | struct variable_list *vls = NULL, *vl; | 460 | struct variable_list *vls = NULL, *vl; |
| 461 | const char *var; | ||
| 435 | 462 | ||
| 436 | buf = synthesize_perf_probe_point(&pev->point); | 463 | buf = synthesize_perf_probe_point(&pev->point); |
| 437 | if (!buf) | 464 | if (!buf) |
| @@ -439,36 +466,45 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev, | |||
| 439 | pr_debug("Searching variables at %s\n", buf); | 466 | pr_debug("Searching variables at %s\n", buf); |
| 440 | 467 | ||
| 441 | ret = find_available_vars_at(fd, pev, &vls, max_vls, externs); | 468 | ret = find_available_vars_at(fd, pev, &vls, max_vls, externs); |
| 442 | if (ret > 0) { | 469 | if (ret <= 0) { |
| 443 | /* Some variables were found */ | 470 | pr_err("Failed to find variables at %s (%d)\n", buf, ret); |
| 444 | fprintf(stdout, "Available variables at %s\n", buf); | 471 | goto end; |
| 445 | for (i = 0; i < ret; i++) { | 472 | } |
| 446 | vl = &vls[i]; | 473 | /* Some variables are found */ |
| 447 | /* | 474 | fprintf(stdout, "Available variables at %s\n", buf); |
| 448 | * A probe point might be converted to | 475 | for (i = 0; i < ret; i++) { |
| 449 | * several trace points. | 476 | vl = &vls[i]; |
| 450 | */ | 477 | /* |
| 451 | fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol, | 478 | * A probe point might be converted to |
| 452 | vl->point.offset); | 479 | * several trace points. |
| 453 | free(vl->point.symbol); | 480 | */ |
| 454 | if (vl->vars) { | 481 | fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol, |
| 455 | strlist__for_each(node, vl->vars) | 482 | vl->point.offset); |
| 483 | free(vl->point.symbol); | ||
| 484 | nvars = 0; | ||
| 485 | if (vl->vars) { | ||
| 486 | strlist__for_each(node, vl->vars) { | ||
| 487 | var = strchr(node->s, '\t') + 1; | ||
| 488 | if (strfilter__compare(_filter, var)) { | ||
| 456 | fprintf(stdout, "\t\t%s\n", node->s); | 489 | fprintf(stdout, "\t\t%s\n", node->s); |
| 457 | strlist__delete(vl->vars); | 490 | nvars++; |
| 458 | } else | 491 | } |
| 459 | fprintf(stdout, "(No variables)\n"); | 492 | } |
| 493 | strlist__delete(vl->vars); | ||
| 460 | } | 494 | } |
| 461 | free(vls); | 495 | if (nvars == 0) |
| 462 | } else | 496 | fprintf(stdout, "\t\t(No matched variables)\n"); |
| 463 | pr_err("Failed to find variables at %s (%d)\n", buf, ret); | 497 | } |
| 464 | 498 | free(vls); | |
| 499 | end: | ||
| 465 | free(buf); | 500 | free(buf); |
| 466 | return ret; | 501 | return ret; |
| 467 | } | 502 | } |
| 468 | 503 | ||
| 469 | /* Show available variables on given probe point */ | 504 | /* Show available variables on given probe point */ |
| 470 | int show_available_vars(struct perf_probe_event *pevs, int npevs, | 505 | int show_available_vars(struct perf_probe_event *pevs, int npevs, |
| 471 | int max_vls, const char *module, bool externs) | 506 | int max_vls, const char *module, |
| 507 | struct strfilter *_filter, bool externs) | ||
| 472 | { | 508 | { |
| 473 | int i, fd, ret = 0; | 509 | int i, fd, ret = 0; |
| 474 | 510 | ||
| @@ -485,7 +521,8 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, | |||
| 485 | setup_pager(); | 521 | setup_pager(); |
| 486 | 522 | ||
| 487 | for (i = 0; i < npevs && ret >= 0; i++) | 523 | for (i = 0; i < npevs && ret >= 0; i++) |
| 488 | ret = show_available_vars_at(fd, &pevs[i], max_vls, externs); | 524 | ret = show_available_vars_at(fd, &pevs[i], max_vls, _filter, |
| 525 | externs); | ||
| 489 | 526 | ||
| 490 | close(fd); | 527 | close(fd); |
| 491 | return ret; | 528 | return ret; |
| @@ -531,7 +568,9 @@ int show_line_range(struct line_range *lr __unused, const char *module __unused) | |||
| 531 | 568 | ||
| 532 | int show_available_vars(struct perf_probe_event *pevs __unused, | 569 | int show_available_vars(struct perf_probe_event *pevs __unused, |
| 533 | int npevs __unused, int max_vls __unused, | 570 | int npevs __unused, int max_vls __unused, |
| 534 | const char *module __unused, bool externs __unused) | 571 | const char *module __unused, |
| 572 | struct strfilter *filter __unused, | ||
| 573 | bool externs __unused) | ||
| 535 | { | 574 | { |
| 536 | pr_warning("Debuginfo-analysis is not supported.\n"); | 575 | pr_warning("Debuginfo-analysis is not supported.\n"); |
| 537 | return -ENOSYS; | 576 | return -ENOSYS; |
| @@ -556,11 +595,11 @@ static int parse_line_num(char **ptr, int *val, const char *what) | |||
| 556 | * The line range syntax is described by: | 595 | * The line range syntax is described by: |
| 557 | * | 596 | * |
| 558 | * SRC[:SLN[+NUM|-ELN]] | 597 | * SRC[:SLN[+NUM|-ELN]] |
| 559 | * FNC[:SLN[+NUM|-ELN]] | 598 | * FNC[@SRC][:SLN[+NUM|-ELN]] |
| 560 | */ | 599 | */ |
| 561 | int parse_line_range_desc(const char *arg, struct line_range *lr) | 600 | int parse_line_range_desc(const char *arg, struct line_range *lr) |
| 562 | { | 601 | { |
| 563 | char *range, *name = strdup(arg); | 602 | char *range, *file, *name = strdup(arg); |
| 564 | int err; | 603 | int err; |
| 565 | 604 | ||
| 566 | if (!name) | 605 | if (!name) |
| @@ -610,7 +649,16 @@ int parse_line_range_desc(const char *arg, struct line_range *lr) | |||
| 610 | } | 649 | } |
| 611 | } | 650 | } |
| 612 | 651 | ||
| 613 | if (strchr(name, '.')) | 652 | file = strchr(name, '@'); |
| 653 | if (file) { | ||
| 654 | *file = '\0'; | ||
| 655 | lr->file = strdup(++file); | ||
| 656 | if (lr->file == NULL) { | ||
| 657 | err = -ENOMEM; | ||
| 658 | goto err; | ||
| 659 | } | ||
| 660 | lr->function = name; | ||
| 661 | } else if (strchr(name, '.')) | ||
| 614 | lr->file = name; | 662 | lr->file = name; |
| 615 | else | 663 | else |
| 616 | lr->function = name; | 664 | lr->function = name; |
| @@ -1784,9 +1832,12 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | |||
| 1784 | } | 1832 | } |
| 1785 | 1833 | ||
| 1786 | /* Loop 2: add all events */ | 1834 | /* Loop 2: add all events */ |
| 1787 | for (i = 0; i < npevs && ret >= 0; i++) | 1835 | for (i = 0; i < npevs; i++) { |
| 1788 | ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, | 1836 | ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, |
| 1789 | pkgs[i].ntevs, force_add); | 1837 | pkgs[i].ntevs, force_add); |
| 1838 | if (ret < 0) | ||
| 1839 | break; | ||
| 1840 | } | ||
| 1790 | end: | 1841 | end: |
| 1791 | /* Loop 3: cleanup and free trace events */ | 1842 | /* Loop 3: cleanup and free trace events */ |
| 1792 | for (i = 0; i < npevs; i++) { | 1843 | for (i = 0; i < npevs; i++) { |
| @@ -1912,4 +1963,46 @@ int del_perf_probe_events(struct strlist *dellist) | |||
| 1912 | 1963 | ||
| 1913 | return ret; | 1964 | return ret; |
| 1914 | } | 1965 | } |
| 1966 | /* TODO: don't use a global variable for filter ... */ | ||
| 1967 | static struct strfilter *available_func_filter; | ||
| 1968 | |||
| 1969 | /* | ||
| 1970 | * If a symbol corresponds to a function with global binding and | ||
| 1971 | * matches filter return 0. For all others return 1. | ||
| 1972 | */ | ||
| 1973 | static int filter_available_functions(struct map *map __unused, | ||
| 1974 | struct symbol *sym) | ||
| 1975 | { | ||
| 1976 | if (sym->binding == STB_GLOBAL && | ||
| 1977 | strfilter__compare(available_func_filter, sym->name)) | ||
| 1978 | return 0; | ||
| 1979 | return 1; | ||
| 1980 | } | ||
| 1981 | |||
| 1982 | int show_available_funcs(const char *module, struct strfilter *_filter) | ||
| 1983 | { | ||
| 1984 | struct map *map; | ||
| 1985 | int ret; | ||
| 1986 | |||
| 1987 | setup_pager(); | ||
| 1988 | |||
| 1989 | ret = init_vmlinux(); | ||
| 1990 | if (ret < 0) | ||
| 1991 | return ret; | ||
| 1915 | 1992 | ||
| 1993 | map = kernel_get_module_map(module); | ||
| 1994 | if (!map) { | ||
| 1995 | pr_err("Failed to find %s map.\n", (module) ? : "kernel"); | ||
| 1996 | return -EINVAL; | ||
| 1997 | } | ||
| 1998 | available_func_filter = _filter; | ||
| 1999 | if (map__load(map, filter_available_functions)) { | ||
| 2000 | pr_err("Failed to load map.\n"); | ||
| 2001 | return -EINVAL; | ||
| 2002 | } | ||
| 2003 | if (!dso__sorted_by_name(map->dso, map->type)) | ||
| 2004 | dso__sort_by_name(map->dso, map->type); | ||
| 2005 | |||
| 2006 | dso__fprintf_symbols_by_name(map->dso, map->type, stdout); | ||
| 2007 | return 0; | ||
| 2008 | } | ||
