diff options
Diffstat (limited to 'tools/perf/util/symbol.c')
-rw-r--r-- | tools/perf/util/symbol.c | 449 |
1 files changed, 382 insertions, 67 deletions
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 7eb0362f4ffd..c0c36965fff0 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -51,6 +51,7 @@ static enum dso_binary_type binary_type_symtab[] = { | |||
51 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, | 51 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, |
52 | DSO_BINARY_TYPE__GUEST_KMODULE, | 52 | DSO_BINARY_TYPE__GUEST_KMODULE, |
53 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, | 53 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, |
54 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, | ||
54 | DSO_BINARY_TYPE__NOT_FOUND, | 55 | DSO_BINARY_TYPE__NOT_FOUND, |
55 | }; | 56 | }; |
56 | 57 | ||
@@ -159,10 +160,12 @@ again: | |||
159 | 160 | ||
160 | if (choose_best_symbol(curr, next) == SYMBOL_A) { | 161 | if (choose_best_symbol(curr, next) == SYMBOL_A) { |
161 | rb_erase(&next->rb_node, symbols); | 162 | rb_erase(&next->rb_node, symbols); |
163 | symbol__delete(next); | ||
162 | goto again; | 164 | goto again; |
163 | } else { | 165 | } else { |
164 | nd = rb_next(&curr->rb_node); | 166 | nd = rb_next(&curr->rb_node); |
165 | rb_erase(&curr->rb_node, symbols); | 167 | rb_erase(&curr->rb_node, symbols); |
168 | symbol__delete(curr); | ||
166 | } | 169 | } |
167 | } | 170 | } |
168 | } | 171 | } |
@@ -499,6 +502,64 @@ out_failure: | |||
499 | return -1; | 502 | return -1; |
500 | } | 503 | } |
501 | 504 | ||
505 | int modules__parse(const char *filename, void *arg, | ||
506 | int (*process_module)(void *arg, const char *name, | ||
507 | u64 start)) | ||
508 | { | ||
509 | char *line = NULL; | ||
510 | size_t n; | ||
511 | FILE *file; | ||
512 | int err = 0; | ||
513 | |||
514 | file = fopen(filename, "r"); | ||
515 | if (file == NULL) | ||
516 | return -1; | ||
517 | |||
518 | while (1) { | ||
519 | char name[PATH_MAX]; | ||
520 | u64 start; | ||
521 | char *sep; | ||
522 | ssize_t line_len; | ||
523 | |||
524 | line_len = getline(&line, &n, file); | ||
525 | if (line_len < 0) { | ||
526 | if (feof(file)) | ||
527 | break; | ||
528 | err = -1; | ||
529 | goto out; | ||
530 | } | ||
531 | |||
532 | if (!line) { | ||
533 | err = -1; | ||
534 | goto out; | ||
535 | } | ||
536 | |||
537 | line[--line_len] = '\0'; /* \n */ | ||
538 | |||
539 | sep = strrchr(line, 'x'); | ||
540 | if (sep == NULL) | ||
541 | continue; | ||
542 | |||
543 | hex2u64(sep + 1, &start); | ||
544 | |||
545 | sep = strchr(line, ' '); | ||
546 | if (sep == NULL) | ||
547 | continue; | ||
548 | |||
549 | *sep = '\0'; | ||
550 | |||
551 | scnprintf(name, sizeof(name), "[%s]", line); | ||
552 | |||
553 | err = process_module(arg, name, start); | ||
554 | if (err) | ||
555 | break; | ||
556 | } | ||
557 | out: | ||
558 | free(line); | ||
559 | fclose(file); | ||
560 | return err; | ||
561 | } | ||
562 | |||
502 | struct process_kallsyms_args { | 563 | struct process_kallsyms_args { |
503 | struct map *map; | 564 | struct map *map; |
504 | struct dso *dso; | 565 | struct dso *dso; |
@@ -739,51 +800,242 @@ bool symbol__restricted_filename(const char *filename, | |||
739 | return restricted; | 800 | return restricted; |
740 | } | 801 | } |
741 | 802 | ||
742 | struct kcore_mapfn_data { | 803 | struct module_info { |
743 | struct dso *dso; | 804 | struct rb_node rb_node; |
744 | enum map_type type; | 805 | char *name; |
745 | struct list_head maps; | 806 | u64 start; |
746 | }; | 807 | }; |
747 | 808 | ||
748 | static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data) | 809 | static void add_module(struct module_info *mi, struct rb_root *modules) |
749 | { | 810 | { |
750 | struct kcore_mapfn_data *md = data; | 811 | struct rb_node **p = &modules->rb_node; |
751 | struct map *map; | 812 | struct rb_node *parent = NULL; |
813 | struct module_info *m; | ||
752 | 814 | ||
753 | map = map__new2(start, md->dso, md->type); | 815 | while (*p != NULL) { |
754 | if (map == NULL) | 816 | parent = *p; |
817 | m = rb_entry(parent, struct module_info, rb_node); | ||
818 | if (strcmp(mi->name, m->name) < 0) | ||
819 | p = &(*p)->rb_left; | ||
820 | else | ||
821 | p = &(*p)->rb_right; | ||
822 | } | ||
823 | rb_link_node(&mi->rb_node, parent, p); | ||
824 | rb_insert_color(&mi->rb_node, modules); | ||
825 | } | ||
826 | |||
827 | static void delete_modules(struct rb_root *modules) | ||
828 | { | ||
829 | struct module_info *mi; | ||
830 | struct rb_node *next = rb_first(modules); | ||
831 | |||
832 | while (next) { | ||
833 | mi = rb_entry(next, struct module_info, rb_node); | ||
834 | next = rb_next(&mi->rb_node); | ||
835 | rb_erase(&mi->rb_node, modules); | ||
836 | free(mi->name); | ||
837 | free(mi); | ||
838 | } | ||
839 | } | ||
840 | |||
841 | static struct module_info *find_module(const char *name, | ||
842 | struct rb_root *modules) | ||
843 | { | ||
844 | struct rb_node *n = modules->rb_node; | ||
845 | |||
846 | while (n) { | ||
847 | struct module_info *m; | ||
848 | int cmp; | ||
849 | |||
850 | m = rb_entry(n, struct module_info, rb_node); | ||
851 | cmp = strcmp(name, m->name); | ||
852 | if (cmp < 0) | ||
853 | n = n->rb_left; | ||
854 | else if (cmp > 0) | ||
855 | n = n->rb_right; | ||
856 | else | ||
857 | return m; | ||
858 | } | ||
859 | |||
860 | return NULL; | ||
861 | } | ||
862 | |||
863 | static int __read_proc_modules(void *arg, const char *name, u64 start) | ||
864 | { | ||
865 | struct rb_root *modules = arg; | ||
866 | struct module_info *mi; | ||
867 | |||
868 | mi = zalloc(sizeof(struct module_info)); | ||
869 | if (!mi) | ||
755 | return -ENOMEM; | 870 | return -ENOMEM; |
756 | 871 | ||
757 | map->end = map->start + len; | 872 | mi->name = strdup(name); |
758 | map->pgoff = pgoff; | 873 | mi->start = start; |
759 | 874 | ||
760 | list_add(&map->node, &md->maps); | 875 | if (!mi->name) { |
876 | free(mi); | ||
877 | return -ENOMEM; | ||
878 | } | ||
879 | |||
880 | add_module(mi, modules); | ||
881 | |||
882 | return 0; | ||
883 | } | ||
884 | |||
885 | static int read_proc_modules(const char *filename, struct rb_root *modules) | ||
886 | { | ||
887 | if (symbol__restricted_filename(filename, "/proc/modules")) | ||
888 | return -1; | ||
889 | |||
890 | if (modules__parse(filename, modules, __read_proc_modules)) { | ||
891 | delete_modules(modules); | ||
892 | return -1; | ||
893 | } | ||
761 | 894 | ||
762 | return 0; | 895 | return 0; |
763 | } | 896 | } |
764 | 897 | ||
898 | int compare_proc_modules(const char *from, const char *to) | ||
899 | { | ||
900 | struct rb_root from_modules = RB_ROOT; | ||
901 | struct rb_root to_modules = RB_ROOT; | ||
902 | struct rb_node *from_node, *to_node; | ||
903 | struct module_info *from_m, *to_m; | ||
904 | int ret = -1; | ||
905 | |||
906 | if (read_proc_modules(from, &from_modules)) | ||
907 | return -1; | ||
908 | |||
909 | if (read_proc_modules(to, &to_modules)) | ||
910 | goto out_delete_from; | ||
911 | |||
912 | from_node = rb_first(&from_modules); | ||
913 | to_node = rb_first(&to_modules); | ||
914 | while (from_node) { | ||
915 | if (!to_node) | ||
916 | break; | ||
917 | |||
918 | from_m = rb_entry(from_node, struct module_info, rb_node); | ||
919 | to_m = rb_entry(to_node, struct module_info, rb_node); | ||
920 | |||
921 | if (from_m->start != to_m->start || | ||
922 | strcmp(from_m->name, to_m->name)) | ||
923 | break; | ||
924 | |||
925 | from_node = rb_next(from_node); | ||
926 | to_node = rb_next(to_node); | ||
927 | } | ||
928 | |||
929 | if (!from_node && !to_node) | ||
930 | ret = 0; | ||
931 | |||
932 | delete_modules(&to_modules); | ||
933 | out_delete_from: | ||
934 | delete_modules(&from_modules); | ||
935 | |||
936 | return ret; | ||
937 | } | ||
938 | |||
939 | static int do_validate_kcore_modules(const char *filename, struct map *map, | ||
940 | struct map_groups *kmaps) | ||
941 | { | ||
942 | struct rb_root modules = RB_ROOT; | ||
943 | struct map *old_map; | ||
944 | int err; | ||
945 | |||
946 | err = read_proc_modules(filename, &modules); | ||
947 | if (err) | ||
948 | return err; | ||
949 | |||
950 | old_map = map_groups__first(kmaps, map->type); | ||
951 | while (old_map) { | ||
952 | struct map *next = map_groups__next(old_map); | ||
953 | struct module_info *mi; | ||
954 | |||
955 | if (old_map == map || old_map->start == map->start) { | ||
956 | /* The kernel map */ | ||
957 | old_map = next; | ||
958 | continue; | ||
959 | } | ||
960 | |||
961 | /* Module must be in memory at the same address */ | ||
962 | mi = find_module(old_map->dso->short_name, &modules); | ||
963 | if (!mi || mi->start != old_map->start) { | ||
964 | err = -EINVAL; | ||
965 | goto out; | ||
966 | } | ||
967 | |||
968 | old_map = next; | ||
969 | } | ||
970 | out: | ||
971 | delete_modules(&modules); | ||
972 | return err; | ||
973 | } | ||
974 | |||
765 | /* | 975 | /* |
766 | * If kallsyms is referenced by name then we look for kcore in the same | 976 | * If kallsyms is referenced by name then we look for filename in the same |
767 | * directory. | 977 | * directory. |
768 | */ | 978 | */ |
769 | static bool kcore_filename_from_kallsyms_filename(char *kcore_filename, | 979 | static bool filename_from_kallsyms_filename(char *filename, |
770 | const char *kallsyms_filename) | 980 | const char *base_name, |
981 | const char *kallsyms_filename) | ||
771 | { | 982 | { |
772 | char *name; | 983 | char *name; |
773 | 984 | ||
774 | strcpy(kcore_filename, kallsyms_filename); | 985 | strcpy(filename, kallsyms_filename); |
775 | name = strrchr(kcore_filename, '/'); | 986 | name = strrchr(filename, '/'); |
776 | if (!name) | 987 | if (!name) |
777 | return false; | 988 | return false; |
778 | 989 | ||
779 | if (!strcmp(name, "/kallsyms")) { | 990 | name += 1; |
780 | strcpy(name, "/kcore"); | 991 | |
992 | if (!strcmp(name, "kallsyms")) { | ||
993 | strcpy(name, base_name); | ||
781 | return true; | 994 | return true; |
782 | } | 995 | } |
783 | 996 | ||
784 | return false; | 997 | return false; |
785 | } | 998 | } |
786 | 999 | ||
1000 | static int validate_kcore_modules(const char *kallsyms_filename, | ||
1001 | struct map *map) | ||
1002 | { | ||
1003 | struct map_groups *kmaps = map__kmap(map)->kmaps; | ||
1004 | char modules_filename[PATH_MAX]; | ||
1005 | |||
1006 | if (!filename_from_kallsyms_filename(modules_filename, "modules", | ||
1007 | kallsyms_filename)) | ||
1008 | return -EINVAL; | ||
1009 | |||
1010 | if (do_validate_kcore_modules(modules_filename, map, kmaps)) | ||
1011 | return -EINVAL; | ||
1012 | |||
1013 | return 0; | ||
1014 | } | ||
1015 | |||
1016 | struct kcore_mapfn_data { | ||
1017 | struct dso *dso; | ||
1018 | enum map_type type; | ||
1019 | struct list_head maps; | ||
1020 | }; | ||
1021 | |||
1022 | static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data) | ||
1023 | { | ||
1024 | struct kcore_mapfn_data *md = data; | ||
1025 | struct map *map; | ||
1026 | |||
1027 | map = map__new2(start, md->dso, md->type); | ||
1028 | if (map == NULL) | ||
1029 | return -ENOMEM; | ||
1030 | |||
1031 | map->end = map->start + len; | ||
1032 | map->pgoff = pgoff; | ||
1033 | |||
1034 | list_add(&map->node, &md->maps); | ||
1035 | |||
1036 | return 0; | ||
1037 | } | ||
1038 | |||
787 | static int dso__load_kcore(struct dso *dso, struct map *map, | 1039 | static int dso__load_kcore(struct dso *dso, struct map *map, |
788 | const char *kallsyms_filename) | 1040 | const char *kallsyms_filename) |
789 | { | 1041 | { |
@@ -800,8 +1052,12 @@ static int dso__load_kcore(struct dso *dso, struct map *map, | |||
800 | if (map != machine->vmlinux_maps[map->type]) | 1052 | if (map != machine->vmlinux_maps[map->type]) |
801 | return -EINVAL; | 1053 | return -EINVAL; |
802 | 1054 | ||
803 | if (!kcore_filename_from_kallsyms_filename(kcore_filename, | 1055 | if (!filename_from_kallsyms_filename(kcore_filename, "kcore", |
804 | kallsyms_filename)) | 1056 | kallsyms_filename)) |
1057 | return -EINVAL; | ||
1058 | |||
1059 | /* All modules must be present at their original addresses */ | ||
1060 | if (validate_kcore_modules(kallsyms_filename, map)) | ||
805 | return -EINVAL; | 1061 | return -EINVAL; |
806 | 1062 | ||
807 | md.dso = dso; | 1063 | md.dso = dso; |
@@ -1188,6 +1444,105 @@ out: | |||
1188 | return err; | 1444 | return err; |
1189 | } | 1445 | } |
1190 | 1446 | ||
1447 | static int find_matching_kcore(struct map *map, char *dir, size_t dir_sz) | ||
1448 | { | ||
1449 | char kallsyms_filename[PATH_MAX]; | ||
1450 | struct dirent *dent; | ||
1451 | int ret = -1; | ||
1452 | DIR *d; | ||
1453 | |||
1454 | d = opendir(dir); | ||
1455 | if (!d) | ||
1456 | return -1; | ||
1457 | |||
1458 | while (1) { | ||
1459 | dent = readdir(d); | ||
1460 | if (!dent) | ||
1461 | break; | ||
1462 | if (dent->d_type != DT_DIR) | ||
1463 | continue; | ||
1464 | scnprintf(kallsyms_filename, sizeof(kallsyms_filename), | ||
1465 | "%s/%s/kallsyms", dir, dent->d_name); | ||
1466 | if (!validate_kcore_modules(kallsyms_filename, map)) { | ||
1467 | strlcpy(dir, kallsyms_filename, dir_sz); | ||
1468 | ret = 0; | ||
1469 | break; | ||
1470 | } | ||
1471 | } | ||
1472 | |||
1473 | closedir(d); | ||
1474 | |||
1475 | return ret; | ||
1476 | } | ||
1477 | |||
1478 | static char *dso__find_kallsyms(struct dso *dso, struct map *map) | ||
1479 | { | ||
1480 | u8 host_build_id[BUILD_ID_SIZE]; | ||
1481 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
1482 | bool is_host = false; | ||
1483 | char path[PATH_MAX]; | ||
1484 | |||
1485 | if (!dso->has_build_id) { | ||
1486 | /* | ||
1487 | * Last resort, if we don't have a build-id and couldn't find | ||
1488 | * any vmlinux file, try the running kernel kallsyms table. | ||
1489 | */ | ||
1490 | goto proc_kallsyms; | ||
1491 | } | ||
1492 | |||
1493 | if (sysfs__read_build_id("/sys/kernel/notes", host_build_id, | ||
1494 | sizeof(host_build_id)) == 0) | ||
1495 | is_host = dso__build_id_equal(dso, host_build_id); | ||
1496 | |||
1497 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); | ||
1498 | |||
1499 | /* Use /proc/kallsyms if possible */ | ||
1500 | if (is_host) { | ||
1501 | DIR *d; | ||
1502 | int fd; | ||
1503 | |||
1504 | /* If no cached kcore go with /proc/kallsyms */ | ||
1505 | scnprintf(path, sizeof(path), "%s/[kernel.kcore]/%s", | ||
1506 | buildid_dir, sbuild_id); | ||
1507 | d = opendir(path); | ||
1508 | if (!d) | ||
1509 | goto proc_kallsyms; | ||
1510 | closedir(d); | ||
1511 | |||
1512 | /* | ||
1513 | * Do not check the build-id cache, until we know we cannot use | ||
1514 | * /proc/kcore. | ||
1515 | */ | ||
1516 | fd = open("/proc/kcore", O_RDONLY); | ||
1517 | if (fd != -1) { | ||
1518 | close(fd); | ||
1519 | /* If module maps match go with /proc/kallsyms */ | ||
1520 | if (!validate_kcore_modules("/proc/kallsyms", map)) | ||
1521 | goto proc_kallsyms; | ||
1522 | } | ||
1523 | |||
1524 | /* Find kallsyms in build-id cache with kcore */ | ||
1525 | if (!find_matching_kcore(map, path, sizeof(path))) | ||
1526 | return strdup(path); | ||
1527 | |||
1528 | goto proc_kallsyms; | ||
1529 | } | ||
1530 | |||
1531 | scnprintf(path, sizeof(path), "%s/[kernel.kallsyms]/%s", | ||
1532 | buildid_dir, sbuild_id); | ||
1533 | |||
1534 | if (access(path, F_OK)) { | ||
1535 | pr_err("No kallsyms or vmlinux with build-id %s was found\n", | ||
1536 | sbuild_id); | ||
1537 | return NULL; | ||
1538 | } | ||
1539 | |||
1540 | return strdup(path); | ||
1541 | |||
1542 | proc_kallsyms: | ||
1543 | return strdup("/proc/kallsyms"); | ||
1544 | } | ||
1545 | |||
1191 | static int dso__load_kernel_sym(struct dso *dso, struct map *map, | 1546 | static int dso__load_kernel_sym(struct dso *dso, struct map *map, |
1192 | symbol_filter_t filter) | 1547 | symbol_filter_t filter) |
1193 | { | 1548 | { |
@@ -1214,7 +1569,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, | |||
1214 | goto do_kallsyms; | 1569 | goto do_kallsyms; |
1215 | } | 1570 | } |
1216 | 1571 | ||
1217 | if (symbol_conf.vmlinux_name != NULL) { | 1572 | if (!symbol_conf.ignore_vmlinux && symbol_conf.vmlinux_name != NULL) { |
1218 | err = dso__load_vmlinux(dso, map, | 1573 | err = dso__load_vmlinux(dso, map, |
1219 | symbol_conf.vmlinux_name, filter); | 1574 | symbol_conf.vmlinux_name, filter); |
1220 | if (err > 0) { | 1575 | if (err > 0) { |
@@ -1226,7 +1581,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, | |||
1226 | return err; | 1581 | return err; |
1227 | } | 1582 | } |
1228 | 1583 | ||
1229 | if (vmlinux_path != NULL) { | 1584 | if (!symbol_conf.ignore_vmlinux && vmlinux_path != NULL) { |
1230 | err = dso__load_vmlinux_path(dso, map, filter); | 1585 | err = dso__load_vmlinux_path(dso, map, filter); |
1231 | if (err > 0) | 1586 | if (err > 0) |
1232 | return err; | 1587 | return err; |
@@ -1236,51 +1591,11 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, | |||
1236 | if (symbol_conf.symfs[0] != 0) | 1591 | if (symbol_conf.symfs[0] != 0) |
1237 | return -1; | 1592 | return -1; |
1238 | 1593 | ||
1239 | /* | 1594 | kallsyms_allocated_filename = dso__find_kallsyms(dso, map); |
1240 | * Say the kernel DSO was created when processing the build-id header table, | 1595 | if (!kallsyms_allocated_filename) |
1241 | * we have a build-id, so check if it is the same as the running kernel, | 1596 | return -1; |
1242 | * using it if it is. | ||
1243 | */ | ||
1244 | if (dso->has_build_id) { | ||
1245 | u8 kallsyms_build_id[BUILD_ID_SIZE]; | ||
1246 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
1247 | |||
1248 | if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id, | ||
1249 | sizeof(kallsyms_build_id)) == 0) { | ||
1250 | if (dso__build_id_equal(dso, kallsyms_build_id)) { | ||
1251 | kallsyms_filename = "/proc/kallsyms"; | ||
1252 | goto do_kallsyms; | ||
1253 | } | ||
1254 | } | ||
1255 | /* | ||
1256 | * Now look if we have it on the build-id cache in | ||
1257 | * $HOME/.debug/[kernel.kallsyms]. | ||
1258 | */ | ||
1259 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), | ||
1260 | sbuild_id); | ||
1261 | |||
1262 | if (asprintf(&kallsyms_allocated_filename, | ||
1263 | "%s/.debug/[kernel.kallsyms]/%s", | ||
1264 | getenv("HOME"), sbuild_id) == -1) { | ||
1265 | pr_err("Not enough memory for kallsyms file lookup\n"); | ||
1266 | return -1; | ||
1267 | } | ||
1268 | |||
1269 | kallsyms_filename = kallsyms_allocated_filename; | ||
1270 | 1597 | ||
1271 | if (access(kallsyms_filename, F_OK)) { | 1598 | kallsyms_filename = kallsyms_allocated_filename; |
1272 | pr_err("No kallsyms or vmlinux with build-id %s " | ||
1273 | "was found\n", sbuild_id); | ||
1274 | free(kallsyms_allocated_filename); | ||
1275 | return -1; | ||
1276 | } | ||
1277 | } else { | ||
1278 | /* | ||
1279 | * Last resort, if we don't have a build-id and couldn't find | ||
1280 | * any vmlinux file, try the running kernel kallsyms table. | ||
1281 | */ | ||
1282 | kallsyms_filename = "/proc/kallsyms"; | ||
1283 | } | ||
1284 | 1599 | ||
1285 | do_kallsyms: | 1600 | do_kallsyms: |
1286 | err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); | 1601 | err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); |