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>
			
			
This commit is contained in:
		
							parent
							
								
									17e44dc46f
								
							
						
					
					
						commit
						18425f13a0
					
				@ -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);
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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 */
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user