Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1246678

[PATCH 04/31] perf record, bpf: Create probe points for BPF programs

From Wang Nan <wangnan0@huawei.com>
Newsgroups linux.kernel
Subject [PATCH 04/31] perf record, bpf: Create probe points for BPF programs
Date 2015-10-14 15:00 +0200
Message-ID <qjtOY-7od-49@gated-at.bofh.it> (permalink)
References <qjtFf-7cv-3@gated-at.bofh.it>
Organization linux.* mail to news gateway

Show all headers | View raw


This patch introduces bpf__{un,}probe() functions to enable callers to
create kprobe points based on section names a BPF program. It parses
the section names in the program and creates corresponding 'struct
perf_probe_event' structures. The parse_perf_probe_command() function is
used to do the main parsing work. The resuling 'struct perf_probe_event'
is stored into program private data for further using.

By utilizing the new probing API, this patch creates probe points during
event parsing.

To ensure probe points be removed correctly, register an atexit hook
so even perf quit through exit() bpf__clear() is still called, so probing
points are cleared. Note that bpf_clear() should be registered before
bpf__probe() is called, so failure of bpf__probe() can still trigger
bpf__clear() to remove probe points which are already probed.

strerror style error reporting scaffold is created by this patch.
bpf__strerror_probe() is the first error reporting function in bpf-loader.c.

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Cc: Alexei Starovoitov <ast@plumgrid.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David Ahern <dsahern@gmail.com>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kaixu Xia <xiakaixu@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Link: http://lkml.kernel.org/n/ebpf-6yw9eg0ej3l4jnqhinngkw86@git.kernel.org
---
 tools/perf/util/bpf-loader.c   | 222 ++++++++++++++++++++++++++++++++++++++++-
 tools/perf/util/bpf-loader.h   |  30 ++++++
 tools/perf/util/parse-events.c |  17 ++++
 3 files changed, 268 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index ab56073..56f6fe8 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -10,6 +10,8 @@
 #include "perf.h"
 #include "debug.h"
 #include "bpf-loader.h"
+#include "probe-event.h"
+#include "probe-finder.h" // for MAX_PROBES
 
 #define DEFINE_PRINT_FN(name, level) \
 static int libbpf_##name(const char *fmt, ...)	\
@@ -27,6 +29,10 @@ DEFINE_PRINT_FN(warning, 0)
 DEFINE_PRINT_FN(info, 0)
 DEFINE_PRINT_FN(debug, 1)
 
+struct bpf_prog_priv {
+	struct perf_probe_event pev;
+};
+
 struct bpf_object *bpf__prepare_load(const char *filename)
 {
 	struct bpf_object *obj;
@@ -52,6 +58,220 @@ void bpf__clear(void)
 {
 	struct bpf_object *obj, *tmp;
 
-	bpf_object__for_each_safe(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 (!config_str) {
+		pr_debug("bpf: unable to get title for program\n");
+		return -EINVAL;
+	}
+
+	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 = -EINVAL;
+		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;
+		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\n",
+			 config_str);
+		err = -EINVAL;
+		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;
+}
+
+#define bpf__strerror_head(err, buf, size) \
+	char sbuf[STRERR_BUFSIZE], *emsg;\
+	if (!size)\
+		return 0;\
+	if (err < 0)\
+		err = -err;\
+	emsg = strerror_r(err, sbuf, sizeof(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_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_end(buf, size);
+	return 0;
 }
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index f402d7c..b819622 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -11,11 +11,18 @@
 #include "debug.h"
 
 struct bpf_object;
+#define PERF_BPF_PROBE_GROUP "perf_bpf_probe"
 
 #ifdef HAVE_LIBBPF_SUPPORT
 struct bpf_object *bpf__prepare_load(const char *filename);
 
 void bpf__clear(void);
+
+int bpf__probe(struct bpf_object *obj);
+int bpf__unprobe(struct bpf_object *obj);
+int bpf__strerror_probe(struct bpf_object *obj, int err,
+			char *buf, size_t size);
+
 #else
 static inline struct bpf_object *
 bpf__prepare_load(const char *filename __maybe_unused)
@@ -25,5 +32,28 @@ bpf__prepare_load(const char *filename __maybe_unused)
 }
 
 static inline void bpf__clear(void) { }
+
+static inline int bpf__probe(struct bpf_object *obj __maybe_unused) { return 0;}
+static inline int bpf__unprobe(struct bpf_object *obj __maybe_unused) { return 0;}
+
+static inline int
+__bpf_strerror(char *buf, size_t size)
+{
+	if (!size)
+		return 0;
+	strncpy(buf,
+		"ERROR: eBPF object loading is disabled during compiling.\n",
+		size);
+	buf[size - 1] = '\0';
+	return 0;
+}
+
+static inline int
+bpf__strerror_probe(struct bpf_object *obj __maybe_unused,
+		    int err __maybe_unused,
+		    char *buf, size_t size)
+{
+	return __bpf_strerror(buf, size);
+}
 #endif
 #endif
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index a02abd3..06ff8d6 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -536,6 +536,7 @@ int parse_events_load_bpf_obj(struct parse_events_evlist *data,
 {
 	int err;
 	char errbuf[BUFSIZ];
+	static bool registered_unprobe_atexit = false;
 
 	if (IS_ERR(obj) || !obj) {
 		snprintf(errbuf, sizeof(errbuf),
@@ -545,6 +546,22 @@ int parse_events_load_bpf_obj(struct parse_events_evlist *data,
 	}
 
 	/*
+	 * Register atexit handler before calling bpf__probe() so
+	 * bpf__probe() don't need to unprobe probe points its already
+	 * created when failure.
+	 */
+	if (!registered_unprobe_atexit) {
+		atexit(bpf__clear);
+		registered_unprobe_atexit = true;
+	}
+
+	err = bpf__probe(obj);
+	if (err) {
+		bpf__strerror_probe(obj, err, errbuf, sizeof(errbuf));
+		goto errout;
+	}
+
+	/*
 	 * Temporary add a dummy event here so we can check whether
 	 * basic bpf loader works. Following patches will replace
 	 * dummy event by useful evsels.
-- 
1.8.3.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Back to linux.kernel | Previous | NextPrevious in thread | Next in thread | Find similar | Unroll thread


Thread

[GIT PULL 00/31] perf tools: filtering events using eBPF programs Wang Nan <wangnan0@huawei.com> - 2015-10-14 14:50 +0200
  [PATCH 28/31] perf tools: Add API to config maps in bpf object Wang Nan <wangnan0@huawei.com> - 2015-10-14 14:50 +0200
  [PATCH 12/31] perf probe: Reset args and nargs for probe_trace_event when failure Wang Nan <wangnan0@huawei.com> - 2015-10-14 14:50 +0200
  [PATCH 31/31] perf tools: Enable BPF object configure syntax Wang Nan <wangnan0@huawei.com> - 2015-10-14 14:50 +0200
  [PATCH 06/31] perf tools: Collect perf_evsel in BPF object files Wang Nan <wangnan0@huawei.com> - 2015-10-14 14:50 +0200
  [PATCH 10/31] perf test: Enforce LLVM test for BPF test Wang Nan <wangnan0@huawei.com> - 2015-10-14 14:50 +0200
    Re: [PATCH 10/31] perf test: Enforce LLVM test for BPF test Namhyung Kim <namhyung@kernel.org> - 2015-10-14 18:00 +0200
      Re: [PATCH 10/31] perf test: Enforce LLVM test for BPF test "Wangnan (F)" <wangnan0@huawei.com> - 2015-10-15 14:10 +0200
  [PATCH 01/31] perf tools: Make perf depend on libbpf Wang Nan <wangnan0@huawei.com> - 2015-10-14 14:50 +0200
  [PATCH 29/31] perf tools: Add API to apply config to BPF map Wang Nan <wangnan0@huawei.com> - 2015-10-14 14:50 +0200
  [PATCH 22/31] perf test: Test BPF prologue Wang Nan <wangnan0@huawei.com> - 2015-10-14 15:00 +0200
  [PATCH 07/31] perf tools: Attach eBPF program to perf event Wang Nan <wangnan0@huawei.com> - 2015-10-14 15:00 +0200
  [PATCH 26/31] perf tools: Support perf event alias name Wang Nan <wangnan0@huawei.com> - 2015-10-14 15:00 +0200
  [PATCH 05/31] perf record: Load eBPF object into kernel Wang Nan <wangnan0@huawei.com> - 2015-10-14 15:00 +0200
  [PATCH 25/31] bpf tools: Extract and collect map names from BPF object file Wang Nan <wangnan0@huawei.com> - 2015-10-14 15:00 +0200
  [PATCH 18/31] perf tools: Use same BPF program if arguments are identical Wang Nan <wangnan0@huawei.com> - 2015-10-14 15:00 +0200
  [PATCH 04/31] perf record, bpf: Create probe points for BPF programs Wang Nan <wangnan0@huawei.com> - 2015-10-14 15:00 +0200
  [PATCH 21/31] perf test: Enforce LLVM test, add kbuild test Wang Nan <wangnan0@huawei.com> - 2015-10-14 15:00 +0200
  [PATCH 03/31] perf tools: Enable passing bpf object file to --event Wang Nan <wangnan0@huawei.com> - 2015-10-14 15:00 +0200
  Re: [GIT PULL 00/31] perf tools: filtering events using eBPF programs Namhyung Kim <namhyung@kernel.org> - 2015-10-14 17:50 +0200

csiph-web