linux/tools/perf/util/bpf-loader.c
Wang Nan d3e0ce3930 perf bpf: Improve BPF related error messages
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>
2015-11-06 17:14:20 -03:00

436 lines
9.5 KiB
C

/*
* bpf-loader.c
*
* Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
* Copyright (C) 2015 Huawei Inc.
*/
#include <bpf/libbpf.h>
#include <linux/err.h>
#include "perf.h"
#include "debug.h"
#include "bpf-loader.h"
#include "probe-event.h"
#include "probe-finder.h" // for MAX_PROBES
#include "llvm-utils.h"
#define DEFINE_PRINT_FN(name, level) \
static int libbpf_##name(const char *fmt, ...) \
{ \
va_list args; \
int ret; \
\
va_start(args, fmt); \
ret = veprintf(level, verbose, pr_fmt(fmt), args);\
va_end(args); \
return ret; \
}
DEFINE_PRINT_FN(warning, 1)
DEFINE_PRINT_FN(info, 1)
DEFINE_PRINT_FN(debug, 1)
struct bpf_prog_priv {
struct perf_probe_event pev;
};
struct bpf_object *bpf__prepare_load(const char *filename, bool source)
{
struct bpf_object *obj;
static bool libbpf_initialized;
if (!libbpf_initialized) {
libbpf_set_print(libbpf_warning,
libbpf_info,
libbpf_debug);
libbpf_initialized = true;
}
if (source) {
int err;
void *obj_buf;
size_t obj_buf_sz;
err = llvm__compile_bpf(filename, &obj_buf, &obj_buf_sz);
if (err)
return ERR_PTR(-BPF_LOADER_ERRNO__COMPILE);
obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, filename);
free(obj_buf);
} else
obj = bpf_object__open(filename);
if (IS_ERR(obj)) {
pr_debug("bpf: failed to load %s\n", filename);
return obj;
}
return obj;
}
void bpf__clear(void)
{
struct bpf_object *obj, *tmp;
bpf_object__for_each_safe(obj, tmp) {
bpf__unprobe(obj);
bpf_object__close(obj);
}
}
static void
bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,
void *_priv)
{
struct bpf_prog_priv *priv = _priv;
cleanup_perf_probe_events(&priv->pev, 1);
free(priv);
}
static int
config_bpf_program(struct bpf_program *prog)
{
struct perf_probe_event *pev = NULL;
struct bpf_prog_priv *priv = NULL;
const char *config_str;
int err;
config_str = bpf_program__title(prog, false);
if (IS_ERR(config_str)) {
pr_debug("bpf: unable to get title for program\n");
return PTR_ERR(config_str);
}
priv = calloc(sizeof(*priv), 1);
if (!priv) {
pr_debug("bpf: failed to alloc priv\n");
return -ENOMEM;
}
pev = &priv->pev;
pr_debug("bpf: config program '%s'\n", config_str);
err = parse_perf_probe_command(config_str, pev);
if (err < 0) {
pr_debug("bpf: '%s' is not a valid config string\n",
config_str);
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 = -BPF_LOADER_ERRNO__GROUP;
goto errout;
} else if (!pev->group)
pev->group = strdup(PERF_BPF_PROBE_GROUP);
if (!pev->group) {
pr_debug("bpf: strdup failed\n");
err = -ENOMEM;
goto errout;
}
if (!pev->event) {
pr_debug("bpf: '%s': event name is missing. Section name should be 'key=value'\n",
config_str);
err = -BPF_LOADER_ERRNO__EVENTNAME;
goto errout;
}
pr_debug("bpf: config '%s' is ok\n", config_str);
err = bpf_program__set_private(prog, priv, bpf_prog_priv__clear);
if (err) {
pr_debug("Failed to set priv for program '%s'\n", config_str);
goto errout;
}
return 0;
errout:
if (pev)
clear_perf_probe_event(pev);
free(priv);
return err;
}
static int bpf__prepare_probe(void)
{
static int err = 0;
static bool initialized = false;
/*
* Make err static, so if init failed the first, bpf__prepare_probe()
* fails each time without calling init_probe_symbol_maps multiple
* times.
*/
if (initialized)
return err;
initialized = true;
err = init_probe_symbol_maps(false);
if (err < 0)
pr_debug("Failed to init_probe_symbol_maps\n");
probe_conf.max_probes = MAX_PROBES;
return err;
}
int bpf__probe(struct bpf_object *obj)
{
int err = 0;
struct bpf_program *prog;
struct bpf_prog_priv *priv;
struct perf_probe_event *pev;
err = bpf__prepare_probe();
if (err) {
pr_debug("bpf__prepare_probe failed\n");
return err;
}
bpf_object__for_each_program(prog, obj) {
err = config_bpf_program(prog);
if (err)
goto out;
err = bpf_program__get_private(prog, (void **)&priv);
if (err || !priv)
goto out;
pev = &priv->pev;
err = convert_perf_probe_events(pev, 1);
if (err < 0) {
pr_debug("bpf_probe: failed to convert perf probe events");
goto out;
}
err = apply_perf_probe_events(pev, 1);
if (err < 0) {
pr_debug("bpf_probe: failed to apply perf probe events");
goto out;
}
}
out:
return err < 0 ? err : 0;
}
#define EVENTS_WRITE_BUFSIZE 4096
int bpf__unprobe(struct bpf_object *obj)
{
int err, ret = 0;
struct bpf_program *prog;
struct bpf_prog_priv *priv;
bpf_object__for_each_program(prog, obj) {
int i;
err = bpf_program__get_private(prog, (void **)&priv);
if (err || !priv)
continue;
for (i = 0; i < priv->pev.ntevs; i++) {
struct probe_trace_event *tev = &priv->pev.tevs[i];
char name_buf[EVENTS_WRITE_BUFSIZE];
struct strfilter *delfilter;
snprintf(name_buf, EVENTS_WRITE_BUFSIZE,
"%s:%s", tev->group, tev->event);
name_buf[EVENTS_WRITE_BUFSIZE - 1] = '\0';
delfilter = strfilter__new(name_buf, NULL);
if (!delfilter) {
pr_debug("Failed to create filter for unprobing\n");
ret = -ENOMEM;
continue;
}
err = del_perf_probe_events(delfilter);
strfilter__delete(delfilter);
if (err) {
pr_debug("Failed to delete %s\n", name_buf);
ret = err;
continue;
}
}
}
return ret;
}
int bpf__load(struct bpf_object *obj)
{
int err;
err = bpf_object__load(obj);
if (err) {
pr_debug("bpf: load objects failed\n");
return err;
}
return 0;
}
int bpf__foreach_tev(struct bpf_object *obj,
bpf_prog_iter_callback_t func,
void *arg)
{
struct bpf_program *prog;
int err;
bpf_object__for_each_program(prog, obj) {
struct probe_trace_event *tev;
struct perf_probe_event *pev;
struct bpf_prog_priv *priv;
int i, fd;
err = bpf_program__get_private(prog,
(void **)&priv);
if (err || !priv) {
pr_debug("bpf: failed to get private field\n");
return -BPF_LOADER_ERRNO__INTERNAL;
}
pev = &priv->pev;
for (i = 0; i < pev->ntevs; i++) {
tev = &pev->tevs[i];
fd = bpf_program__fd(prog);
if (fd < 0) {
pr_debug("bpf: failed to get file descriptor\n");
return fd;
}
err = (*func)(tev, fd, arg);
if (err) {
pr_debug("bpf: call back failed, stop iterate\n");
return err;
}
}
}
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)
{
char sbuf[STRERR_BUFSIZE];
const char *msg;
if (!buf || !size)
return -1;
err = err > 0 ? err : -err;
if (err >= __LIBBPF_ERRNO__START)
return libbpf_strerror(err, buf, size);
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 -1;
}
#define bpf__strerror_head(err, buf, size) \
char sbuf[STRERR_BUFSIZE], *emsg;\
if (!size)\
return 0;\
if (err < 0)\
err = -err;\
bpf_loader_strerror(err, sbuf, sizeof(sbuf));\
emsg = sbuf;\
switch (err) {\
default:\
scnprintf(buf, size, "%s", emsg);\
break;
#define bpf__strerror_entry(val, fmt...)\
case val: {\
scnprintf(buf, size, fmt);\
break;\
}
#define bpf__strerror_end(buf, 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(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,
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;
}