perf bpf: Improve BPF related error messages
authorWang Nan <wangnan0@huawei.com>
Fri, 6 Nov 2015 13:58:09 +0000 (13:58 +0000)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Fri, 6 Nov 2015 20:14:20 +0000 (17:14 -0300)
A series of bpf loader related error codes were introduced to help error
reporting. Functions were improved to return these new error codes.

Functions which return pointers were adjusted to encode error codes into
return value using the ERR_PTR() interface.

bpf_loader_strerror() was improved to convert these error messages to
strings. It checks the error codes and calls libbpf_strerror() and
strerror_r() accordingly, so caller don't need to consider checking the
range of the error code.

In bpf__strerror_load(), print kernel version of running kernel and the
object's 'version' section to notify user how to fix his/her program.

v1 -> v2:
 Use macro for error code.

 Fetch error message based on array index, eliminate for-loop.

 Print version strings.

Before:

  # perf record -e ./test_kversion_nomatch_program.o sleep 1
  event syntax error: './test_kversion_nomatch_program.o'
                       \___ Failed to load program: Validate your program and check 'license'/'version' sections in your object
  SKIP

  After:

  # perf record -e ./test_kversion_nomatch_program.o ls
  event syntax error: './test_kversion_nomatch_program.o'
                       \___ 'version' (4.4.0) doesn't match running kernel (4.3.0)
  SKIP

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1446818289-87444-1-git-send-email-wangnan0@huawei.com
[ Add 'static inline' to bpf__strerror_prepare_load() when LIBBPF is disabled ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/bpf-loader.c
tools/perf/util/bpf-loader.h
tools/perf/util/parse-events.c
tools/perf/util/util.h

index c46256b1f5fdec46d321f2d53b4f8c073e1bcf2d..e3afa1b60bb56bcd13db678ec0ea4d70b9197327 100644 (file)
@@ -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;
 }
index ccd8d7fd79d3443bde634534580883a19adde53e..5eb3629eed8bdd6e6d1797013c33f21a63ceee97 100644 (file)
@@ -8,9 +8,21 @@
 #include <linux/compiler.h>
 #include <linux/err.h>
 #include <string.h>
+#include <bpf/libbpf.h>
 #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,
index c75b25d5e28cb80f60b311a2d7e98a9f612e4530..e48d9da757078f0c35afae590ce87ac7892dafab 100644 (file)
@@ -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);
index 2665126267dcfe005d59447975d1ea2e844a8265..dcc659017976da2a90ae67e51b922bcb7bb99a51 100644 (file)
@@ -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 */