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: default avatarIngo Molnar <mingo@kernel.org>
Acked-by: default avatarJiri 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.orgSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 17e44dc4
...@@ -757,8 +757,10 @@ static void perf_event__process_sample(struct perf_tool *tool, ...@@ -757,8 +757,10 @@ static void perf_event__process_sample(struct perf_tool *tool,
al.map == machine->vmlinux_maps[MAP__FUNCTION] && al.map == machine->vmlinux_maps[MAP__FUNCTION] &&
RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) {
if (symbol_conf.vmlinux_name) { if (symbol_conf.vmlinux_name) {
ui__warning("The %s file can't be used.\n%s", char serr[256];
symbol_conf.vmlinux_name, msg); dso__strerror_load(al.map->dso, serr, sizeof(serr));
ui__warning("The %s file can't be used: %s\n%s",
symbol_conf.vmlinux_name, serr, msg);
} else { } else {
ui__warning("A vmlinux file was not found.\n%s", ui__warning("A vmlinux file was not found.\n%s",
msg); msg);
......
...@@ -1137,3 +1137,36 @@ enum dso_type dso__type(struct dso *dso, struct machine *machine) ...@@ -1137,3 +1137,36 @@ enum dso_type dso__type(struct dso *dso, struct machine *machine)
return dso__type_fd(fd); return dso__type_fd(fd);
} }
int dso__strerror_load(struct dso *dso, char *buf, size_t buflen)
{
int idx, errnum = dso->load_errno;
/*
* This must have a same ordering as the enum dso_load_errno.
*/
static const char *dso_load__error_str[] = {
"Internal tools/perf/ library error",
"Invalid ELF file",
"Can not read build id",
"Mismatching build id",
"Decompression failure",
};
BUG_ON(buflen == 0);
if (errnum >= 0) {
const char *err = strerror_r(errnum, buf, buflen);
if (err != buf)
scnprintf(buf, buflen, "%s", err);
return 0;
}
if (errnum < __DSO_LOAD_ERRNO__START || errnum >= __DSO_LOAD_ERRNO__END)
return -1;
idx = errnum - __DSO_LOAD_ERRNO__START;
scnprintf(buf, buflen, "%s", dso_load__error_str[idx]);
return 0;
}
...@@ -60,6 +60,31 @@ enum dso_type { ...@@ -60,6 +60,31 @@ enum dso_type {
DSO__TYPE_X32BIT, DSO__TYPE_X32BIT,
}; };
enum dso_load_errno {
DSO_LOAD_ERRNO__SUCCESS = 0,
/*
* Choose an arbitrary negative big number not to clash with standard
* errno since SUS requires the errno has distinct positive values.
* See 'Issue 6' in the link below.
*
* http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
*/
__DSO_LOAD_ERRNO__START = -10000,
DSO_LOAD_ERRNO__INTERNAL_ERROR = __DSO_LOAD_ERRNO__START,
/* for symsrc__init() */
DSO_LOAD_ERRNO__INVALID_ELF,
DSO_LOAD_ERRNO__CANNOT_READ_BUILDID,
DSO_LOAD_ERRNO__MISMATCHING_BUILDID,
/* for decompress_kmodule */
DSO_LOAD_ERRNO__DECOMPRESSION_FAILURE,
__DSO_LOAD_ERRNO__END,
};
#define DSO__SWAP(dso, type, val) \ #define DSO__SWAP(dso, type, val) \
({ \ ({ \
type ____r = val; \ type ____r = val; \
...@@ -113,6 +138,7 @@ struct dso { ...@@ -113,6 +138,7 @@ struct dso {
enum dso_swap_type needs_swap; enum dso_swap_type needs_swap;
enum dso_binary_type symtab_type; enum dso_binary_type symtab_type;
enum dso_binary_type binary_type; enum dso_binary_type binary_type;
enum dso_load_errno load_errno;
u8 adjust_symbols:1; u8 adjust_symbols:1;
u8 has_build_id:1; u8 has_build_id:1;
u8 has_srcline:1; u8 has_srcline:1;
...@@ -294,4 +320,6 @@ void dso__free_a2l(struct dso *dso); ...@@ -294,4 +320,6 @@ void dso__free_a2l(struct dso *dso);
enum dso_type dso__type(struct dso *dso, struct machine *machine); enum dso_type dso__type(struct dso *dso, struct machine *machine);
int dso__strerror_load(struct dso *dso, char *buf, size_t buflen);
#endif /* __PERF_DSO */ #endif /* __PERF_DSO */
...@@ -595,10 +595,13 @@ static int decompress_kmodule(struct dso *dso, const char *name, ...@@ -595,10 +595,13 @@ static int decompress_kmodule(struct dso *dso, const char *name,
return -1; return -1;
fd = mkstemp(tmpbuf); fd = mkstemp(tmpbuf);
if (fd < 0) if (fd < 0) {
dso->load_errno = errno;
goto out; goto out;
}
if (!decompress_to_file(m.ext, name, fd)) { if (!decompress_to_file(m.ext, name, fd)) {
dso->load_errno = DSO_LOAD_ERRNO__DECOMPRESSION_FAILURE;
close(fd); close(fd);
fd = -1; fd = -1;
} }
...@@ -635,37 +638,49 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, ...@@ -635,37 +638,49 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
Elf *elf; Elf *elf;
int fd; int fd;
if (dso__needs_decompress(dso)) if (dso__needs_decompress(dso)) {
fd = decompress_kmodule(dso, name, type); fd = decompress_kmodule(dso, name, type);
else if (fd < 0)
return -1;
} else {
fd = open(name, O_RDONLY); fd = open(name, O_RDONLY);
if (fd < 0) {
if (fd < 0) dso->load_errno = errno;
return -1; return -1;
}
}
elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
if (elf == NULL) { if (elf == NULL) {
pr_debug("%s: cannot read %s ELF file.\n", __func__, name); pr_debug("%s: cannot read %s ELF file.\n", __func__, name);
dso->load_errno = DSO_LOAD_ERRNO__INVALID_ELF;
goto out_close; goto out_close;
} }
if (gelf_getehdr(elf, &ehdr) == NULL) { if (gelf_getehdr(elf, &ehdr) == NULL) {
dso->load_errno = DSO_LOAD_ERRNO__INVALID_ELF;
pr_debug("%s: cannot get elf header.\n", __func__); pr_debug("%s: cannot get elf header.\n", __func__);
goto out_elf_end; goto out_elf_end;
} }
if (dso__swap_init(dso, ehdr.e_ident[EI_DATA])) if (dso__swap_init(dso, ehdr.e_ident[EI_DATA])) {
dso->load_errno = DSO_LOAD_ERRNO__INTERNAL_ERROR;
goto out_elf_end; goto out_elf_end;
}
/* Always reject images with a mismatched build-id: */ /* Always reject images with a mismatched build-id: */
if (dso->has_build_id) { if (dso->has_build_id) {
u8 build_id[BUILD_ID_SIZE]; u8 build_id[BUILD_ID_SIZE];
if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0) if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0) {
dso->load_errno = DSO_LOAD_ERRNO__CANNOT_READ_BUILDID;
goto out_elf_end; goto out_elf_end;
}
if (!dso__build_id_equal(dso, build_id)) if (!dso__build_id_equal(dso, build_id)) {
dso->load_errno = DSO_LOAD_ERRNO__MISMATCHING_BUILDID;
goto out_elf_end; goto out_elf_end;
}
} }
ss->is_64_bit = (gelf_getclass(elf) == ELFCLASS64); 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,8 +716,10 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
} }
ss->name = strdup(name); ss->name = strdup(name);
if (!ss->name) if (!ss->name) {
dso->load_errno = errno;
goto out_elf_end; goto out_elf_end;
}
ss->elf = elf; ss->elf = elf;
ss->fd = fd; ss->fd = fd;
......
...@@ -246,13 +246,12 @@ int sysfs__read_build_id(const char *filename, void *build_id, size_t size) ...@@ -246,13 +246,12 @@ int sysfs__read_build_id(const char *filename, void *build_id, size_t size)
return ret; return ret;
} }
int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused, int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
const char *name,
enum dso_binary_type type) enum dso_binary_type type)
{ {
int fd = open(name, O_RDONLY); int fd = open(name, O_RDONLY);
if (fd < 0) if (fd < 0)
return -1; goto out_errno;
ss->name = strdup(name); ss->name = strdup(name);
if (!ss->name) if (!ss->name)
...@@ -264,6 +263,8 @@ int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused, ...@@ -264,6 +263,8 @@ int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused,
return 0; return 0;
out_close: out_close:
close(fd); close(fd);
out_errno:
dso->load_errno = errno;
return -1; return -1;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment