diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index c46256b1f5fd..e3afa1b60bb5 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -53,7 +53,7 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source) err = llvm__compile_bpf(filename, &obj_buf, &obj_buf_sz); if (err) - return ERR_PTR(err); + return ERR_PTR(-BPF_LOADER_ERRNO__COMPILE); obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, filename); free(obj_buf); } else @@ -113,14 +113,14 @@ config_bpf_program(struct bpf_program *prog) if (err < 0) { pr_debug("bpf: '%s' is not a valid config string\n", config_str); - err = -EINVAL; + err = -BPF_LOADER_ERRNO__CONFIG; goto errout; } if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) { pr_debug("bpf: '%s': group for event is set and not '%s'.\n", config_str, PERF_BPF_PROBE_GROUP); - err = -EINVAL; + err = -BPF_LOADER_ERRNO__GROUP; goto errout; } else if (!pev->group) pev->group = strdup(PERF_BPF_PROBE_GROUP); @@ -132,9 +132,9 @@ config_bpf_program(struct bpf_program *prog) } if (!pev->event) { - pr_debug("bpf: '%s': event name is missing\n", + pr_debug("bpf: '%s': event name is missing. Section name should be 'key=value'\n", config_str); - err = -EINVAL; + err = -BPF_LOADER_ERRNO__EVENTNAME; goto errout; } pr_debug("bpf: config '%s' is ok\n", config_str); @@ -285,7 +285,7 @@ int bpf__foreach_tev(struct bpf_object *obj, (void **)&priv); if (err || !priv) { pr_debug("bpf: failed to get private field\n"); - return -EINVAL; + return -BPF_LOADER_ERRNO__INTERNAL; } pev = &priv->pev; @@ -308,6 +308,18 @@ int bpf__foreach_tev(struct bpf_object *obj, return 0; } +#define ERRNO_OFFSET(e) ((e) - __BPF_LOADER_ERRNO__START) +#define ERRCODE_OFFSET(c) ERRNO_OFFSET(BPF_LOADER_ERRNO__##c) +#define NR_ERRNO (__BPF_LOADER_ERRNO__END - __BPF_LOADER_ERRNO__START) + +static const char *bpf_loader_strerror_table[NR_ERRNO] = { + [ERRCODE_OFFSET(CONFIG)] = "Invalid config string", + [ERRCODE_OFFSET(GROUP)] = "Invalid group name", + [ERRCODE_OFFSET(EVENTNAME)] = "No event name found in config string", + [ERRCODE_OFFSET(INTERNAL)] = "BPF loader internal error", + [ERRCODE_OFFSET(COMPILE)] = "Error when compiling BPF scriptlet", +}; + static int bpf_loader_strerror(int err, char *buf, size_t size) { @@ -322,10 +334,21 @@ bpf_loader_strerror(int err, char *buf, size_t size) if (err >= __LIBBPF_ERRNO__START) return libbpf_strerror(err, buf, size); - msg = strerror_r(err, sbuf, sizeof(sbuf)); - snprintf(buf, size, "%s", msg); + if (err >= __BPF_LOADER_ERRNO__START && err < __BPF_LOADER_ERRNO__END) { + msg = bpf_loader_strerror_table[ERRNO_OFFSET(err)]; + snprintf(buf, size, "%s", msg); + buf[size - 1] = '\0'; + return 0; + } + + if (err >= __BPF_LOADER_ERRNO__END) + snprintf(buf, size, "Unknown bpf loader error %d", err); + else + snprintf(buf, size, "%s", + strerror_r(err, sbuf, sizeof(sbuf))); + buf[size - 1] = '\0'; - return 0; + return -1; } #define bpf__strerror_head(err, buf, size) \ @@ -351,21 +374,62 @@ bpf_loader_strerror(int err, char *buf, size_t size) }\ buf[size - 1] = '\0'; +int bpf__strerror_prepare_load(const char *filename, bool source, + int err, char *buf, size_t size) +{ + size_t n; + int ret; + + n = snprintf(buf, size, "Failed to load %s%s: ", + filename, source ? " from source" : ""); + if (n >= size) { + buf[size - 1] = '\0'; + return 0; + } + buf += n; + size -= n; + + ret = bpf_loader_strerror(err, buf, size); + buf[size - 1] = '\0'; + return ret; +} + int bpf__strerror_probe(struct bpf_object *obj __maybe_unused, int err, char *buf, size_t size) { bpf__strerror_head(err, buf, size); bpf__strerror_entry(EEXIST, "Probe point exist. Try use 'perf probe -d \"*\"'"); - bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0\n"); - bpf__strerror_entry(ENOENT, "You need to check probing points in BPF file\n"); + bpf__strerror_entry(EACCES, "You need to be root"); + bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0"); + bpf__strerror_entry(ENOENT, "You need to check probing points in BPF file"); bpf__strerror_end(buf, size); return 0; } -int bpf__strerror_load(struct bpf_object *obj __maybe_unused, +int bpf__strerror_load(struct bpf_object *obj, int err, char *buf, size_t size) { bpf__strerror_head(err, buf, size); + case LIBBPF_ERRNO__KVER: { + unsigned int obj_kver = bpf_object__get_kversion(obj); + unsigned int real_kver; + + if (fetch_kernel_version(&real_kver, NULL, 0)) { + scnprintf(buf, size, "Unable to fetch kernel version"); + break; + } + + if (obj_kver != real_kver) { + scnprintf(buf, size, + "'version' ("KVER_FMT") doesn't match running kernel ("KVER_FMT")", + KVER_PARAM(obj_kver), + KVER_PARAM(real_kver)); + break; + } + + scnprintf(buf, size, "Failed to load program for unknown reason"); + break; + } bpf__strerror_end(buf, size); return 0; } diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h index ccd8d7fd79d3..5eb3629eed8b 100644 --- a/tools/perf/util/bpf-loader.h +++ b/tools/perf/util/bpf-loader.h @@ -8,9 +8,21 @@ #include #include #include +#include #include "probe-event.h" #include "debug.h" +enum bpf_loader_errno { + __BPF_LOADER_ERRNO__START = __LIBBPF_ERRNO__START - 100, + /* Invalid config string */ + BPF_LOADER_ERRNO__CONFIG = __BPF_LOADER_ERRNO__START, + BPF_LOADER_ERRNO__GROUP, /* Invalid group name */ + BPF_LOADER_ERRNO__EVENTNAME, /* Event name is missing */ + BPF_LOADER_ERRNO__INTERNAL, /* BPF loader internal error */ + BPF_LOADER_ERRNO__COMPILE, /* Error when compiling BPF scriptlet */ + __BPF_LOADER_ERRNO__END, +}; + struct bpf_object; #define PERF_BPF_PROBE_GROUP "perf_bpf_probe" @@ -19,6 +31,8 @@ typedef int (*bpf_prog_iter_callback_t)(struct probe_trace_event *tev, #ifdef HAVE_LIBBPF_SUPPORT struct bpf_object *bpf__prepare_load(const char *filename, bool source); +int bpf__strerror_prepare_load(const char *filename, bool source, + int err, char *buf, size_t size); void bpf__clear(void); @@ -67,6 +81,15 @@ __bpf_strerror(char *buf, size_t size) return 0; } +static inline +int bpf__strerror_prepare_load(const char *filename __maybe_unused, + bool source __maybe_unused, + int err __maybe_unused, + char *buf, size_t size) +{ + return __bpf_strerror(buf, size); +} + static inline int bpf__strerror_probe(struct bpf_object *obj __maybe_unused, int err __maybe_unused, diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index c75b25d5e28c..e48d9da75707 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -642,9 +642,10 @@ int parse_events_load_bpf(struct parse_events_evlist *data, snprintf(errbuf, sizeof(errbuf), "BPF support is not compiled"); else - snprintf(errbuf, sizeof(errbuf), - "BPF object file '%s' is invalid", - bpf_file_name); + bpf__strerror_prepare_load(bpf_file_name, + source, + -err, errbuf, + sizeof(errbuf)); data->error->help = strdup("(add -v to see detail)"); data->error->str = strdup(errbuf); diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 2665126267dc..dcc659017976 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -352,5 +352,10 @@ int get_stack_size(const char *str, unsigned long *_size); int fetch_kernel_version(unsigned int *puint, char *str, size_t str_sz); +#define KVER_VERSION(x) (((x) >> 16) & 0xff) +#define KVER_PATCHLEVEL(x) (((x) >> 8) & 0xff) +#define KVER_SUBLEVEL(x) ((x) & 0xff) +#define KVER_FMT "%d.%d.%d" +#define KVER_PARAM(x) KVER_VERSION(x), KVER_PATCHLEVEL(x), KVER_SUBLEVEL(x) #endif /* GIT_COMPAT_UTIL_H */