diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2009-10-02 02:29:58 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-10-02 04:48:42 -0400 |
commit | 439d473b4777de510e1322168ac6f2f377ecd5bc (patch) | |
tree | f33622a0b98c2b0ce16637322d48542eb93e2fa3 /tools/perf/util/symbol.c | |
parent | 2ccdc450e658053681202d42ac64b3638f22dc1a (diff) |
perf tools: Rewrite and improve support for kernel modules
Representing modules as struct map entries, backed by a DSO, etc,
using /proc/modules to find where the module is loaded.
DSOs now can have a short and long name, so that in verbose mode we
can show exactly which .ko or vmlinux image was used.
As kernel modules now are a DSO separate from the kernel, we can
ask for just the hits for a particular set of kernel modules, just
like we can do with shared libraries:
[root@doppio linux-2.6-tip]# perf report -n --vmlinux
/home/acme/git/build/tip-recvmmsg/vmlinux --modules --dsos \[drm\] | head -15
84.58% 13266 Xorg [k] drm_clflush_pages
4.02% 630 Xorg [k] trace_kmalloc.clone.0
3.95% 619 Xorg [k] drm_ioctl
2.07% 324 Xorg [k] drm_addbufs
1.68% 263 Xorg [k] drm_gem_close_ioctl
0.77% 120 Xorg [k] drm_setmaster_ioctl
0.70% 110 Xorg [k] drm_lastclose
0.68% 106 Xorg [k] drm_open
0.54% 85 Xorg [k] drm_mm_search_free
[root@doppio linux-2.6-tip]#
Specifying --dsos /lib/modules/2.6.31-tip/kernel/drivers/gpu/drm/drm.ko
would have the same effect. Allowing specifying just 'drm.ko' is left
for another patch.
Processing kallsyms so that per kernel module struct map are
instantiated was also left for another patch. That will allow
removing the module name from each of its symbols.
struct symbol was reduced by removing the ->module backpointer and
moving it (well now the map) to struct symbol_entry in perf top,
that is its only user right now.
The total linecount went down by ~500 lines.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Avi Kivity <avi@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools/perf/util/symbol.c')
-rw-r--r-- | tools/perf/util/symbol.c | 447 |
1 files changed, 301 insertions, 146 deletions
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 559fb06210f5..e88296899470 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -2,12 +2,14 @@ | |||
2 | #include "../perf.h" | 2 | #include "../perf.h" |
3 | #include "string.h" | 3 | #include "string.h" |
4 | #include "symbol.h" | 4 | #include "symbol.h" |
5 | #include "thread.h" | ||
5 | 6 | ||
6 | #include "debug.h" | 7 | #include "debug.h" |
7 | 8 | ||
8 | #include <libelf.h> | 9 | #include <libelf.h> |
9 | #include <gelf.h> | 10 | #include <gelf.h> |
10 | #include <elf.h> | 11 | #include <elf.h> |
12 | #include <sys/utsname.h> | ||
11 | 13 | ||
12 | const char *sym_hist_filter; | 14 | const char *sym_hist_filter; |
13 | 15 | ||
@@ -18,12 +20,15 @@ enum dso_origin { | |||
18 | DSO__ORIG_UBUNTU, | 20 | DSO__ORIG_UBUNTU, |
19 | DSO__ORIG_BUILDID, | 21 | DSO__ORIG_BUILDID, |
20 | DSO__ORIG_DSO, | 22 | DSO__ORIG_DSO, |
23 | DSO__ORIG_KMODULE, | ||
21 | DSO__ORIG_NOT_FOUND, | 24 | DSO__ORIG_NOT_FOUND, |
22 | }; | 25 | }; |
23 | 26 | ||
24 | static struct symbol *symbol__new(u64 start, u64 len, | 27 | static void dsos__add(struct dso *dso); |
25 | const char *name, unsigned int priv_size, | 28 | static struct dso *dsos__find(const char *name); |
26 | u64 obj_start, int v) | 29 | |
30 | static struct symbol *symbol__new(u64 start, u64 len, const char *name, | ||
31 | unsigned int priv_size, int v) | ||
27 | { | 32 | { |
28 | size_t namelen = strlen(name) + 1; | 33 | size_t namelen = strlen(name) + 1; |
29 | struct symbol *self = calloc(1, priv_size + sizeof(*self) + namelen); | 34 | struct symbol *self = calloc(1, priv_size + sizeof(*self) + namelen); |
@@ -32,10 +37,9 @@ static struct symbol *symbol__new(u64 start, u64 len, | |||
32 | return NULL; | 37 | return NULL; |
33 | 38 | ||
34 | if (v >= 2) | 39 | if (v >= 2) |
35 | printf("new symbol: %016Lx [%08lx]: %s, hist: %p, obj_start: %p\n", | 40 | printf("new symbol: %016Lx [%08lx]: %s, hist: %p\n", |
36 | (u64)start, (unsigned long)len, name, self->hist, (void *)(unsigned long)obj_start); | 41 | start, (unsigned long)len, name, self->hist); |
37 | 42 | ||
38 | self->obj_start= obj_start; | ||
39 | self->hist = NULL; | 43 | self->hist = NULL; |
40 | self->hist_sum = 0; | 44 | self->hist_sum = 0; |
41 | 45 | ||
@@ -60,12 +64,8 @@ static void symbol__delete(struct symbol *self, unsigned int priv_size) | |||
60 | 64 | ||
61 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) | 65 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) |
62 | { | 66 | { |
63 | if (!self->module) | 67 | return fprintf(fp, " %llx-%llx %s\n", |
64 | return fprintf(fp, " %llx-%llx %s\n", | ||
65 | self->start, self->end, self->name); | 68 | self->start, self->end, self->name); |
66 | else | ||
67 | return fprintf(fp, " %llx-%llx %s \t[%s]\n", | ||
68 | self->start, self->end, self->name, self->module->name); | ||
69 | } | 69 | } |
70 | 70 | ||
71 | struct dso *dso__new(const char *name, unsigned int sym_priv_size) | 71 | struct dso *dso__new(const char *name, unsigned int sym_priv_size) |
@@ -74,6 +74,8 @@ struct dso *dso__new(const char *name, unsigned int sym_priv_size) | |||
74 | 74 | ||
75 | if (self != NULL) { | 75 | if (self != NULL) { |
76 | strcpy(self->name, name); | 76 | strcpy(self->name, name); |
77 | self->long_name = self->name; | ||
78 | self->short_name = self->name; | ||
77 | self->syms = RB_ROOT; | 79 | self->syms = RB_ROOT; |
78 | self->sym_priv_size = sym_priv_size; | 80 | self->sym_priv_size = sym_priv_size; |
79 | self->find_symbol = dso__find_symbol; | 81 | self->find_symbol = dso__find_symbol; |
@@ -100,6 +102,8 @@ static void dso__delete_symbols(struct dso *self) | |||
100 | void dso__delete(struct dso *self) | 102 | void dso__delete(struct dso *self) |
101 | { | 103 | { |
102 | dso__delete_symbols(self); | 104 | dso__delete_symbols(self); |
105 | if (self->long_name != self->name) | ||
106 | free(self->long_name); | ||
103 | free(self); | 107 | free(self); |
104 | } | 108 | } |
105 | 109 | ||
@@ -147,7 +151,7 @@ struct symbol *dso__find_symbol(struct dso *self, u64 ip) | |||
147 | 151 | ||
148 | size_t dso__fprintf(struct dso *self, FILE *fp) | 152 | size_t dso__fprintf(struct dso *self, FILE *fp) |
149 | { | 153 | { |
150 | size_t ret = fprintf(fp, "dso: %s\n", self->name); | 154 | size_t ret = fprintf(fp, "dso: %s\n", self->long_name); |
151 | 155 | ||
152 | struct rb_node *nd; | 156 | struct rb_node *nd; |
153 | for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) { | 157 | for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) { |
@@ -158,7 +162,8 @@ size_t dso__fprintf(struct dso *self, FILE *fp) | |||
158 | return ret; | 162 | return ret; |
159 | } | 163 | } |
160 | 164 | ||
161 | static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int v) | 165 | static int dso__load_kallsyms(struct dso *self, struct map *map, |
166 | symbol_filter_t filter, int v) | ||
162 | { | 167 | { |
163 | struct rb_node *nd, *prevnd; | 168 | struct rb_node *nd, *prevnd; |
164 | char *line = NULL; | 169 | char *line = NULL; |
@@ -200,12 +205,12 @@ static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int v) | |||
200 | * Well fix up the end later, when we have all sorted. | 205 | * Well fix up the end later, when we have all sorted. |
201 | */ | 206 | */ |
202 | sym = symbol__new(start, 0xdead, line + len + 2, | 207 | sym = symbol__new(start, 0xdead, line + len + 2, |
203 | self->sym_priv_size, 0, v); | 208 | self->sym_priv_size, v); |
204 | 209 | ||
205 | if (sym == NULL) | 210 | if (sym == NULL) |
206 | goto out_delete_line; | 211 | goto out_delete_line; |
207 | 212 | ||
208 | if (filter && filter(self, sym)) | 213 | if (filter && filter(map, sym)) |
209 | symbol__delete(sym, self->sym_priv_size); | 214 | symbol__delete(sym, self->sym_priv_size); |
210 | else { | 215 | else { |
211 | dso__insert_symbol(self, sym); | 216 | dso__insert_symbol(self, sym); |
@@ -241,14 +246,15 @@ out_failure: | |||
241 | return -1; | 246 | return -1; |
242 | } | 247 | } |
243 | 248 | ||
244 | static int dso__load_perf_map(struct dso *self, symbol_filter_t filter, int v) | 249 | static int dso__load_perf_map(struct dso *self, struct map *map, |
250 | symbol_filter_t filter, int v) | ||
245 | { | 251 | { |
246 | char *line = NULL; | 252 | char *line = NULL; |
247 | size_t n; | 253 | size_t n; |
248 | FILE *file; | 254 | FILE *file; |
249 | int nr_syms = 0; | 255 | int nr_syms = 0; |
250 | 256 | ||
251 | file = fopen(self->name, "r"); | 257 | file = fopen(self->long_name, "r"); |
252 | if (file == NULL) | 258 | if (file == NULL) |
253 | goto out_failure; | 259 | goto out_failure; |
254 | 260 | ||
@@ -279,12 +285,12 @@ static int dso__load_perf_map(struct dso *self, symbol_filter_t filter, int v) | |||
279 | continue; | 285 | continue; |
280 | 286 | ||
281 | sym = symbol__new(start, size, line + len, | 287 | sym = symbol__new(start, size, line + len, |
282 | self->sym_priv_size, start, v); | 288 | self->sym_priv_size, v); |
283 | 289 | ||
284 | if (sym == NULL) | 290 | if (sym == NULL) |
285 | goto out_delete_line; | 291 | goto out_delete_line; |
286 | 292 | ||
287 | if (filter && filter(self, sym)) | 293 | if (filter && filter(map, sym)) |
288 | symbol__delete(sym, self->sym_priv_size); | 294 | symbol__delete(sym, self->sym_priv_size); |
289 | else { | 295 | else { |
290 | dso__insert_symbol(self, sym); | 296 | dso__insert_symbol(self, sym); |
@@ -410,7 +416,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, int v) | |||
410 | Elf *elf; | 416 | Elf *elf; |
411 | int nr = 0, symidx, fd, err = 0; | 417 | int nr = 0, symidx, fd, err = 0; |
412 | 418 | ||
413 | fd = open(self->name, O_RDONLY); | 419 | fd = open(self->long_name, O_RDONLY); |
414 | if (fd < 0) | 420 | if (fd < 0) |
415 | goto out; | 421 | goto out; |
416 | 422 | ||
@@ -478,7 +484,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, int v) | |||
478 | "%s@plt", elf_sym__name(&sym, symstrs)); | 484 | "%s@plt", elf_sym__name(&sym, symstrs)); |
479 | 485 | ||
480 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 486 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
481 | sympltname, self->sym_priv_size, 0, v); | 487 | sympltname, self->sym_priv_size, v); |
482 | if (!f) | 488 | if (!f) |
483 | goto out_elf_end; | 489 | goto out_elf_end; |
484 | 490 | ||
@@ -496,7 +502,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, int v) | |||
496 | "%s@plt", elf_sym__name(&sym, symstrs)); | 502 | "%s@plt", elf_sym__name(&sym, symstrs)); |
497 | 503 | ||
498 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 504 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
499 | sympltname, self->sym_priv_size, 0, v); | 505 | sympltname, self->sym_priv_size, v); |
500 | if (!f) | 506 | if (!f) |
501 | goto out_elf_end; | 507 | goto out_elf_end; |
502 | 508 | ||
@@ -515,12 +521,13 @@ out_close: | |||
515 | return nr; | 521 | return nr; |
516 | out: | 522 | out: |
517 | fprintf(stderr, "%s: problems reading %s PLT info.\n", | 523 | fprintf(stderr, "%s: problems reading %s PLT info.\n", |
518 | __func__, self->name); | 524 | __func__, self->long_name); |
519 | return 0; | 525 | return 0; |
520 | } | 526 | } |
521 | 527 | ||
522 | static int dso__load_sym(struct dso *self, int fd, const char *name, | 528 | static int dso__load_sym(struct dso *self, struct map *map, const char *name, |
523 | symbol_filter_t filter, int v, struct module *mod) | 529 | int fd, symbol_filter_t filter, int kernel, |
530 | int kmodule, int v) | ||
524 | { | 531 | { |
525 | Elf_Data *symstrs, *secstrs; | 532 | Elf_Data *symstrs, *secstrs; |
526 | uint32_t nr_syms; | 533 | uint32_t nr_syms; |
@@ -532,7 +539,7 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
532 | GElf_Sym sym; | 539 | GElf_Sym sym; |
533 | Elf_Scn *sec, *sec_strndx; | 540 | Elf_Scn *sec, *sec_strndx; |
534 | Elf *elf; | 541 | Elf *elf; |
535 | int nr = 0, kernel = !strcmp("[kernel]", self->name); | 542 | int nr = 0; |
536 | 543 | ||
537 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | 544 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); |
538 | if (elf == NULL) { | 545 | if (elf == NULL) { |
@@ -589,8 +596,6 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
589 | struct symbol *f; | 596 | struct symbol *f; |
590 | const char *elf_name; | 597 | const char *elf_name; |
591 | char *demangled; | 598 | char *demangled; |
592 | u64 obj_start; | ||
593 | struct section *section = NULL; | ||
594 | int is_label = elf_sym__is_label(&sym); | 599 | int is_label = elf_sym__is_label(&sym); |
595 | const char *section_name; | 600 | const char *section_name; |
596 | 601 | ||
@@ -607,7 +612,6 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
607 | continue; | 612 | continue; |
608 | 613 | ||
609 | section_name = elf_sec__name(&shdr, secstrs); | 614 | section_name = elf_sec__name(&shdr, secstrs); |
610 | obj_start = sym.st_value; | ||
611 | 615 | ||
612 | if (self->adjust_symbols) { | 616 | if (self->adjust_symbols) { |
613 | if (v >= 2) | 617 | if (v >= 2) |
@@ -615,18 +619,8 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
615 | (u64)sym.st_value, (u64)shdr.sh_addr, (u64)shdr.sh_offset); | 619 | (u64)sym.st_value, (u64)shdr.sh_addr, (u64)shdr.sh_offset); |
616 | 620 | ||
617 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; | 621 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; |
618 | } | 622 | } else if (kmodule) |
619 | 623 | sym.st_value += shdr.sh_offset; | |
620 | if (mod) { | ||
621 | section = mod->sections->find_section(mod->sections, section_name); | ||
622 | if (section) | ||
623 | sym.st_value += section->vma; | ||
624 | else { | ||
625 | fprintf(stderr, "dso__load_sym() module %s lookup of %s failed\n", | ||
626 | mod->name, section_name); | ||
627 | goto out_elf_end; | ||
628 | } | ||
629 | } | ||
630 | /* | 624 | /* |
631 | * We need to figure out if the object was created from C++ sources | 625 | * We need to figure out if the object was created from C++ sources |
632 | * DWARF DW_compile_unit has this, but we don't always have access | 626 | * DWARF DW_compile_unit has this, but we don't always have access |
@@ -638,15 +632,14 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
638 | elf_name = demangled; | 632 | elf_name = demangled; |
639 | 633 | ||
640 | f = symbol__new(sym.st_value, sym.st_size, elf_name, | 634 | f = symbol__new(sym.st_value, sym.st_size, elf_name, |
641 | self->sym_priv_size, obj_start, v); | 635 | self->sym_priv_size, v); |
642 | free(demangled); | 636 | free(demangled); |
643 | if (!f) | 637 | if (!f) |
644 | goto out_elf_end; | 638 | goto out_elf_end; |
645 | 639 | ||
646 | if (filter && filter(self, f)) | 640 | if (filter && filter(map, f)) |
647 | symbol__delete(f, self->sym_priv_size); | 641 | symbol__delete(f, self->sym_priv_size); |
648 | else { | 642 | else { |
649 | f->module = mod; | ||
650 | dso__insert_symbol(self, f); | 643 | dso__insert_symbol(self, f); |
651 | nr++; | 644 | nr++; |
652 | } | 645 | } |
@@ -671,7 +664,7 @@ static char *dso__read_build_id(struct dso *self, int v) | |||
671 | char *build_id = NULL, *bid; | 664 | char *build_id = NULL, *bid; |
672 | unsigned char *raw; | 665 | unsigned char *raw; |
673 | Elf *elf; | 666 | Elf *elf; |
674 | int fd = open(self->name, O_RDONLY); | 667 | int fd = open(self->long_name, O_RDONLY); |
675 | 668 | ||
676 | if (fd < 0) | 669 | if (fd < 0) |
677 | goto out; | 670 | goto out; |
@@ -680,7 +673,7 @@ static char *dso__read_build_id(struct dso *self, int v) | |||
680 | if (elf == NULL) { | 673 | if (elf == NULL) { |
681 | if (v) | 674 | if (v) |
682 | fprintf(stderr, "%s: cannot read %s ELF file.\n", | 675 | fprintf(stderr, "%s: cannot read %s ELF file.\n", |
683 | __func__, self->name); | 676 | __func__, self->long_name); |
684 | goto out_close; | 677 | goto out_close; |
685 | } | 678 | } |
686 | 679 | ||
@@ -709,7 +702,7 @@ static char *dso__read_build_id(struct dso *self, int v) | |||
709 | bid += 2; | 702 | bid += 2; |
710 | } | 703 | } |
711 | if (v >= 2) | 704 | if (v >= 2) |
712 | printf("%s(%s): %s\n", __func__, self->name, build_id); | 705 | printf("%s(%s): %s\n", __func__, self->long_name, build_id); |
713 | out_elf_end: | 706 | out_elf_end: |
714 | elf_end(elf); | 707 | elf_end(elf); |
715 | out_close: | 708 | out_close: |
@@ -727,6 +720,7 @@ char dso__symtab_origin(const struct dso *self) | |||
727 | [DSO__ORIG_UBUNTU] = 'u', | 720 | [DSO__ORIG_UBUNTU] = 'u', |
728 | [DSO__ORIG_BUILDID] = 'b', | 721 | [DSO__ORIG_BUILDID] = 'b', |
729 | [DSO__ORIG_DSO] = 'd', | 722 | [DSO__ORIG_DSO] = 'd', |
723 | [DSO__ORIG_KMODULE] = 'K', | ||
730 | }; | 724 | }; |
731 | 725 | ||
732 | if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) | 726 | if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) |
@@ -734,7 +728,7 @@ char dso__symtab_origin(const struct dso *self) | |||
734 | return origin[self->origin]; | 728 | return origin[self->origin]; |
735 | } | 729 | } |
736 | 730 | ||
737 | int dso__load(struct dso *self, symbol_filter_t filter, int v) | 731 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter, int v) |
738 | { | 732 | { |
739 | int size = PATH_MAX; | 733 | int size = PATH_MAX; |
740 | char *name = malloc(size), *build_id = NULL; | 734 | char *name = malloc(size), *build_id = NULL; |
@@ -747,7 +741,7 @@ int dso__load(struct dso *self, symbol_filter_t filter, int v) | |||
747 | self->adjust_symbols = 0; | 741 | self->adjust_symbols = 0; |
748 | 742 | ||
749 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) { | 743 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) { |
750 | ret = dso__load_perf_map(self, filter, v); | 744 | ret = dso__load_perf_map(self, map, filter, v); |
751 | self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT : | 745 | self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT : |
752 | DSO__ORIG_NOT_FOUND; | 746 | DSO__ORIG_NOT_FOUND; |
753 | return ret; | 747 | return ret; |
@@ -760,10 +754,12 @@ more: | |||
760 | self->origin++; | 754 | self->origin++; |
761 | switch (self->origin) { | 755 | switch (self->origin) { |
762 | case DSO__ORIG_FEDORA: | 756 | case DSO__ORIG_FEDORA: |
763 | snprintf(name, size, "/usr/lib/debug%s.debug", self->name); | 757 | snprintf(name, size, "/usr/lib/debug%s.debug", |
758 | self->long_name); | ||
764 | break; | 759 | break; |
765 | case DSO__ORIG_UBUNTU: | 760 | case DSO__ORIG_UBUNTU: |
766 | snprintf(name, size, "/usr/lib/debug%s", self->name); | 761 | snprintf(name, size, "/usr/lib/debug%s", |
762 | self->long_name); | ||
767 | break; | 763 | break; |
768 | case DSO__ORIG_BUILDID: | 764 | case DSO__ORIG_BUILDID: |
769 | build_id = dso__read_build_id(self, v); | 765 | build_id = dso__read_build_id(self, v); |
@@ -777,7 +773,7 @@ more: | |||
777 | self->origin++; | 773 | self->origin++; |
778 | /* Fall thru */ | 774 | /* Fall thru */ |
779 | case DSO__ORIG_DSO: | 775 | case DSO__ORIG_DSO: |
780 | snprintf(name, size, "%s", self->name); | 776 | snprintf(name, size, "%s", self->long_name); |
781 | break; | 777 | break; |
782 | 778 | ||
783 | default: | 779 | default: |
@@ -787,7 +783,7 @@ more: | |||
787 | fd = open(name, O_RDONLY); | 783 | fd = open(name, O_RDONLY); |
788 | } while (fd < 0); | 784 | } while (fd < 0); |
789 | 785 | ||
790 | ret = dso__load_sym(self, fd, name, filter, v, NULL); | 786 | ret = dso__load_sym(self, map, name, fd, filter, 0, 0, v); |
791 | close(fd); | 787 | close(fd); |
792 | 788 | ||
793 | /* | 789 | /* |
@@ -808,89 +804,247 @@ out: | |||
808 | return ret; | 804 | return ret; |
809 | } | 805 | } |
810 | 806 | ||
811 | static int dso__load_module(struct dso *self, struct mod_dso *mods, const char *name, | 807 | static struct rb_root kernel_maps; |
812 | symbol_filter_t filter, int v) | 808 | struct map *kernel_map; |
809 | |||
810 | static void kernel_maps__insert(struct map *map) | ||
813 | { | 811 | { |
814 | struct module *mod = mod_dso__find_module(mods, name); | 812 | maps__insert(&kernel_maps, map); |
815 | int err = 0, fd; | 813 | } |
816 | 814 | ||
817 | if (mod == NULL || !mod->active) | 815 | struct symbol *kernel_maps__find_symbol(u64 ip, struct map **mapp) |
818 | return err; | 816 | { |
817 | /* | ||
818 | * We can't have kernel_map in kernel_maps because it spans an address | ||
819 | * space that includes the modules. The right way to fix this is to | ||
820 | * create several maps, so that we don't have overlapping ranges with | ||
821 | * modules. For now lets look first on the kernel dso. | ||
822 | */ | ||
823 | struct map *map = maps__find(&kernel_maps, ip); | ||
824 | struct symbol *sym; | ||
825 | |||
826 | if (map) { | ||
827 | ip = map->map_ip(map, ip); | ||
828 | sym = map->dso->find_symbol(map->dso, ip); | ||
829 | } else { | ||
830 | map = kernel_map; | ||
831 | sym = map->dso->find_symbol(map->dso, ip); | ||
832 | } | ||
819 | 833 | ||
820 | fd = open(mod->path, O_RDONLY); | 834 | if (mapp) |
835 | *mapp = map; | ||
821 | 836 | ||
822 | if (fd < 0) | 837 | return sym; |
838 | } | ||
839 | |||
840 | struct map *kernel_maps__find_by_dso_name(const char *name) | ||
841 | { | ||
842 | struct rb_node *nd; | ||
843 | |||
844 | for (nd = rb_first(&kernel_maps); nd; nd = rb_next(nd)) { | ||
845 | struct map *map = rb_entry(nd, struct map, rb_node); | ||
846 | |||
847 | if (map->dso && strcmp(map->dso->name, name) == 0) | ||
848 | return map; | ||
849 | } | ||
850 | |||
851 | return NULL; | ||
852 | } | ||
853 | |||
854 | static int dso__load_module_sym(struct dso *self, struct map *map, | ||
855 | symbol_filter_t filter, int v) | ||
856 | { | ||
857 | int err = 0, fd = open(self->long_name, O_RDONLY); | ||
858 | |||
859 | if (fd < 0) { | ||
860 | if (v) | ||
861 | fprintf(stderr, "%s: cannot open %s\n", | ||
862 | __func__, self->long_name); | ||
823 | return err; | 863 | return err; |
864 | } | ||
824 | 865 | ||
825 | err = dso__load_sym(self, fd, name, filter, v, mod); | 866 | err = dso__load_sym(self, map, self->long_name, fd, filter, 0, 1, v); |
826 | close(fd); | 867 | close(fd); |
827 | 868 | ||
828 | return err; | 869 | return err; |
829 | } | 870 | } |
830 | 871 | ||
831 | int dso__load_modules(struct dso *self, symbol_filter_t filter, int v) | 872 | static int dsos__load_modules_sym_dir(char *dirname, |
873 | symbol_filter_t filter, int v) | ||
832 | { | 874 | { |
833 | struct mod_dso *mods = mod_dso__new_dso("modules"); | 875 | struct dirent *dent; |
834 | struct module *pos; | 876 | int nr_symbols = 0, err; |
835 | struct rb_node *next; | 877 | DIR *dir = opendir(dirname); |
836 | int err, count = 0; | ||
837 | 878 | ||
838 | err = mod_dso__load_modules(mods); | 879 | if (!dir) { |
880 | if (v) | ||
881 | fprintf(stderr, "%s: cannot open %s dir\n", __func__, | ||
882 | dirname); | ||
883 | return -1; | ||
884 | } | ||
839 | 885 | ||
840 | if (err <= 0) | 886 | while ((dent = readdir(dir)) != NULL) { |
841 | return err; | 887 | char path[PATH_MAX]; |
888 | |||
889 | if (dent->d_type == DT_DIR) { | ||
890 | if (!strcmp(dent->d_name, ".") || | ||
891 | !strcmp(dent->d_name, "..")) | ||
892 | continue; | ||
893 | |||
894 | snprintf(path, sizeof(path), "%s/%s", | ||
895 | dirname, dent->d_name); | ||
896 | err = dsos__load_modules_sym_dir(path, filter, v); | ||
897 | if (err < 0) | ||
898 | goto failure; | ||
899 | } else { | ||
900 | char *dot = strrchr(dent->d_name, '.'), | ||
901 | dso_name[PATH_MAX]; | ||
902 | struct map *map; | ||
903 | struct rb_node *last; | ||
904 | |||
905 | if (dot == NULL || strcmp(dot, ".ko")) | ||
906 | continue; | ||
907 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", | ||
908 | (int)(dot - dent->d_name), dent->d_name); | ||
909 | |||
910 | map = kernel_maps__find_by_dso_name(dso_name); | ||
911 | if (map == NULL) | ||
912 | continue; | ||
913 | |||
914 | snprintf(path, sizeof(path), "%s/%s", | ||
915 | dirname, dent->d_name); | ||
916 | |||
917 | map->dso->long_name = strdup(path); | ||
918 | if (map->dso->long_name == NULL) | ||
919 | goto failure; | ||
920 | |||
921 | err = dso__load_module_sym(map->dso, map, filter, v); | ||
922 | if (err < 0) | ||
923 | goto failure; | ||
924 | last = rb_last(&map->dso->syms); | ||
925 | if (last) { | ||
926 | struct symbol *sym; | ||
927 | sym = rb_entry(last, struct symbol, rb_node); | ||
928 | map->end = map->start + sym->end; | ||
929 | } | ||
930 | } | ||
931 | nr_symbols += err; | ||
932 | } | ||
842 | 933 | ||
843 | /* | 934 | return nr_symbols; |
844 | * Iterate over modules, and load active symbols. | 935 | failure: |
845 | */ | 936 | closedir(dir); |
846 | next = rb_first(&mods->mods); | 937 | return -1; |
847 | while (next) { | 938 | } |
848 | pos = rb_entry(next, struct module, rb_node); | ||
849 | err = dso__load_module(self, mods, pos->name, filter, v); | ||
850 | 939 | ||
851 | if (err < 0) | 940 | static int dsos__load_modules_sym(symbol_filter_t filter, int v) |
852 | break; | 941 | { |
942 | struct utsname uts; | ||
943 | char modules_path[PATH_MAX]; | ||
853 | 944 | ||
854 | next = rb_next(&pos->rb_node); | 945 | if (uname(&uts) < 0) |
855 | count += err; | 946 | return -1; |
856 | } | ||
857 | 947 | ||
858 | if (err < 0) { | 948 | snprintf(modules_path, sizeof(modules_path), "/lib/modules/%s/kernel", |
859 | mod_dso__delete_modules(mods); | 949 | uts.release); |
860 | mod_dso__delete_self(mods); | ||
861 | return err; | ||
862 | } | ||
863 | 950 | ||
864 | return count; | 951 | return dsos__load_modules_sym_dir(modules_path, filter, v); |
865 | } | 952 | } |
866 | 953 | ||
867 | static inline void dso__fill_symbol_holes(struct dso *self) | 954 | /* |
955 | * Constructor variant for modules (where we know from /proc/modules where | ||
956 | * they are loaded) and for vmlinux, where only after we load all the | ||
957 | * symbols we'll know where it starts and ends. | ||
958 | */ | ||
959 | static struct map *map__new2(u64 start, struct dso *dso) | ||
868 | { | 960 | { |
869 | struct symbol *prev = NULL; | 961 | struct map *self = malloc(sizeof(*self)); |
870 | struct rb_node *nd; | ||
871 | 962 | ||
872 | for (nd = rb_last(&self->syms); nd; nd = rb_prev(nd)) { | 963 | if (self != NULL) { |
873 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | 964 | self->start = start; |
965 | /* | ||
966 | * Will be filled after we load all the symbols | ||
967 | */ | ||
968 | self->end = 0; | ||
969 | |||
970 | self->pgoff = 0; | ||
971 | self->dso = dso; | ||
972 | self->map_ip = map__map_ip; | ||
973 | RB_CLEAR_NODE(&self->rb_node); | ||
974 | } | ||
975 | return self; | ||
976 | } | ||
977 | |||
978 | int dsos__load_modules(unsigned int sym_priv_size, | ||
979 | symbol_filter_t filter, int v) | ||
980 | { | ||
981 | char *line = NULL; | ||
982 | size_t n; | ||
983 | FILE *file = fopen("/proc/modules", "r"); | ||
984 | struct map *map; | ||
874 | 985 | ||
875 | if (prev) { | 986 | if (file == NULL) |
876 | u64 hole = 0; | 987 | return -1; |
877 | int alias = pos->start == prev->start; | ||
878 | 988 | ||
879 | if (!alias) | 989 | while (!feof(file)) { |
880 | hole = prev->start - pos->end - 1; | 990 | char name[PATH_MAX]; |
991 | u64 start; | ||
992 | struct dso *dso; | ||
993 | char *sep; | ||
994 | int line_len; | ||
881 | 995 | ||
882 | if (hole || alias) { | 996 | line_len = getline(&line, &n, file); |
883 | if (alias) | 997 | if (line_len < 0) |
884 | pos->end = prev->end; | 998 | break; |
885 | else if (hole) | 999 | |
886 | pos->end = prev->start - 1; | 1000 | if (!line) |
887 | } | 1001 | goto out_failure; |
1002 | |||
1003 | line[--line_len] = '\0'; /* \n */ | ||
1004 | |||
1005 | sep = strrchr(line, 'x'); | ||
1006 | if (sep == NULL) | ||
1007 | continue; | ||
1008 | |||
1009 | hex2u64(sep + 1, &start); | ||
1010 | |||
1011 | sep = strchr(line, ' '); | ||
1012 | if (sep == NULL) | ||
1013 | continue; | ||
1014 | |||
1015 | *sep = '\0'; | ||
1016 | |||
1017 | snprintf(name, sizeof(name), "[%s]", line); | ||
1018 | dso = dso__new(name, sym_priv_size); | ||
1019 | |||
1020 | if (dso == NULL) | ||
1021 | goto out_delete_line; | ||
1022 | |||
1023 | map = map__new2(start, dso); | ||
1024 | if (map == NULL) { | ||
1025 | dso__delete(dso); | ||
1026 | goto out_delete_line; | ||
888 | } | 1027 | } |
889 | prev = pos; | 1028 | |
1029 | dso->origin = DSO__ORIG_KMODULE; | ||
1030 | kernel_maps__insert(map); | ||
1031 | dsos__add(dso); | ||
890 | } | 1032 | } |
1033 | |||
1034 | free(line); | ||
1035 | fclose(file); | ||
1036 | |||
1037 | v = 1; | ||
1038 | return dsos__load_modules_sym(filter, v); | ||
1039 | |||
1040 | out_delete_line: | ||
1041 | free(line); | ||
1042 | out_failure: | ||
1043 | return -1; | ||
891 | } | 1044 | } |
892 | 1045 | ||
893 | static int dso__load_vmlinux(struct dso *self, const char *vmlinux, | 1046 | static int dso__load_vmlinux(struct dso *self, struct map *map, |
1047 | const char *vmlinux, | ||
894 | symbol_filter_t filter, int v) | 1048 | symbol_filter_t filter, int v) |
895 | { | 1049 | { |
896 | int err, fd = open(vmlinux, O_RDONLY); | 1050 | int err, fd = open(vmlinux, O_RDONLY); |
@@ -898,28 +1052,36 @@ static int dso__load_vmlinux(struct dso *self, const char *vmlinux, | |||
898 | if (fd < 0) | 1052 | if (fd < 0) |
899 | return -1; | 1053 | return -1; |
900 | 1054 | ||
901 | err = dso__load_sym(self, fd, vmlinux, filter, v, NULL); | 1055 | err = dso__load_sym(self, map, self->long_name, fd, filter, 1, 0, v); |
902 | |||
903 | if (err > 0) | ||
904 | dso__fill_symbol_holes(self); | ||
905 | 1056 | ||
906 | close(fd); | 1057 | close(fd); |
907 | 1058 | ||
908 | return err; | 1059 | return err; |
909 | } | 1060 | } |
910 | 1061 | ||
911 | int dso__load_kernel(struct dso *self, const char *vmlinux, | 1062 | int dsos__load_kernel(const char *vmlinux, unsigned int sym_priv_size, |
912 | symbol_filter_t filter, int v, int use_modules) | 1063 | symbol_filter_t filter, int v, int use_modules) |
913 | { | 1064 | { |
914 | int err = -1; | 1065 | int err = -1; |
1066 | struct dso *dso = dso__new(vmlinux, sym_priv_size); | ||
1067 | |||
1068 | if (dso == NULL) | ||
1069 | return -1; | ||
1070 | |||
1071 | dso->short_name = "[kernel]"; | ||
1072 | kernel_map = map__new2(0, dso); | ||
1073 | if (kernel_map == NULL) | ||
1074 | goto out_delete_dso; | ||
1075 | |||
1076 | kernel_map->map_ip = vdso__map_ip; | ||
915 | 1077 | ||
916 | if (vmlinux) { | 1078 | if (vmlinux) { |
917 | err = dso__load_vmlinux(self, vmlinux, filter, v); | 1079 | err = dso__load_vmlinux(dso, kernel_map, vmlinux, filter, v); |
918 | if (err > 0 && use_modules) { | 1080 | if (err > 0 && use_modules) { |
919 | int syms = dso__load_modules(self, filter, v); | 1081 | int syms = dsos__load_modules(sym_priv_size, filter, v); |
920 | 1082 | ||
921 | if (syms < 0) { | 1083 | if (syms < 0) { |
922 | fprintf(stderr, "dso__load_modules failed!\n"); | 1084 | fprintf(stderr, "dsos__load_modules failed!\n"); |
923 | return syms; | 1085 | return syms; |
924 | } | 1086 | } |
925 | err += syms; | 1087 | err += syms; |
@@ -927,18 +1089,34 @@ int dso__load_kernel(struct dso *self, const char *vmlinux, | |||
927 | } | 1089 | } |
928 | 1090 | ||
929 | if (err <= 0) | 1091 | if (err <= 0) |
930 | err = dso__load_kallsyms(self, filter, v); | 1092 | err = dso__load_kallsyms(dso, kernel_map, filter, v); |
1093 | |||
1094 | if (err > 0) { | ||
1095 | struct rb_node *node = rb_first(&dso->syms); | ||
1096 | struct symbol *sym = rb_entry(node, struct symbol, rb_node); | ||
931 | 1097 | ||
932 | if (err > 0) | 1098 | kernel_map->start = sym->start; |
933 | self->origin = DSO__ORIG_KERNEL; | 1099 | node = rb_last(&dso->syms); |
1100 | sym = rb_entry(node, struct symbol, rb_node); | ||
1101 | kernel_map->end = sym->end; | ||
1102 | |||
1103 | dso->origin = DSO__ORIG_KERNEL; | ||
1104 | /* | ||
1105 | * XXX See kernel_maps__find_symbol comment | ||
1106 | * kernel_maps__insert(kernel_map) | ||
1107 | */ | ||
1108 | dsos__add(dso); | ||
1109 | } | ||
934 | 1110 | ||
935 | return err; | 1111 | return err; |
1112 | |||
1113 | out_delete_dso: | ||
1114 | dso__delete(dso); | ||
1115 | return -1; | ||
936 | } | 1116 | } |
937 | 1117 | ||
938 | LIST_HEAD(dsos); | 1118 | LIST_HEAD(dsos); |
939 | struct dso *kernel_dso; | ||
940 | struct dso *vdso; | 1119 | struct dso *vdso; |
941 | struct dso *hypervisor_dso; | ||
942 | 1120 | ||
943 | const char *vmlinux_name = "vmlinux"; | 1121 | const char *vmlinux_name = "vmlinux"; |
944 | int modules; | 1122 | int modules; |
@@ -970,7 +1148,7 @@ struct dso *dsos__findnew(const char *name) | |||
970 | if (!dso) | 1148 | if (!dso) |
971 | goto out_delete_dso; | 1149 | goto out_delete_dso; |
972 | 1150 | ||
973 | nr = dso__load(dso, NULL, verbose); | 1151 | nr = dso__load(dso, NULL, NULL, verbose); |
974 | if (nr < 0) { | 1152 | if (nr < 0) { |
975 | eprintf("Failed to open: %s\n", name); | 1153 | eprintf("Failed to open: %s\n", name); |
976 | goto out_delete_dso; | 1154 | goto out_delete_dso; |
@@ -995,43 +1173,20 @@ void dsos__fprintf(FILE *fp) | |||
995 | dso__fprintf(pos, fp); | 1173 | dso__fprintf(pos, fp); |
996 | } | 1174 | } |
997 | 1175 | ||
998 | static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip) | ||
999 | { | ||
1000 | return dso__find_symbol(dso, ip); | ||
1001 | } | ||
1002 | |||
1003 | int load_kernel(void) | 1176 | int load_kernel(void) |
1004 | { | 1177 | { |
1005 | int err; | 1178 | if (dsos__load_kernel(vmlinux_name, 0, NULL, verbose, modules) <= 0) |
1006 | |||
1007 | kernel_dso = dso__new("[kernel]", 0); | ||
1008 | if (!kernel_dso) | ||
1009 | return -1; | 1179 | return -1; |
1010 | 1180 | ||
1011 | err = dso__load_kernel(kernel_dso, vmlinux_name, NULL, verbose, modules); | ||
1012 | if (err <= 0) { | ||
1013 | dso__delete(kernel_dso); | ||
1014 | kernel_dso = NULL; | ||
1015 | } else | ||
1016 | dsos__add(kernel_dso); | ||
1017 | |||
1018 | vdso = dso__new("[vdso]", 0); | 1181 | vdso = dso__new("[vdso]", 0); |
1019 | if (!vdso) | 1182 | if (!vdso) |
1020 | return -1; | 1183 | return -1; |
1021 | 1184 | ||
1022 | vdso->find_symbol = vdso__find_symbol; | ||
1023 | |||
1024 | dsos__add(vdso); | 1185 | dsos__add(vdso); |
1025 | 1186 | ||
1026 | hypervisor_dso = dso__new("[hypervisor]", 0); | 1187 | return 0; |
1027 | if (!hypervisor_dso) | ||
1028 | return -1; | ||
1029 | dsos__add(hypervisor_dso); | ||
1030 | |||
1031 | return err; | ||
1032 | } | 1188 | } |
1033 | 1189 | ||
1034 | |||
1035 | void symbol__init(void) | 1190 | void symbol__init(void) |
1036 | { | 1191 | { |
1037 | elf_version(EV_CURRENT); | 1192 | elf_version(EV_CURRENT); |