diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2015-03-24 10:49:02 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2015-03-24 11:08:43 -0400 |
commit | 18425f13a0890ac1e88a64276771c1ae10030b4a (patch) | |
tree | b5970c64fcf4e8716dcfe865c5e0b1f22c6451d1 /tools | |
parent | 17e44dc46f035ca27847bbf75ffd3072ed49f13c (diff) |
perf symbols: Save DSO loading errno to better report errors
Before, when some problem happened while trying to load the kernel
symtab, 'perf top' would show:
┌─Warning:───────────────────────────┐
│The vmlinux file can't be used. │
│Kernel samples will not be resolved.│
│ │
│ │
│Press any key... │
└────────────────────────────────────┘
Now, it reports:
# perf top --vmlinux /dev/null
┌─Warning:───────────────────────────────────────────┐
│The /tmp/passwd file can't be used: Invalid ELF file│
│Kernel samples will not be resolved. │
│ │
│ │
│Press any key... │
└────────────────────────────────────────────────────┘
This is possible because we now register the reason for not being able
to load the symtab in the dso->load_errno member, and provide a
dso__strerror_load() routine to format this error into a strerror like
string with a short reason for the error while loading.
That can be just forwarding the dso__strerror_load() call to
strerror_r(), or, for a separate errno range providing a custom message.
Reported-by: Ingo Molnar <mingo@kernel.org>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Borislav Petkov <bp@suse.de>
Cc: David Ahern <dsahern@gmail.com>
Cc: Don Zickus <dzickus@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/n/tip-u5rb5uq63xqhkfb8uv2lxd5u@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/perf/builtin-top.c | 6 | ||||
-rw-r--r-- | tools/perf/util/dso.c | 33 | ||||
-rw-r--r-- | tools/perf/util/dso.h | 28 | ||||
-rw-r--r-- | tools/perf/util/symbol-elf.c | 37 | ||||
-rw-r--r-- | tools/perf/util/symbol-minimal.c | 7 |
5 files changed, 96 insertions, 15 deletions
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 5fb8723c7128..1cb3436276d1 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -757,8 +757,10 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
757 | al.map == machine->vmlinux_maps[MAP__FUNCTION] && | 757 | al.map == machine->vmlinux_maps[MAP__FUNCTION] && |
758 | RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { | 758 | RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { |
759 | if (symbol_conf.vmlinux_name) { | 759 | if (symbol_conf.vmlinux_name) { |
760 | ui__warning("The %s file can't be used.\n%s", | 760 | char serr[256]; |
761 | symbol_conf.vmlinux_name, msg); | 761 | dso__strerror_load(al.map->dso, serr, sizeof(serr)); |
762 | ui__warning("The %s file can't be used: %s\n%s", | ||
763 | symbol_conf.vmlinux_name, serr, msg); | ||
762 | } else { | 764 | } else { |
763 | ui__warning("A vmlinux file was not found.\n%s", | 765 | ui__warning("A vmlinux file was not found.\n%s", |
764 | msg); | 766 | msg); |
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 0d3667f92023..fc0ddd5792a9 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c | |||
@@ -1137,3 +1137,36 @@ enum dso_type dso__type(struct dso *dso, struct machine *machine) | |||
1137 | 1137 | ||
1138 | return dso__type_fd(fd); | 1138 | return dso__type_fd(fd); |
1139 | } | 1139 | } |
1140 | |||
1141 | int dso__strerror_load(struct dso *dso, char *buf, size_t buflen) | ||
1142 | { | ||
1143 | int idx, errnum = dso->load_errno; | ||
1144 | /* | ||
1145 | * This must have a same ordering as the enum dso_load_errno. | ||
1146 | */ | ||
1147 | static const char *dso_load__error_str[] = { | ||
1148 | "Internal tools/perf/ library error", | ||
1149 | "Invalid ELF file", | ||
1150 | "Can not read build id", | ||
1151 | "Mismatching build id", | ||
1152 | "Decompression failure", | ||
1153 | }; | ||
1154 | |||
1155 | BUG_ON(buflen == 0); | ||
1156 | |||
1157 | if (errnum >= 0) { | ||
1158 | const char *err = strerror_r(errnum, buf, buflen); | ||
1159 | |||
1160 | if (err != buf) | ||
1161 | scnprintf(buf, buflen, "%s", err); | ||
1162 | |||
1163 | return 0; | ||
1164 | } | ||
1165 | |||
1166 | if (errnum < __DSO_LOAD_ERRNO__START || errnum >= __DSO_LOAD_ERRNO__END) | ||
1167 | return -1; | ||
1168 | |||
1169 | idx = errnum - __DSO_LOAD_ERRNO__START; | ||
1170 | scnprintf(buf, buflen, "%s", dso_load__error_str[idx]); | ||
1171 | return 0; | ||
1172 | } | ||
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 88f345cc5be2..e0901b4ed8de 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h | |||
@@ -60,6 +60,31 @@ enum dso_type { | |||
60 | DSO__TYPE_X32BIT, | 60 | DSO__TYPE_X32BIT, |
61 | }; | 61 | }; |
62 | 62 | ||
63 | enum dso_load_errno { | ||
64 | DSO_LOAD_ERRNO__SUCCESS = 0, | ||
65 | |||
66 | /* | ||
67 | * Choose an arbitrary negative big number not to clash with standard | ||
68 | * errno since SUS requires the errno has distinct positive values. | ||
69 | * See 'Issue 6' in the link below. | ||
70 | * | ||
71 | * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html | ||
72 | */ | ||
73 | __DSO_LOAD_ERRNO__START = -10000, | ||
74 | |||
75 | DSO_LOAD_ERRNO__INTERNAL_ERROR = __DSO_LOAD_ERRNO__START, | ||
76 | |||
77 | /* for symsrc__init() */ | ||
78 | DSO_LOAD_ERRNO__INVALID_ELF, | ||
79 | DSO_LOAD_ERRNO__CANNOT_READ_BUILDID, | ||
80 | DSO_LOAD_ERRNO__MISMATCHING_BUILDID, | ||
81 | |||
82 | /* for decompress_kmodule */ | ||
83 | DSO_LOAD_ERRNO__DECOMPRESSION_FAILURE, | ||
84 | |||
85 | __DSO_LOAD_ERRNO__END, | ||
86 | }; | ||
87 | |||
63 | #define DSO__SWAP(dso, type, val) \ | 88 | #define DSO__SWAP(dso, type, val) \ |
64 | ({ \ | 89 | ({ \ |
65 | type ____r = val; \ | 90 | type ____r = val; \ |
@@ -113,6 +138,7 @@ struct dso { | |||
113 | enum dso_swap_type needs_swap; | 138 | enum dso_swap_type needs_swap; |
114 | enum dso_binary_type symtab_type; | 139 | enum dso_binary_type symtab_type; |
115 | enum dso_binary_type binary_type; | 140 | enum dso_binary_type binary_type; |
141 | enum dso_load_errno load_errno; | ||
116 | u8 adjust_symbols:1; | 142 | u8 adjust_symbols:1; |
117 | u8 has_build_id:1; | 143 | u8 has_build_id:1; |
118 | u8 has_srcline:1; | 144 | u8 has_srcline:1; |
@@ -294,4 +320,6 @@ void dso__free_a2l(struct dso *dso); | |||
294 | 320 | ||
295 | enum dso_type dso__type(struct dso *dso, struct machine *machine); | 321 | enum dso_type dso__type(struct dso *dso, struct machine *machine); |
296 | 322 | ||
323 | int dso__strerror_load(struct dso *dso, char *buf, size_t buflen); | ||
324 | |||
297 | #endif /* __PERF_DSO */ | 325 | #endif /* __PERF_DSO */ |
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 78ffde9df9bf..476268c99431 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
@@ -595,10 +595,13 @@ static int decompress_kmodule(struct dso *dso, const char *name, | |||
595 | return -1; | 595 | return -1; |
596 | 596 | ||
597 | fd = mkstemp(tmpbuf); | 597 | fd = mkstemp(tmpbuf); |
598 | if (fd < 0) | 598 | if (fd < 0) { |
599 | dso->load_errno = errno; | ||
599 | goto out; | 600 | goto out; |
601 | } | ||
600 | 602 | ||
601 | if (!decompress_to_file(m.ext, name, fd)) { | 603 | if (!decompress_to_file(m.ext, name, fd)) { |
604 | dso->load_errno = DSO_LOAD_ERRNO__DECOMPRESSION_FAILURE; | ||
602 | close(fd); | 605 | close(fd); |
603 | fd = -1; | 606 | fd = -1; |
604 | } | 607 | } |
@@ -635,37 +638,49 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, | |||
635 | Elf *elf; | 638 | Elf *elf; |
636 | int fd; | 639 | int fd; |
637 | 640 | ||
638 | if (dso__needs_decompress(dso)) | 641 | if (dso__needs_decompress(dso)) { |
639 | fd = decompress_kmodule(dso, name, type); | 642 | fd = decompress_kmodule(dso, name, type); |
640 | else | 643 | if (fd < 0) |
644 | return -1; | ||
645 | } else { | ||
641 | fd = open(name, O_RDONLY); | 646 | fd = open(name, O_RDONLY); |
642 | 647 | if (fd < 0) { | |
643 | if (fd < 0) | 648 | dso->load_errno = errno; |
644 | return -1; | 649 | return -1; |
650 | } | ||
651 | } | ||
645 | 652 | ||
646 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | 653 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); |
647 | if (elf == NULL) { | 654 | if (elf == NULL) { |
648 | pr_debug("%s: cannot read %s ELF file.\n", __func__, name); | 655 | pr_debug("%s: cannot read %s ELF file.\n", __func__, name); |
656 | dso->load_errno = DSO_LOAD_ERRNO__INVALID_ELF; | ||
649 | goto out_close; | 657 | goto out_close; |
650 | } | 658 | } |
651 | 659 | ||
652 | if (gelf_getehdr(elf, &ehdr) == NULL) { | 660 | if (gelf_getehdr(elf, &ehdr) == NULL) { |
661 | dso->load_errno = DSO_LOAD_ERRNO__INVALID_ELF; | ||
653 | pr_debug("%s: cannot get elf header.\n", __func__); | 662 | pr_debug("%s: cannot get elf header.\n", __func__); |
654 | goto out_elf_end; | 663 | goto out_elf_end; |
655 | } | 664 | } |
656 | 665 | ||
657 | if (dso__swap_init(dso, ehdr.e_ident[EI_DATA])) | 666 | if (dso__swap_init(dso, ehdr.e_ident[EI_DATA])) { |
667 | dso->load_errno = DSO_LOAD_ERRNO__INTERNAL_ERROR; | ||
658 | goto out_elf_end; | 668 | goto out_elf_end; |
669 | } | ||
659 | 670 | ||
660 | /* Always reject images with a mismatched build-id: */ | 671 | /* Always reject images with a mismatched build-id: */ |
661 | if (dso->has_build_id) { | 672 | if (dso->has_build_id) { |
662 | u8 build_id[BUILD_ID_SIZE]; | 673 | u8 build_id[BUILD_ID_SIZE]; |
663 | 674 | ||
664 | if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0) | 675 | if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0) { |
676 | dso->load_errno = DSO_LOAD_ERRNO__CANNOT_READ_BUILDID; | ||
665 | goto out_elf_end; | 677 | goto out_elf_end; |
678 | } | ||
666 | 679 | ||
667 | if (!dso__build_id_equal(dso, build_id)) | 680 | if (!dso__build_id_equal(dso, build_id)) { |
681 | dso->load_errno = DSO_LOAD_ERRNO__MISMATCHING_BUILDID; | ||
668 | goto out_elf_end; | 682 | goto out_elf_end; |
683 | } | ||
669 | } | 684 | } |
670 | 685 | ||
671 | ss->is_64_bit = (gelf_getclass(elf) == ELFCLASS64); | 686 | ss->is_64_bit = (gelf_getclass(elf) == ELFCLASS64); |
@@ -701,8 +716,10 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, | |||
701 | } | 716 | } |
702 | 717 | ||
703 | ss->name = strdup(name); | 718 | ss->name = strdup(name); |
704 | if (!ss->name) | 719 | if (!ss->name) { |
720 | dso->load_errno = errno; | ||
705 | goto out_elf_end; | 721 | goto out_elf_end; |
722 | } | ||
706 | 723 | ||
707 | ss->elf = elf; | 724 | ss->elf = elf; |
708 | ss->fd = fd; | 725 | ss->fd = fd; |
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index d7efb03b3f9a..fd8477cacf88 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c | |||
@@ -246,13 +246,12 @@ out: | |||
246 | return ret; | 246 | return ret; |
247 | } | 247 | } |
248 | 248 | ||
249 | int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused, | 249 | int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, |
250 | const char *name, | ||
251 | enum dso_binary_type type) | 250 | enum dso_binary_type type) |
252 | { | 251 | { |
253 | int fd = open(name, O_RDONLY); | 252 | int fd = open(name, O_RDONLY); |
254 | if (fd < 0) | 253 | if (fd < 0) |
255 | return -1; | 254 | goto out_errno; |
256 | 255 | ||
257 | ss->name = strdup(name); | 256 | ss->name = strdup(name); |
258 | if (!ss->name) | 257 | if (!ss->name) |
@@ -264,6 +263,8 @@ int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused, | |||
264 | return 0; | 263 | return 0; |
265 | out_close: | 264 | out_close: |
266 | close(fd); | 265 | close(fd); |
266 | out_errno: | ||
267 | dso->load_errno = errno; | ||
267 | return -1; | 268 | return -1; |
268 | } | 269 | } |
269 | 270 | ||