perf symbols: Save DSO loading errno to better report errors
authorArnaldo Carvalho de Melo <acme@redhat.com>
Tue, 24 Mar 2015 14:49:02 +0000 (11:49 -0300)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Tue, 24 Mar 2015 15:08:43 +0000 (12:08 -0300)
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>
tools/perf/builtin-top.c
tools/perf/util/dso.c
tools/perf/util/dso.h
tools/perf/util/symbol-elf.c
tools/perf/util/symbol-minimal.c

index 5fb8723..1cb3436 100644 (file)
@@ -757,8 +757,10 @@ static void perf_event__process_sample(struct perf_tool *tool,
                    al.map == machine->vmlinux_maps[MAP__FUNCTION] &&
                    RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) {
                        if (symbol_conf.vmlinux_name) {
-                               ui__warning("The %s file can't be used.\n%s",
-                                           symbol_conf.vmlinux_name, msg);
+                               char serr[256];
+                               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 {
                                ui__warning("A vmlinux file was not found.\n%s",
                                            msg);
index 0d3667f..fc0ddd5 100644 (file)
@@ -1137,3 +1137,36 @@ enum dso_type dso__type(struct dso *dso, struct machine *machine)
 
        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;
+}
index 88f345c..e0901b4 100644 (file)
@@ -60,6 +60,31 @@ enum dso_type {
        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)                      \
 ({                                                     \
        type ____r = val;                               \
@@ -113,6 +138,7 @@ struct dso {
        enum dso_swap_type      needs_swap;
        enum dso_binary_type    symtab_type;
        enum dso_binary_type    binary_type;
+       enum dso_load_errno     load_errno;
        u8               adjust_symbols:1;
        u8               has_build_id:1;
        u8               has_srcline:1;
@@ -294,4 +320,6 @@ void dso__free_a2l(struct dso *dso);
 
 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 */
index 78ffde9..476268c 100644 (file)
@@ -595,10 +595,13 @@ static int decompress_kmodule(struct dso *dso, const char *name,
                return -1;
 
        fd = mkstemp(tmpbuf);
-       if (fd < 0)
+       if (fd < 0) {
+               dso->load_errno = errno;
                goto out;
+       }
 
        if (!decompress_to_file(m.ext, name, fd)) {
+               dso->load_errno = DSO_LOAD_ERRNO__DECOMPRESSION_FAILURE;
                close(fd);
                fd = -1;
        }
@@ -635,37 +638,49 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
        Elf *elf;
        int fd;
 
-       if (dso__needs_decompress(dso))
+       if (dso__needs_decompress(dso)) {
                fd = decompress_kmodule(dso, name, type);
-       else
+               if (fd < 0)
+                       return -1;
+       } else {
                fd = open(name, O_RDONLY);
-
-       if (fd < 0)
-               return -1;
+               if (fd < 0) {
+                       dso->load_errno = errno;
+                       return -1;
+               }
+       }
 
        elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
        if (elf == NULL) {
                pr_debug("%s: cannot read %s ELF file.\n", __func__, name);
+               dso->load_errno = DSO_LOAD_ERRNO__INVALID_ELF;
                goto out_close;
        }
 
        if (gelf_getehdr(elf, &ehdr) == NULL) {
+               dso->load_errno = DSO_LOAD_ERRNO__INVALID_ELF;
                pr_debug("%s: cannot get elf header.\n", __func__);
                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;
+       }
 
        /* Always reject images with a mismatched build-id: */
        if (dso->has_build_id) {
                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;
+               }
 
-               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;
+               }
        }
 
        ss->is_64_bit = (gelf_getclass(elf) == ELFCLASS64);
@@ -701,8 +716,10 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
        }
 
        ss->name   = strdup(name);
-       if (!ss->name)
+       if (!ss->name) {
+               dso->load_errno = errno;
                goto out_elf_end;
+       }
 
        ss->elf    = elf;
        ss->fd     = fd;
index d7efb03..fd8477c 100644 (file)
@@ -246,13 +246,12 @@ out:
        return ret;
 }
 
-int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused,
-                const char *name,
+int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
                 enum dso_binary_type type)
 {
        int fd = open(name, O_RDONLY);
        if (fd < 0)
-               return -1;
+               goto out_errno;
 
        ss->name = strdup(name);
        if (!ss->name)
@@ -264,6 +263,8 @@ int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused,
        return 0;
 out_close:
        close(fd);
+out_errno:
+       dso->load_errno = errno;
        return -1;
 }