diff options
author | Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | 2016-07-01 04:04:10 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2016-07-04 18:39:00 -0400 |
commit | 6430a94ead2a4c8f350441351a735303eb6d1c8a (patch) | |
tree | ab1b6fc072549f519d8a15dca3c39a8ed0cad8ca | |
parent | 8d993d96901f55d26e083390aae80fd02cbff7aa (diff) |
perf buildid-cache: Scan and import user SDT events to probe cache
perf buildid-cache --add <binary> scans given binary and add
the SDT events to probe cache. "sdt_" prefix is appended for
all SDT providers to avoid event-name clash with other pre-defined
events. It is possible to use the cached SDT events as other cached
events, via perf probe --add "sdt_<provider>:<event>=<event>".
e.g.
----
# perf buildid-cache --add /lib/libc-2.17.so
# perf probe --cache --list | head -n 5
/usr/lib/libc-2.17.so (a6fb821bdf53660eb2c29f778757aef294d3d392):
sdt_libc:setjmp=setjmp
sdt_libc:longjmp=longjmp
sdt_libc:longjmp_target=longjmp_target
sdt_libc:memory_heap_new=memory_heap_new
# perf probe -x /usr/lib/libc-2.17.so \
-a sdt_libc:memory_heap_new=memory_heap_new
Added new event:
sdt_libc:memory_heap_new (on memory_heap_new
in /usr/lib/libc-2.17.so)
You can now use it in all perf tools, such as:
perf record -e sdt_libc:memory_heap_new -aR sleep 1
# perf probe -l
sdt_libc:memory_heap_new (on new_heap+183 in /usr/lib/libc-2.17.so)
----
Note that SDT event entries in probe-cache file is somewhat different
from normal cached events. Normal one starts with "#", but SDTs are
starting with "%".
Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ananth N Mavinakayanahalli <ananth@linux.vnet.ibm.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Hemant Kumar <hemant@linux.vnet.ibm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/146736025058.27797.13043265488541434502.stgit@devbox
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/perf/Documentation/perf-buildid-cache.txt | 3 | ||||
-rw-r--r-- | tools/perf/util/build-id.c | 30 | ||||
-rw-r--r-- | tools/perf/util/probe-file.c | 69 | ||||
-rw-r--r-- | tools/perf/util/probe-file.h | 2 |
4 files changed, 101 insertions, 3 deletions
diff --git a/tools/perf/Documentation/perf-buildid-cache.txt b/tools/perf/Documentation/perf-buildid-cache.txt index dd07b55f58d8..058064db39d2 100644 --- a/tools/perf/Documentation/perf-buildid-cache.txt +++ b/tools/perf/Documentation/perf-buildid-cache.txt | |||
@@ -15,6 +15,9 @@ DESCRIPTION | |||
15 | This command manages the build-id cache. It can add, remove, update and purge | 15 | This command manages the build-id cache. It can add, remove, update and purge |
16 | files to/from the cache. In the future it should as well set upper limits for | 16 | files to/from the cache. In the future it should as well set upper limits for |
17 | the space used by the cache, etc. | 17 | the space used by the cache, etc. |
18 | This also scans the target binary for SDT (Statically Defined Tracing) and | ||
19 | record it along with the buildid-cache, which will be used by perf-probe. | ||
20 | For more details, see linkperf:perf-probe[1]. | ||
18 | 21 | ||
19 | OPTIONS | 22 | OPTIONS |
20 | ------- | 23 | ------- |
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 1c49620e98b2..e1a16408da9c 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include "tool.h" | 17 | #include "tool.h" |
18 | #include "header.h" | 18 | #include "header.h" |
19 | #include "vdso.h" | 19 | #include "vdso.h" |
20 | #include "probe-file.h" | ||
20 | 21 | ||
21 | 22 | ||
22 | static bool no_buildid_cache; | 23 | static bool no_buildid_cache; |
@@ -532,6 +533,30 @@ int build_id_cache__list_build_ids(const char *pathname, | |||
532 | return ret; | 533 | return ret; |
533 | } | 534 | } |
534 | 535 | ||
536 | #ifdef HAVE_LIBELF_SUPPORT | ||
537 | static int build_id_cache__add_sdt_cache(const char *sbuild_id, | ||
538 | const char *realname) | ||
539 | { | ||
540 | struct probe_cache *cache; | ||
541 | int ret; | ||
542 | |||
543 | cache = probe_cache__new(sbuild_id); | ||
544 | if (!cache) | ||
545 | return -1; | ||
546 | |||
547 | ret = probe_cache__scan_sdt(cache, realname); | ||
548 | if (ret >= 0) { | ||
549 | pr_debug("Found %d SDTs in %s\n", ret, realname); | ||
550 | if (probe_cache__commit(cache) < 0) | ||
551 | ret = -1; | ||
552 | } | ||
553 | probe_cache__delete(cache); | ||
554 | return ret; | ||
555 | } | ||
556 | #else | ||
557 | #define build_id_cache__add_sdt_cache(sbuild_id, realname) (0) | ||
558 | #endif | ||
559 | |||
535 | int build_id_cache__add_s(const char *sbuild_id, const char *name, | 560 | int build_id_cache__add_s(const char *sbuild_id, const char *name, |
536 | bool is_kallsyms, bool is_vdso) | 561 | bool is_kallsyms, bool is_vdso) |
537 | { | 562 | { |
@@ -589,6 +614,11 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name, | |||
589 | 614 | ||
590 | if (symlink(tmp, linkname) == 0) | 615 | if (symlink(tmp, linkname) == 0) |
591 | err = 0; | 616 | err = 0; |
617 | |||
618 | /* Update SDT cache : error is just warned */ | ||
619 | if (build_id_cache__add_sdt_cache(sbuild_id, realname) < 0) | ||
620 | pr_debug("Failed to update/scan SDT cache for %s\n", realname); | ||
621 | |||
592 | out_free: | 622 | out_free: |
593 | if (!is_kallsyms) | 623 | if (!is_kallsyms) |
594 | free(realname); | 624 | free(realname); |
diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c index 6cb6ec03c1fe..5b563b2e8b1d 100644 --- a/tools/perf/util/probe-file.c +++ b/tools/perf/util/probe-file.c | |||
@@ -434,12 +434,15 @@ static int probe_cache__load(struct probe_cache *pcache) | |||
434 | p = strchr(buf, '\n'); | 434 | p = strchr(buf, '\n'); |
435 | if (p) | 435 | if (p) |
436 | *p = '\0'; | 436 | *p = '\0'; |
437 | if (buf[0] == '#') { /* #perf_probe_event */ | 437 | /* #perf_probe_event or %sdt_event */ |
438 | if (buf[0] == '#' || buf[0] == '%') { | ||
438 | entry = probe_cache_entry__new(NULL); | 439 | entry = probe_cache_entry__new(NULL); |
439 | if (!entry) { | 440 | if (!entry) { |
440 | ret = -ENOMEM; | 441 | ret = -ENOMEM; |
441 | goto out; | 442 | goto out; |
442 | } | 443 | } |
444 | if (buf[0] == '%') | ||
445 | entry->sdt = true; | ||
443 | entry->spev = strdup(buf + 1); | 446 | entry->spev = strdup(buf + 1); |
444 | if (entry->spev) | 447 | if (entry->spev) |
445 | ret = parse_perf_probe_command(buf + 1, | 448 | ret = parse_perf_probe_command(buf + 1, |
@@ -621,19 +624,79 @@ out_err: | |||
621 | return ret; | 624 | return ret; |
622 | } | 625 | } |
623 | 626 | ||
627 | static unsigned long long sdt_note__get_addr(struct sdt_note *note) | ||
628 | { | ||
629 | return note->bit32 ? (unsigned long long)note->addr.a32[0] | ||
630 | : (unsigned long long)note->addr.a64[0]; | ||
631 | } | ||
632 | |||
633 | int probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname) | ||
634 | { | ||
635 | struct probe_cache_entry *entry = NULL; | ||
636 | struct list_head sdtlist; | ||
637 | struct sdt_note *note; | ||
638 | char *buf; | ||
639 | char sdtgrp[64]; | ||
640 | int ret; | ||
641 | |||
642 | INIT_LIST_HEAD(&sdtlist); | ||
643 | ret = get_sdt_note_list(&sdtlist, pathname); | ||
644 | if (ret < 0) { | ||
645 | pr_debug("Failed to get sdt note: %d\n", ret); | ||
646 | return ret; | ||
647 | } | ||
648 | list_for_each_entry(note, &sdtlist, note_list) { | ||
649 | ret = snprintf(sdtgrp, 64, "sdt_%s", note->provider); | ||
650 | if (ret < 0) | ||
651 | break; | ||
652 | /* Try to find same-name entry */ | ||
653 | entry = probe_cache__find_by_name(pcache, sdtgrp, note->name); | ||
654 | if (!entry) { | ||
655 | entry = probe_cache_entry__new(NULL); | ||
656 | if (!entry) { | ||
657 | ret = -ENOMEM; | ||
658 | break; | ||
659 | } | ||
660 | entry->sdt = true; | ||
661 | ret = asprintf(&entry->spev, "%s:%s=%s", sdtgrp, | ||
662 | note->name, note->name); | ||
663 | if (ret < 0) | ||
664 | break; | ||
665 | entry->pev.event = strdup(note->name); | ||
666 | entry->pev.group = strdup(sdtgrp); | ||
667 | list_add_tail(&entry->node, &pcache->entries); | ||
668 | } | ||
669 | ret = asprintf(&buf, "p:%s/%s %s:0x%llx", | ||
670 | sdtgrp, note->name, pathname, | ||
671 | sdt_note__get_addr(note)); | ||
672 | if (ret < 0) | ||
673 | break; | ||
674 | strlist__add(entry->tevlist, buf); | ||
675 | free(buf); | ||
676 | entry = NULL; | ||
677 | } | ||
678 | if (entry) { | ||
679 | list_del_init(&entry->node); | ||
680 | probe_cache_entry__delete(entry); | ||
681 | } | ||
682 | cleanup_sdt_note_list(&sdtlist); | ||
683 | return ret; | ||
684 | } | ||
685 | |||
624 | static int probe_cache_entry__write(struct probe_cache_entry *entry, int fd) | 686 | static int probe_cache_entry__write(struct probe_cache_entry *entry, int fd) |
625 | { | 687 | { |
626 | struct str_node *snode; | 688 | struct str_node *snode; |
627 | struct stat st; | 689 | struct stat st; |
628 | struct iovec iov[3]; | 690 | struct iovec iov[3]; |
691 | const char *prefix = entry->sdt ? "%" : "#"; | ||
629 | int ret; | 692 | int ret; |
630 | /* Save stat for rollback */ | 693 | /* Save stat for rollback */ |
631 | ret = fstat(fd, &st); | 694 | ret = fstat(fd, &st); |
632 | if (ret < 0) | 695 | if (ret < 0) |
633 | return ret; | 696 | return ret; |
634 | 697 | ||
635 | pr_debug("Writing cache: #%s\n", entry->spev); | 698 | pr_debug("Writing cache: %s%s\n", prefix, entry->spev); |
636 | iov[0].iov_base = (void *)"#"; iov[0].iov_len = 1; | 699 | iov[0].iov_base = (void *)prefix; iov[0].iov_len = 1; |
637 | iov[1].iov_base = entry->spev; iov[1].iov_len = strlen(entry->spev); | 700 | iov[1].iov_base = entry->spev; iov[1].iov_len = strlen(entry->spev); |
638 | iov[2].iov_base = (void *)"\n"; iov[2].iov_len = 1; | 701 | iov[2].iov_base = (void *)"\n"; iov[2].iov_len = 1; |
639 | ret = writev(fd, iov, 3); | 702 | ret = writev(fd, iov, 3); |
diff --git a/tools/perf/util/probe-file.h b/tools/perf/util/probe-file.h index 0ed1fc563b77..ddf5ae212c2f 100644 --- a/tools/perf/util/probe-file.h +++ b/tools/perf/util/probe-file.h | |||
@@ -8,6 +8,7 @@ | |||
8 | /* Cache of probe definitions */ | 8 | /* Cache of probe definitions */ |
9 | struct probe_cache_entry { | 9 | struct probe_cache_entry { |
10 | struct list_head node; | 10 | struct list_head node; |
11 | bool sdt; | ||
11 | struct perf_probe_event pev; | 12 | struct perf_probe_event pev; |
12 | char *spev; | 13 | char *spev; |
13 | struct strlist *tevlist; | 14 | struct strlist *tevlist; |
@@ -35,6 +36,7 @@ struct probe_cache *probe_cache__new(const char *target); | |||
35 | int probe_cache__add_entry(struct probe_cache *pcache, | 36 | int probe_cache__add_entry(struct probe_cache *pcache, |
36 | struct perf_probe_event *pev, | 37 | struct perf_probe_event *pev, |
37 | struct probe_trace_event *tevs, int ntevs); | 38 | struct probe_trace_event *tevs, int ntevs); |
39 | int probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname); | ||
38 | int probe_cache__commit(struct probe_cache *pcache); | 40 | int probe_cache__commit(struct probe_cache *pcache); |
39 | void probe_cache__purge(struct probe_cache *pcache); | 41 | void probe_cache__purge(struct probe_cache *pcache); |
40 | void probe_cache__delete(struct probe_cache *pcache); | 42 | void probe_cache__delete(struct probe_cache *pcache); |