diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-08-07 10:43:07 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-08-07 10:43:07 -0700 |
commit | da758ddede96dd850945d3417ff75209a666ba0d (patch) | |
tree | f8da615ef68c9311b87af8fed6a7b717d7e5564d /tools/perf/util/symbol.c | |
parent | 389623fef0e8b088f293c437d3b7094fe82349fd (diff) | |
parent | 1054598cab8674438675085fae459e960eb10799 (diff) |
Merge branch 'perfcounters-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'perfcounters-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
perf_counter: Fix double list iteration in per task precise stats
perf: Auto-detect libelf
perf symbol: Fix symbol parsing in certain cases: use the build-id as a symlink
perf_counter/powerpc: Check oprofile_cpu_type for NULL before using it
ftrace: Fix perf-tracepoint OOPS
perf report: Add missing command line options to man page
perf: Auto-detect libbfd
perf report: Make --sort comm,dso,symbol the default
Diffstat (limited to 'tools/perf/util/symbol.c')
-rw-r--r-- | tools/perf/util/symbol.c | 85 |
1 files changed, 82 insertions, 3 deletions
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index b4fe0579bd6..16ddca20294 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -6,7 +6,16 @@ #include <libelf.h> #include <gelf.h> #include <elf.h> + +#ifndef NO_DEMANGLE #include <bfd.h> +#else +static inline +char *bfd_demangle(void __used *v, const char __used *c, int __used i) +{ + return NULL; +} +#endif const char *sym_hist_filter; @@ -652,10 +661,69 @@ out_close: return err; } +#define BUILD_ID_SIZE 128 + +static char *dso__read_build_id(struct dso *self, int verbose) +{ + int i; + GElf_Ehdr ehdr; + GElf_Shdr shdr; + Elf_Data *build_id_data; + Elf_Scn *sec; + char *build_id = NULL, *bid; + unsigned char *raw; + Elf *elf; + int fd = open(self->name, O_RDONLY); + + if (fd < 0) + goto out; + + elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); + if (elf == NULL) { + if (verbose) + fprintf(stderr, "%s: cannot read %s ELF file.\n", + __func__, self->name); + goto out_close; + } + + if (gelf_getehdr(elf, &ehdr) == NULL) { + if (verbose) + fprintf(stderr, "%s: cannot get elf header.\n", __func__); + goto out_elf_end; + } + + sec = elf_section_by_name(elf, &ehdr, &shdr, ".note.gnu.build-id", NULL); + if (sec == NULL) + goto out_elf_end; + + build_id_data = elf_getdata(sec, NULL); + if (build_id_data == NULL) + goto out_elf_end; + build_id = malloc(BUILD_ID_SIZE); + if (build_id == NULL) + goto out_elf_end; + raw = build_id_data->d_buf + 16; + bid = build_id; + + for (i = 0; i < 20; ++i) { + sprintf(bid, "%02x", *raw); + ++raw; + bid += 2; + } + if (verbose) + printf("%s(%s): %s\n", __func__, self->name, build_id); +out_elf_end: + elf_end(elf); +out_close: + close(fd); +out: + return build_id; +} + int dso__load(struct dso *self, symbol_filter_t filter, int verbose) { - int size = strlen(self->name) + sizeof("/usr/lib/debug%s.debug"); - char *name = malloc(size); + int size = PATH_MAX; + char *name = malloc(size), *build_id = NULL; int variant = 0; int ret = -1; int fd; @@ -677,7 +745,18 @@ more: case 1: /* Ubuntu */ snprintf(name, size, "/usr/lib/debug%s", self->name); break; - case 2: /* Sane people */ + case 2: + build_id = dso__read_build_id(self, verbose); + if (build_id != NULL) { + snprintf(name, size, + "/usr/lib/debug/.build-id/%.2s/%s.debug", + build_id, build_id + 2); + free(build_id); + break; + } + variant++; + /* Fall thru */ + case 3: /* Sane people */ snprintf(name, size, "%s", self->name); break; |