diff options
Diffstat (limited to 'tools/perf/util/symbol.c')
-rw-r--r-- | tools/perf/util/symbol.c | 133 |
1 files changed, 117 insertions, 16 deletions
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 28106059bf12..5c0f42e6b33b 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -6,14 +6,18 @@ | |||
6 | #include <libelf.h> | 6 | #include <libelf.h> |
7 | #include <gelf.h> | 7 | #include <gelf.h> |
8 | #include <elf.h> | 8 | #include <elf.h> |
9 | #include <bfd.h> | ||
10 | 9 | ||
11 | const char *sym_hist_filter; | 10 | const char *sym_hist_filter; |
12 | 11 | ||
13 | #ifndef DMGL_PARAMS | 12 | enum dso_origin { |
14 | #define DMGL_PARAMS (1 << 0) /* Include function args */ | 13 | DSO__ORIG_KERNEL = 0, |
15 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ | 14 | DSO__ORIG_JAVA_JIT, |
16 | #endif | 15 | DSO__ORIG_FEDORA, |
16 | DSO__ORIG_UBUNTU, | ||
17 | DSO__ORIG_BUILDID, | ||
18 | DSO__ORIG_DSO, | ||
19 | DSO__ORIG_NOT_FOUND, | ||
20 | }; | ||
17 | 21 | ||
18 | static struct symbol *symbol__new(u64 start, u64 len, | 22 | static struct symbol *symbol__new(u64 start, u64 len, |
19 | const char *name, unsigned int priv_size, | 23 | const char *name, unsigned int priv_size, |
@@ -72,6 +76,7 @@ struct dso *dso__new(const char *name, unsigned int sym_priv_size) | |||
72 | self->sym_priv_size = sym_priv_size; | 76 | self->sym_priv_size = sym_priv_size; |
73 | self->find_symbol = dso__find_symbol; | 77 | self->find_symbol = dso__find_symbol; |
74 | self->slen_calculated = 0; | 78 | self->slen_calculated = 0; |
79 | self->origin = DSO__ORIG_NOT_FOUND; | ||
75 | } | 80 | } |
76 | 81 | ||
77 | return self; | 82 | return self; |
@@ -565,7 +570,7 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
565 | goto out_elf_end; | 570 | goto out_elf_end; |
566 | 571 | ||
567 | secstrs = elf_getdata(sec_strndx, NULL); | 572 | secstrs = elf_getdata(sec_strndx, NULL); |
568 | if (symstrs == NULL) | 573 | if (secstrs == NULL) |
569 | goto out_elf_end; | 574 | goto out_elf_end; |
570 | 575 | ||
571 | nr_syms = shdr.sh_size / shdr.sh_entsize; | 576 | nr_syms = shdr.sh_size / shdr.sh_entsize; |
@@ -652,11 +657,85 @@ out_close: | |||
652 | return err; | 657 | return err; |
653 | } | 658 | } |
654 | 659 | ||
660 | #define BUILD_ID_SIZE 128 | ||
661 | |||
662 | static char *dso__read_build_id(struct dso *self, int verbose) | ||
663 | { | ||
664 | int i; | ||
665 | GElf_Ehdr ehdr; | ||
666 | GElf_Shdr shdr; | ||
667 | Elf_Data *build_id_data; | ||
668 | Elf_Scn *sec; | ||
669 | char *build_id = NULL, *bid; | ||
670 | unsigned char *raw; | ||
671 | Elf *elf; | ||
672 | int fd = open(self->name, O_RDONLY); | ||
673 | |||
674 | if (fd < 0) | ||
675 | goto out; | ||
676 | |||
677 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | ||
678 | if (elf == NULL) { | ||
679 | if (verbose) | ||
680 | fprintf(stderr, "%s: cannot read %s ELF file.\n", | ||
681 | __func__, self->name); | ||
682 | goto out_close; | ||
683 | } | ||
684 | |||
685 | if (gelf_getehdr(elf, &ehdr) == NULL) { | ||
686 | if (verbose) | ||
687 | fprintf(stderr, "%s: cannot get elf header.\n", __func__); | ||
688 | goto out_elf_end; | ||
689 | } | ||
690 | |||
691 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".note.gnu.build-id", NULL); | ||
692 | if (sec == NULL) | ||
693 | goto out_elf_end; | ||
694 | |||
695 | build_id_data = elf_getdata(sec, NULL); | ||
696 | if (build_id_data == NULL) | ||
697 | goto out_elf_end; | ||
698 | build_id = malloc(BUILD_ID_SIZE); | ||
699 | if (build_id == NULL) | ||
700 | goto out_elf_end; | ||
701 | raw = build_id_data->d_buf + 16; | ||
702 | bid = build_id; | ||
703 | |||
704 | for (i = 0; i < 20; ++i) { | ||
705 | sprintf(bid, "%02x", *raw); | ||
706 | ++raw; | ||
707 | bid += 2; | ||
708 | } | ||
709 | if (verbose >= 2) | ||
710 | printf("%s(%s): %s\n", __func__, self->name, build_id); | ||
711 | out_elf_end: | ||
712 | elf_end(elf); | ||
713 | out_close: | ||
714 | close(fd); | ||
715 | out: | ||
716 | return build_id; | ||
717 | } | ||
718 | |||
719 | char dso__symtab_origin(const struct dso *self) | ||
720 | { | ||
721 | static const char origin[] = { | ||
722 | [DSO__ORIG_KERNEL] = 'k', | ||
723 | [DSO__ORIG_JAVA_JIT] = 'j', | ||
724 | [DSO__ORIG_FEDORA] = 'f', | ||
725 | [DSO__ORIG_UBUNTU] = 'u', | ||
726 | [DSO__ORIG_BUILDID] = 'b', | ||
727 | [DSO__ORIG_DSO] = 'd', | ||
728 | }; | ||
729 | |||
730 | if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) | ||
731 | return '!'; | ||
732 | return origin[self->origin]; | ||
733 | } | ||
734 | |||
655 | int dso__load(struct dso *self, symbol_filter_t filter, int verbose) | 735 | int dso__load(struct dso *self, symbol_filter_t filter, int verbose) |
656 | { | 736 | { |
657 | int size = strlen(self->name) + sizeof("/usr/lib/debug%s.debug"); | 737 | int size = PATH_MAX; |
658 | char *name = malloc(size); | 738 | char *name = malloc(size), *build_id = NULL; |
659 | int variant = 0; | ||
660 | int ret = -1; | 739 | int ret = -1; |
661 | int fd; | 740 | int fd; |
662 | 741 | ||
@@ -665,26 +744,43 @@ int dso__load(struct dso *self, symbol_filter_t filter, int verbose) | |||
665 | 744 | ||
666 | self->adjust_symbols = 0; | 745 | self->adjust_symbols = 0; |
667 | 746 | ||
668 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) | 747 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) { |
669 | return dso__load_perf_map(self, filter, verbose); | 748 | ret = dso__load_perf_map(self, filter, verbose); |
749 | self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT : | ||
750 | DSO__ORIG_NOT_FOUND; | ||
751 | return ret; | ||
752 | } | ||
753 | |||
754 | self->origin = DSO__ORIG_FEDORA - 1; | ||
670 | 755 | ||
671 | more: | 756 | more: |
672 | do { | 757 | do { |
673 | switch (variant) { | 758 | self->origin++; |
674 | case 0: /* Fedora */ | 759 | switch (self->origin) { |
760 | case DSO__ORIG_FEDORA: | ||
675 | snprintf(name, size, "/usr/lib/debug%s.debug", self->name); | 761 | snprintf(name, size, "/usr/lib/debug%s.debug", self->name); |
676 | break; | 762 | break; |
677 | case 1: /* Ubuntu */ | 763 | case DSO__ORIG_UBUNTU: |
678 | snprintf(name, size, "/usr/lib/debug%s", self->name); | 764 | snprintf(name, size, "/usr/lib/debug%s", self->name); |
679 | break; | 765 | break; |
680 | case 2: /* Sane people */ | 766 | case DSO__ORIG_BUILDID: |
767 | build_id = dso__read_build_id(self, verbose); | ||
768 | if (build_id != NULL) { | ||
769 | snprintf(name, size, | ||
770 | "/usr/lib/debug/.build-id/%.2s/%s.debug", | ||
771 | build_id, build_id + 2); | ||
772 | free(build_id); | ||
773 | break; | ||
774 | } | ||
775 | self->origin++; | ||
776 | /* Fall thru */ | ||
777 | case DSO__ORIG_DSO: | ||
681 | snprintf(name, size, "%s", self->name); | 778 | snprintf(name, size, "%s", self->name); |
682 | break; | 779 | break; |
683 | 780 | ||
684 | default: | 781 | default: |
685 | goto out; | 782 | goto out; |
686 | } | 783 | } |
687 | variant++; | ||
688 | 784 | ||
689 | fd = open(name, O_RDONLY); | 785 | fd = open(name, O_RDONLY); |
690 | } while (fd < 0); | 786 | } while (fd < 0); |
@@ -705,6 +801,8 @@ more: | |||
705 | } | 801 | } |
706 | out: | 802 | out: |
707 | free(name); | 803 | free(name); |
804 | if (ret < 0 && strstr(self->name, " (deleted)") != NULL) | ||
805 | return 0; | ||
708 | return ret; | 806 | return ret; |
709 | } | 807 | } |
710 | 808 | ||
@@ -820,6 +918,9 @@ int dso__load_kernel(struct dso *self, const char *vmlinux, | |||
820 | if (err <= 0) | 918 | if (err <= 0) |
821 | err = dso__load_kallsyms(self, filter, verbose); | 919 | err = dso__load_kallsyms(self, filter, verbose); |
822 | 920 | ||
921 | if (err > 0) | ||
922 | self->origin = DSO__ORIG_KERNEL; | ||
923 | |||
823 | return err; | 924 | return err; |
824 | } | 925 | } |
825 | 926 | ||