perf symbols: Workaround objdump difficulties with kcore
authorAdrian Hunter <adrian.hunter@intel.com>
Wed, 9 Oct 2013 12:01:12 +0000 (15:01 +0300)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Mon, 14 Oct 2013 13:29:00 +0000 (10:29 -0300)
The objdump tool fails to annotate module symbols when looking at kcore.

Workaround this by extracting object code from kcore and putting it in a
temporary file for objdump to use instead.

The temporary file is created to look like kcore but contains only the
function being disassembled.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1381320078-16497-3-git-send-email-adrian.hunter@intel.com
[ Renamed 'index' to 'idx' to avoid shadowing string.h's 'index' in Fedora 12,
  Replace local with variable length with malloc/free to fix build in Fedora 12 ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/annotate.c
tools/perf/util/symbol-elf.c
tools/perf/util/symbol-minimal.c
tools/perf/util/symbol.h

index d73e8008aadaaa4ba6d2724ca8b9e56f1cfa9c76..882bb864cee0f558cc363019c79dc72b070ed71e 100644 (file)
@@ -879,6 +879,8 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize)
        FILE *file;
        int err = 0;
        char symfs_filename[PATH_MAX];
+       struct kcore_extract kce;
+       bool delete_extract = false;
 
        if (filename) {
                snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
@@ -940,6 +942,23 @@ fallback:
        pr_debug("annotating [%p] %30s : [%p] %30s\n",
                 dso, dso->long_name, sym, sym->name);
 
+       if (dso__is_kcore(dso)) {
+               kce.kcore_filename = symfs_filename;
+               kce.addr = map__rip_2objdump(map, sym->start);
+               kce.offs = sym->start;
+               kce.len = sym->end + 1 - sym->start;
+               if (!kcore_extract__create(&kce)) {
+                       delete_extract = true;
+                       strlcpy(symfs_filename, kce.extract_filename,
+                               sizeof(symfs_filename));
+                       if (free_filename) {
+                               free(filename);
+                               free_filename = false;
+                       }
+                       filename = symfs_filename;
+               }
+       }
+
        snprintf(command, sizeof(command),
                 "%s %s%s --start-address=0x%016" PRIx64
                 " --stop-address=0x%016" PRIx64
@@ -972,6 +991,8 @@ fallback:
 
        pclose(file);
 out_free_filename:
+       if (delete_extract)
+               kcore_extract__delete(&kce);
        if (free_filename)
                free(filename);
        return err;
index c376930528005aa081a4127a6e1eaa837e684e59..499c71d30225e054fed6536494d5b63b73ba69d5 100644 (file)
@@ -1018,6 +1018,235 @@ int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data,
        return err;
 }
 
+static int copy_bytes(int from, off_t from_offs, int to, off_t to_offs, u64 len)
+{
+       ssize_t r;
+       size_t n;
+       int err = -1;
+       char *buf = malloc(page_size);
+
+       if (buf == NULL)
+               return -1;
+
+       if (lseek(to, to_offs, SEEK_SET) != to_offs)
+               goto out;
+
+       if (lseek(from, from_offs, SEEK_SET) != from_offs)
+               goto out;
+
+       while (len) {
+               n = page_size;
+               if (len < n)
+                       n = len;
+               /* Use read because mmap won't work on proc files */
+               r = read(from, buf, n);
+               if (r < 0)
+                       goto out;
+               if (!r)
+                       break;
+               n = r;
+               r = write(to, buf, n);
+               if (r < 0)
+                       goto out;
+               if ((size_t)r != n)
+                       goto out;
+               len -= n;
+       }
+
+       err = 0;
+out:
+       free(buf);
+       return err;
+}
+
+struct kcore {
+       int fd;
+       int elfclass;
+       Elf *elf;
+       GElf_Ehdr ehdr;
+};
+
+static int kcore__open(struct kcore *kcore, const char *filename)
+{
+       GElf_Ehdr *ehdr;
+
+       kcore->fd = open(filename, O_RDONLY);
+       if (kcore->fd == -1)
+               return -1;
+
+       kcore->elf = elf_begin(kcore->fd, ELF_C_READ, NULL);
+       if (!kcore->elf)
+               goto out_close;
+
+       kcore->elfclass = gelf_getclass(kcore->elf);
+       if (kcore->elfclass == ELFCLASSNONE)
+               goto out_end;
+
+       ehdr = gelf_getehdr(kcore->elf, &kcore->ehdr);
+       if (!ehdr)
+               goto out_end;
+
+       return 0;
+
+out_end:
+       elf_end(kcore->elf);
+out_close:
+       close(kcore->fd);
+       return -1;
+}
+
+static int kcore__init(struct kcore *kcore, char *filename, int elfclass,
+                      bool temp)
+{
+       GElf_Ehdr *ehdr;
+
+       kcore->elfclass = elfclass;
+
+       if (temp)
+               kcore->fd = mkstemp(filename);
+       else
+               kcore->fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0400);
+       if (kcore->fd == -1)
+               return -1;
+
+       kcore->elf = elf_begin(kcore->fd, ELF_C_WRITE, NULL);
+       if (!kcore->elf)
+               goto out_close;
+
+       if (!gelf_newehdr(kcore->elf, elfclass))
+               goto out_end;
+
+       ehdr = gelf_getehdr(kcore->elf, &kcore->ehdr);
+       if (!ehdr)
+               goto out_end;
+
+       return 0;
+
+out_end:
+       elf_end(kcore->elf);
+out_close:
+       close(kcore->fd);
+       unlink(filename);
+       return -1;
+}
+
+static void kcore__close(struct kcore *kcore)
+{
+       elf_end(kcore->elf);
+       close(kcore->fd);
+}
+
+static int kcore__copy_hdr(struct kcore *from, struct kcore *to, size_t count)
+{
+       GElf_Ehdr *ehdr = &to->ehdr;
+       GElf_Ehdr *kehdr = &from->ehdr;
+
+       memcpy(ehdr->e_ident, kehdr->e_ident, EI_NIDENT);
+       ehdr->e_type      = kehdr->e_type;
+       ehdr->e_machine   = kehdr->e_machine;
+       ehdr->e_version   = kehdr->e_version;
+       ehdr->e_entry     = 0;
+       ehdr->e_shoff     = 0;
+       ehdr->e_flags     = kehdr->e_flags;
+       ehdr->e_phnum     = count;
+       ehdr->e_shentsize = 0;
+       ehdr->e_shnum     = 0;
+       ehdr->e_shstrndx  = 0;
+
+       if (from->elfclass == ELFCLASS32) {
+               ehdr->e_phoff     = sizeof(Elf32_Ehdr);
+               ehdr->e_ehsize    = sizeof(Elf32_Ehdr);
+               ehdr->e_phentsize = sizeof(Elf32_Phdr);
+       } else {
+               ehdr->e_phoff     = sizeof(Elf64_Ehdr);
+               ehdr->e_ehsize    = sizeof(Elf64_Ehdr);
+               ehdr->e_phentsize = sizeof(Elf64_Phdr);
+       }
+
+       if (!gelf_update_ehdr(to->elf, ehdr))
+               return -1;
+
+       if (!gelf_newphdr(to->elf, count))
+               return -1;
+
+       return 0;
+}
+
+static int kcore__add_phdr(struct kcore *kcore, int idx, off_t offset,
+                          u64 addr, u64 len)
+{
+       GElf_Phdr gphdr;
+       GElf_Phdr *phdr;
+
+       phdr = gelf_getphdr(kcore->elf, idx, &gphdr);
+       if (!phdr)
+               return -1;
+
+       phdr->p_type    = PT_LOAD;
+       phdr->p_flags   = PF_R | PF_W | PF_X;
+       phdr->p_offset  = offset;
+       phdr->p_vaddr   = addr;
+       phdr->p_paddr   = 0;
+       phdr->p_filesz  = len;
+       phdr->p_memsz   = len;
+       phdr->p_align   = page_size;
+
+       if (!gelf_update_phdr(kcore->elf, idx, phdr))
+               return -1;
+
+       return 0;
+}
+
+static off_t kcore__write(struct kcore *kcore)
+{
+       return elf_update(kcore->elf, ELF_C_WRITE);
+}
+
+int kcore_extract__create(struct kcore_extract *kce)
+{
+       struct kcore kcore;
+       struct kcore extract;
+       size_t count = 1;
+       int idx = 0, err = -1;
+       off_t offset = page_size, sz;
+
+       if (kcore__open(&kcore, kce->kcore_filename))
+               return -1;
+
+       strcpy(kce->extract_filename, PERF_KCORE_EXTRACT);
+       if (kcore__init(&extract, kce->extract_filename, kcore.elfclass, true))
+               goto out_kcore_close;
+
+       if (kcore__copy_hdr(&kcore, &extract, count))
+               goto out_extract_close;
+
+       if (kcore__add_phdr(&extract, idx, offset, kce->addr, kce->len))
+               goto out_extract_close;
+
+       sz = kcore__write(&extract);
+       if (sz < 0 || sz > offset)
+               goto out_extract_close;
+
+       if (copy_bytes(kcore.fd, kce->offs, extract.fd, offset, kce->len))
+               goto out_extract_close;
+
+       err = 0;
+
+out_extract_close:
+       kcore__close(&extract);
+       if (err)
+               unlink(kce->extract_filename);
+out_kcore_close:
+       kcore__close(&kcore);
+
+       return err;
+}
+
+void kcore_extract__delete(struct kcore_extract *kce)
+{
+       unlink(kce->extract_filename);
+}
+
 void symbol__elf_init(void)
 {
        elf_version(EV_CURRENT);
index 3a802c300fc564adce713aebbd848a1b936e78c1..928556d2508fd370eaa88940adefa6834a5d842e 100644 (file)
@@ -308,6 +308,15 @@ int file__read_maps(int fd __maybe_unused, bool exe __maybe_unused,
        return -1;
 }
 
+int kcore_extract__create(struct kcore_extract *kce __maybe_unused)
+{
+       return -1;
+}
+
+void kcore_extract__delete(struct kcore_extract *kce __maybe_unused)
+{
+}
+
 void symbol__elf_init(void)
 {
 }
index 2d3eb43ab9f948242b1735d17d4c513f9a28ecbd..fb107e13e9250d6bd32102e2b4b531aeb15b8fd5 100644 (file)
@@ -256,4 +256,18 @@ typedef int (*mapfn_t)(u64 start, u64 len, u64 pgoff, void *data);
 int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data,
                    bool *is_64_bit);
 
+#define PERF_KCORE_EXTRACT "/tmp/perf-kcore-XXXXXX"
+
+struct kcore_extract {
+       char *kcore_filename;
+       u64 addr;
+       u64 offs;
+       u64 len;
+       char extract_filename[sizeof(PERF_KCORE_EXTRACT)];
+       int fd;
+};
+
+int kcore_extract__create(struct kcore_extract *kce);
+void kcore_extract__delete(struct kcore_extract *kce);
+
 #endif /* __PERF_SYMBOL */