diff options
Diffstat (limited to 'tools/perf/util/symbol.c')
-rw-r--r-- | tools/perf/util/symbol.c | 285 |
1 files changed, 263 insertions, 22 deletions
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index d5528e1cc03a..7eb0362f4ffd 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -87,6 +87,7 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb) | |||
87 | { | 87 | { |
88 | s64 a; | 88 | s64 a; |
89 | s64 b; | 89 | s64 b; |
90 | size_t na, nb; | ||
90 | 91 | ||
91 | /* Prefer a symbol with non zero length */ | 92 | /* Prefer a symbol with non zero length */ |
92 | a = syma->end - syma->start; | 93 | a = syma->end - syma->start; |
@@ -120,11 +121,21 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb) | |||
120 | else if (a > b) | 121 | else if (a > b) |
121 | return SYMBOL_B; | 122 | return SYMBOL_B; |
122 | 123 | ||
123 | /* If all else fails, choose the symbol with the longest name */ | 124 | /* Choose the symbol with the longest name */ |
124 | if (strlen(syma->name) >= strlen(symb->name)) | 125 | na = strlen(syma->name); |
126 | nb = strlen(symb->name); | ||
127 | if (na > nb) | ||
125 | return SYMBOL_A; | 128 | return SYMBOL_A; |
126 | else | 129 | else if (na < nb) |
130 | return SYMBOL_B; | ||
131 | |||
132 | /* Avoid "SyS" kernel syscall aliases */ | ||
133 | if (na >= 3 && !strncmp(syma->name, "SyS", 3)) | ||
134 | return SYMBOL_B; | ||
135 | if (na >= 10 && !strncmp(syma->name, "compat_SyS", 10)) | ||
127 | return SYMBOL_B; | 136 | return SYMBOL_B; |
137 | |||
138 | return SYMBOL_A; | ||
128 | } | 139 | } |
129 | 140 | ||
130 | void symbols__fixup_duplicate(struct rb_root *symbols) | 141 | void symbols__fixup_duplicate(struct rb_root *symbols) |
@@ -248,7 +259,10 @@ size_t symbol__fprintf_symname_offs(const struct symbol *sym, | |||
248 | if (sym && sym->name) { | 259 | if (sym && sym->name) { |
249 | length = fprintf(fp, "%s", sym->name); | 260 | length = fprintf(fp, "%s", sym->name); |
250 | if (al) { | 261 | if (al) { |
251 | offset = al->addr - sym->start; | 262 | if (al->addr < sym->end) |
263 | offset = al->addr - sym->start; | ||
264 | else | ||
265 | offset = al->addr - al->map->start - sym->start; | ||
252 | length += fprintf(fp, "+0x%lx", offset); | 266 | length += fprintf(fp, "+0x%lx", offset); |
253 | } | 267 | } |
254 | return length; | 268 | return length; |
@@ -316,6 +330,16 @@ static struct symbol *symbols__find(struct rb_root *symbols, u64 ip) | |||
316 | return NULL; | 330 | return NULL; |
317 | } | 331 | } |
318 | 332 | ||
333 | static struct symbol *symbols__first(struct rb_root *symbols) | ||
334 | { | ||
335 | struct rb_node *n = rb_first(symbols); | ||
336 | |||
337 | if (n) | ||
338 | return rb_entry(n, struct symbol, rb_node); | ||
339 | |||
340 | return NULL; | ||
341 | } | ||
342 | |||
319 | struct symbol_name_rb_node { | 343 | struct symbol_name_rb_node { |
320 | struct rb_node rb_node; | 344 | struct rb_node rb_node; |
321 | struct symbol sym; | 345 | struct symbol sym; |
@@ -386,6 +410,11 @@ struct symbol *dso__find_symbol(struct dso *dso, | |||
386 | return symbols__find(&dso->symbols[type], addr); | 410 | return symbols__find(&dso->symbols[type], addr); |
387 | } | 411 | } |
388 | 412 | ||
413 | struct symbol *dso__first_symbol(struct dso *dso, enum map_type type) | ||
414 | { | ||
415 | return symbols__first(&dso->symbols[type]); | ||
416 | } | ||
417 | |||
389 | struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, | 418 | struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, |
390 | const char *name) | 419 | const char *name) |
391 | { | 420 | { |
@@ -522,6 +551,53 @@ static int dso__load_all_kallsyms(struct dso *dso, const char *filename, | |||
522 | return kallsyms__parse(filename, &args, map__process_kallsym_symbol); | 551 | return kallsyms__parse(filename, &args, map__process_kallsym_symbol); |
523 | } | 552 | } |
524 | 553 | ||
554 | static int dso__split_kallsyms_for_kcore(struct dso *dso, struct map *map, | ||
555 | symbol_filter_t filter) | ||
556 | { | ||
557 | struct map_groups *kmaps = map__kmap(map)->kmaps; | ||
558 | struct map *curr_map; | ||
559 | struct symbol *pos; | ||
560 | int count = 0, moved = 0; | ||
561 | struct rb_root *root = &dso->symbols[map->type]; | ||
562 | struct rb_node *next = rb_first(root); | ||
563 | |||
564 | while (next) { | ||
565 | char *module; | ||
566 | |||
567 | pos = rb_entry(next, struct symbol, rb_node); | ||
568 | next = rb_next(&pos->rb_node); | ||
569 | |||
570 | module = strchr(pos->name, '\t'); | ||
571 | if (module) | ||
572 | *module = '\0'; | ||
573 | |||
574 | curr_map = map_groups__find(kmaps, map->type, pos->start); | ||
575 | |||
576 | if (!curr_map || (filter && filter(curr_map, pos))) { | ||
577 | rb_erase(&pos->rb_node, root); | ||
578 | symbol__delete(pos); | ||
579 | } else { | ||
580 | pos->start -= curr_map->start - curr_map->pgoff; | ||
581 | if (pos->end) | ||
582 | pos->end -= curr_map->start - curr_map->pgoff; | ||
583 | if (curr_map != map) { | ||
584 | rb_erase(&pos->rb_node, root); | ||
585 | symbols__insert( | ||
586 | &curr_map->dso->symbols[curr_map->type], | ||
587 | pos); | ||
588 | ++moved; | ||
589 | } else { | ||
590 | ++count; | ||
591 | } | ||
592 | } | ||
593 | } | ||
594 | |||
595 | /* Symbols have been adjusted */ | ||
596 | dso->adjust_symbols = 1; | ||
597 | |||
598 | return count + moved; | ||
599 | } | ||
600 | |||
525 | /* | 601 | /* |
526 | * Split the symbols into maps, making sure there are no overlaps, i.e. the | 602 | * Split the symbols into maps, making sure there are no overlaps, i.e. the |
527 | * kernel range is broken in several maps, named [kernel].N, as we don't have | 603 | * kernel range is broken in several maps, named [kernel].N, as we don't have |
@@ -663,6 +739,161 @@ bool symbol__restricted_filename(const char *filename, | |||
663 | return restricted; | 739 | return restricted; |
664 | } | 740 | } |
665 | 741 | ||
742 | struct kcore_mapfn_data { | ||
743 | struct dso *dso; | ||
744 | enum map_type type; | ||
745 | struct list_head maps; | ||
746 | }; | ||
747 | |||
748 | static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data) | ||
749 | { | ||
750 | struct kcore_mapfn_data *md = data; | ||
751 | struct map *map; | ||
752 | |||
753 | map = map__new2(start, md->dso, md->type); | ||
754 | if (map == NULL) | ||
755 | return -ENOMEM; | ||
756 | |||
757 | map->end = map->start + len; | ||
758 | map->pgoff = pgoff; | ||
759 | |||
760 | list_add(&map->node, &md->maps); | ||
761 | |||
762 | return 0; | ||
763 | } | ||
764 | |||
765 | /* | ||
766 | * If kallsyms is referenced by name then we look for kcore in the same | ||
767 | * directory. | ||
768 | */ | ||
769 | static bool kcore_filename_from_kallsyms_filename(char *kcore_filename, | ||
770 | const char *kallsyms_filename) | ||
771 | { | ||
772 | char *name; | ||
773 | |||
774 | strcpy(kcore_filename, kallsyms_filename); | ||
775 | name = strrchr(kcore_filename, '/'); | ||
776 | if (!name) | ||
777 | return false; | ||
778 | |||
779 | if (!strcmp(name, "/kallsyms")) { | ||
780 | strcpy(name, "/kcore"); | ||
781 | return true; | ||
782 | } | ||
783 | |||
784 | return false; | ||
785 | } | ||
786 | |||
787 | static int dso__load_kcore(struct dso *dso, struct map *map, | ||
788 | const char *kallsyms_filename) | ||
789 | { | ||
790 | struct map_groups *kmaps = map__kmap(map)->kmaps; | ||
791 | struct machine *machine = kmaps->machine; | ||
792 | struct kcore_mapfn_data md; | ||
793 | struct map *old_map, *new_map, *replacement_map = NULL; | ||
794 | bool is_64_bit; | ||
795 | int err, fd; | ||
796 | char kcore_filename[PATH_MAX]; | ||
797 | struct symbol *sym; | ||
798 | |||
799 | /* This function requires that the map is the kernel map */ | ||
800 | if (map != machine->vmlinux_maps[map->type]) | ||
801 | return -EINVAL; | ||
802 | |||
803 | if (!kcore_filename_from_kallsyms_filename(kcore_filename, | ||
804 | kallsyms_filename)) | ||
805 | return -EINVAL; | ||
806 | |||
807 | md.dso = dso; | ||
808 | md.type = map->type; | ||
809 | INIT_LIST_HEAD(&md.maps); | ||
810 | |||
811 | fd = open(kcore_filename, O_RDONLY); | ||
812 | if (fd < 0) | ||
813 | return -EINVAL; | ||
814 | |||
815 | /* Read new maps into temporary lists */ | ||
816 | err = file__read_maps(fd, md.type == MAP__FUNCTION, kcore_mapfn, &md, | ||
817 | &is_64_bit); | ||
818 | if (err) | ||
819 | goto out_err; | ||
820 | |||
821 | if (list_empty(&md.maps)) { | ||
822 | err = -EINVAL; | ||
823 | goto out_err; | ||
824 | } | ||
825 | |||
826 | /* Remove old maps */ | ||
827 | old_map = map_groups__first(kmaps, map->type); | ||
828 | while (old_map) { | ||
829 | struct map *next = map_groups__next(old_map); | ||
830 | |||
831 | if (old_map != map) | ||
832 | map_groups__remove(kmaps, old_map); | ||
833 | old_map = next; | ||
834 | } | ||
835 | |||
836 | /* Find the kernel map using the first symbol */ | ||
837 | sym = dso__first_symbol(dso, map->type); | ||
838 | list_for_each_entry(new_map, &md.maps, node) { | ||
839 | if (sym && sym->start >= new_map->start && | ||
840 | sym->start < new_map->end) { | ||
841 | replacement_map = new_map; | ||
842 | break; | ||
843 | } | ||
844 | } | ||
845 | |||
846 | if (!replacement_map) | ||
847 | replacement_map = list_entry(md.maps.next, struct map, node); | ||
848 | |||
849 | /* Add new maps */ | ||
850 | while (!list_empty(&md.maps)) { | ||
851 | new_map = list_entry(md.maps.next, struct map, node); | ||
852 | list_del(&new_map->node); | ||
853 | if (new_map == replacement_map) { | ||
854 | map->start = new_map->start; | ||
855 | map->end = new_map->end; | ||
856 | map->pgoff = new_map->pgoff; | ||
857 | map->map_ip = new_map->map_ip; | ||
858 | map->unmap_ip = new_map->unmap_ip; | ||
859 | map__delete(new_map); | ||
860 | /* Ensure maps are correctly ordered */ | ||
861 | map_groups__remove(kmaps, map); | ||
862 | map_groups__insert(kmaps, map); | ||
863 | } else { | ||
864 | map_groups__insert(kmaps, new_map); | ||
865 | } | ||
866 | } | ||
867 | |||
868 | /* | ||
869 | * Set the data type and long name so that kcore can be read via | ||
870 | * dso__data_read_addr(). | ||
871 | */ | ||
872 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | ||
873 | dso->data_type = DSO_BINARY_TYPE__GUEST_KCORE; | ||
874 | else | ||
875 | dso->data_type = DSO_BINARY_TYPE__KCORE; | ||
876 | dso__set_long_name(dso, strdup(kcore_filename)); | ||
877 | |||
878 | close(fd); | ||
879 | |||
880 | if (map->type == MAP__FUNCTION) | ||
881 | pr_debug("Using %s for kernel object code\n", kcore_filename); | ||
882 | else | ||
883 | pr_debug("Using %s for kernel data\n", kcore_filename); | ||
884 | |||
885 | return 0; | ||
886 | |||
887 | out_err: | ||
888 | while (!list_empty(&md.maps)) { | ||
889 | map = list_entry(md.maps.next, struct map, node); | ||
890 | list_del(&map->node); | ||
891 | map__delete(map); | ||
892 | } | ||
893 | close(fd); | ||
894 | return -EINVAL; | ||
895 | } | ||
896 | |||
666 | int dso__load_kallsyms(struct dso *dso, const char *filename, | 897 | int dso__load_kallsyms(struct dso *dso, const char *filename, |
667 | struct map *map, symbol_filter_t filter) | 898 | struct map *map, symbol_filter_t filter) |
668 | { | 899 | { |
@@ -680,7 +911,10 @@ int dso__load_kallsyms(struct dso *dso, const char *filename, | |||
680 | else | 911 | else |
681 | dso->symtab_type = DSO_BINARY_TYPE__KALLSYMS; | 912 | dso->symtab_type = DSO_BINARY_TYPE__KALLSYMS; |
682 | 913 | ||
683 | return dso__split_kallsyms(dso, map, filter); | 914 | if (!dso__load_kcore(dso, map, filename)) |
915 | return dso__split_kallsyms_for_kcore(dso, map, filter); | ||
916 | else | ||
917 | return dso__split_kallsyms(dso, map, filter); | ||
684 | } | 918 | } |
685 | 919 | ||
686 | static int dso__load_perf_map(struct dso *dso, struct map *map, | 920 | static int dso__load_perf_map(struct dso *dso, struct map *map, |
@@ -843,10 +1077,15 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) | |||
843 | if (!runtime_ss && syms_ss) | 1077 | if (!runtime_ss && syms_ss) |
844 | runtime_ss = syms_ss; | 1078 | runtime_ss = syms_ss; |
845 | 1079 | ||
846 | if (syms_ss) | 1080 | if (syms_ss) { |
847 | ret = dso__load_sym(dso, map, syms_ss, runtime_ss, filter, 0); | 1081 | int km; |
848 | else | 1082 | |
1083 | km = dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE || | ||
1084 | dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE; | ||
1085 | ret = dso__load_sym(dso, map, syms_ss, runtime_ss, filter, km); | ||
1086 | } else { | ||
849 | ret = -1; | 1087 | ret = -1; |
1088 | } | ||
850 | 1089 | ||
851 | if (ret > 0) { | 1090 | if (ret > 0) { |
852 | int nr_plt; | 1091 | int nr_plt; |
@@ -888,8 +1127,11 @@ int dso__load_vmlinux(struct dso *dso, struct map *map, | |||
888 | char symfs_vmlinux[PATH_MAX]; | 1127 | char symfs_vmlinux[PATH_MAX]; |
889 | enum dso_binary_type symtab_type; | 1128 | enum dso_binary_type symtab_type; |
890 | 1129 | ||
891 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s%s", | 1130 | if (vmlinux[0] == '/') |
892 | symbol_conf.symfs, vmlinux); | 1131 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s", vmlinux); |
1132 | else | ||
1133 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s%s", | ||
1134 | symbol_conf.symfs, vmlinux); | ||
893 | 1135 | ||
894 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | 1136 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) |
895 | symtab_type = DSO_BINARY_TYPE__GUEST_VMLINUX; | 1137 | symtab_type = DSO_BINARY_TYPE__GUEST_VMLINUX; |
@@ -903,6 +1145,10 @@ int dso__load_vmlinux(struct dso *dso, struct map *map, | |||
903 | symsrc__destroy(&ss); | 1145 | symsrc__destroy(&ss); |
904 | 1146 | ||
905 | if (err > 0) { | 1147 | if (err > 0) { |
1148 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | ||
1149 | dso->data_type = DSO_BINARY_TYPE__GUEST_VMLINUX; | ||
1150 | else | ||
1151 | dso->data_type = DSO_BINARY_TYPE__VMLINUX; | ||
906 | dso__set_long_name(dso, (char *)vmlinux); | 1152 | dso__set_long_name(dso, (char *)vmlinux); |
907 | dso__set_loaded(dso, map->type); | 1153 | dso__set_loaded(dso, map->type); |
908 | pr_debug("Using %s for symbols\n", symfs_vmlinux); | 1154 | pr_debug("Using %s for symbols\n", symfs_vmlinux); |
@@ -975,7 +1221,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, | |||
975 | dso__set_long_name(dso, | 1221 | dso__set_long_name(dso, |
976 | strdup(symbol_conf.vmlinux_name)); | 1222 | strdup(symbol_conf.vmlinux_name)); |
977 | dso->lname_alloc = 1; | 1223 | dso->lname_alloc = 1; |
978 | goto out_fixup; | 1224 | return err; |
979 | } | 1225 | } |
980 | return err; | 1226 | return err; |
981 | } | 1227 | } |
@@ -983,7 +1229,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, | |||
983 | if (vmlinux_path != NULL) { | 1229 | if (vmlinux_path != NULL) { |
984 | err = dso__load_vmlinux_path(dso, map, filter); | 1230 | err = dso__load_vmlinux_path(dso, map, filter); |
985 | if (err > 0) | 1231 | if (err > 0) |
986 | goto out_fixup; | 1232 | return err; |
987 | } | 1233 | } |
988 | 1234 | ||
989 | /* do not try local files if a symfs was given */ | 1235 | /* do not try local files if a symfs was given */ |
@@ -1042,9 +1288,8 @@ do_kallsyms: | |||
1042 | pr_debug("Using %s for symbols\n", kallsyms_filename); | 1288 | pr_debug("Using %s for symbols\n", kallsyms_filename); |
1043 | free(kallsyms_allocated_filename); | 1289 | free(kallsyms_allocated_filename); |
1044 | 1290 | ||
1045 | if (err > 0) { | 1291 | if (err > 0 && !dso__is_kcore(dso)) { |
1046 | dso__set_long_name(dso, strdup("[kernel.kallsyms]")); | 1292 | dso__set_long_name(dso, strdup("[kernel.kallsyms]")); |
1047 | out_fixup: | ||
1048 | map__fixup_start(map); | 1293 | map__fixup_start(map); |
1049 | map__fixup_end(map); | 1294 | map__fixup_end(map); |
1050 | } | 1295 | } |
@@ -1075,7 +1320,7 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, | |||
1075 | if (symbol_conf.default_guest_vmlinux_name != NULL) { | 1320 | if (symbol_conf.default_guest_vmlinux_name != NULL) { |
1076 | err = dso__load_vmlinux(dso, map, | 1321 | err = dso__load_vmlinux(dso, map, |
1077 | symbol_conf.default_guest_vmlinux_name, filter); | 1322 | symbol_conf.default_guest_vmlinux_name, filter); |
1078 | goto out_try_fixup; | 1323 | return err; |
1079 | } | 1324 | } |
1080 | 1325 | ||
1081 | kallsyms_filename = symbol_conf.default_guest_kallsyms; | 1326 | kallsyms_filename = symbol_conf.default_guest_kallsyms; |
@@ -1089,13 +1334,9 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, | |||
1089 | err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); | 1334 | err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); |
1090 | if (err > 0) | 1335 | if (err > 0) |
1091 | pr_debug("Using %s for symbols\n", kallsyms_filename); | 1336 | pr_debug("Using %s for symbols\n", kallsyms_filename); |
1092 | 1337 | if (err > 0 && !dso__is_kcore(dso)) { | |
1093 | out_try_fixup: | 1338 | machine__mmap_name(machine, path, sizeof(path)); |
1094 | if (err > 0) { | 1339 | dso__set_long_name(dso, strdup(path)); |
1095 | if (kallsyms_filename != NULL) { | ||
1096 | machine__mmap_name(machine, path, sizeof(path)); | ||
1097 | dso__set_long_name(dso, strdup(path)); | ||
1098 | } | ||
1099 | map__fixup_start(map); | 1340 | map__fixup_start(map); |
1100 | map__fixup_end(map); | 1341 | map__fixup_end(map); |
1101 | } | 1342 | } |