2016-04-20 18:06:34

by Wang Nan

[permalink] [raw]
Subject: [RFC PATCH 00/13] perf tools: Support uBPF script

This patch set allows to perf invoke some user space BPF scripts on some
point. uBPF scripts and kernel BPF scripts reside in one BPF object.
They communicate with each other with BPF maps. uBPF scripts can invoke
helper functions provided by perf.

At least following new features can be achieved based on uBPF support:

1) Report statistical result:
Like DTrace, perf print statistical report before quit. No need to
extract data using 'perf report'. Statistical method is controled by
user.

2) Control perf's behavior:
Dynamically adjust period of different events. Policy is defined by
user.

uBPF library is required before compile. It can be found from github:

https://github.com/iovisor/ubpf.git

Following is an example:

Using BPF script attached at the bottom of this commit message, one
can print histogram of write size before perf exit like this:

# ~/perf record -a -e ./test_ubpf.c &
[1] 16800
# dd if=/dev/zero of=/dev/null bs=512 count=5000
5000+0 records in
5000+0 records out
2560000 bytes (2.6 MB) copied, 0.00552838 s, 463 MB/s
# dd if=/dev/zero of=/dev/null bs=2048 count=5000
5000+0 records in
5000+0 records out
10240000 bytes (10 MB) copied, 0.0188971 s, 542 MB/s
# fg
^C <--- *Press Ctrl-c*
2^^0: 47
2^^1: 13
2^^2: 4
2^^3: 130
2^^4: 11
2^^5: 1051
2^^6: 486
2^^7: 4863
2^^8: 0
2^^9: 5003
2^^10: 4
2^^11: 5003
2^^12: 1
2^^13: 0
2^^14: 0
2^^15: 0
2^^16: 0
2^^17: 0
2^^18: 0
2^^19: 0
2^^20: 0
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.788 MB perf.data ]

Here is test_ubpf.c.

/************ BEGIN ***************/
#include <uapi/linux/bpf.h>
#define SEC(NAME) __attribute__((section(NAME), used))
struct bpf_map_def {
unsigned int type;
unsigned int key_size;
unsigned int value_size;
unsigned int max_entries;
};

#define BPF_ANY 0

static void *(*map_lookup_elem)(struct bpf_map_def *, void *) =
(void *)BPF_FUNC_map_lookup_elem;

static inline unsigned int log2(unsigned int v)
{
unsigned int r;
unsigned int shift;

r = (v > 0xFFFF) << 4; v >>= r;
shift = (v > 0xFF) << 3; v >>= shift; r |= shift;
shift = (v > 0xF) << 2; v >>= shift; r |= shift;
shift = (v > 0x3) << 1; v >>= shift; r |= shift;
r |= (v >> 1);
return r;
}

static inline unsigned int log2l(unsigned long v)
{
unsigned int hi = v >> 32;
if (hi)
return log2(hi) + 32;
else
return log2(v);
}

struct bpf_map_def SEC("maps") my_hist_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(long),
.max_entries = 21,
};

SEC("sys_write=sys_write count")
int sys_write(void *ctx, int err, long write_size)
{
long *value;
int key = 0;

if (err)
return 0;

key = log2l(write_size);
if (key > 20)
key = 20;
value = map_lookup_elem(&my_hist_map, &key);
if (!value)
return 0;
__sync_fetch_and_add(value, 1);
return 0;
}
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;

/* Following ugly magic numbers can be find from tools/perf/util/ubpf-helpers-list.h */
static int (*ubpf_memcmp)(void *s1, void *s2, unsigned int n) = (void *)1;
static void (*ubpf_memcpy)(void *d, void *s, unsigned int size) = (void *)2;
static int (*ubpf_strcmp)(void *s1, void *s2) = (void *)3;
static int (*ubpf_printf)(char *fmt, ...) = (void *)4;
static int (*ubpf_map_lookup_elem)(void *map_desc, void *key, void *value) = (void *)5;
static int (*ubpf_map_update_elem)(void *map_desc, void *key, void *value, unsigned long long flags) = (void *)6;
static int (*ubpf_map_get_next_key)(void *map_desc, void *key, void *value) = (void *)7;

SEC("UBPF;perf_record_end")
int perf_record_end(int samples)
{
int i, key;
long value;
char fmt[] = "2^^%d: %d\n";

for (i = 0; i < 21; i++) {
ubpf_map_lookup_elem(&my_hist_map, &i, &value);
ubpf_printf(fmt, i, value);
}
return 0;
}
/************** END ***************/

Cc: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Li Zefan <[email protected]>

Wang Nan (13):
bpf tools: Add map related BPF helper
tools: Add ubpf feature test
bpf tools: Add ubpf include and makefile options
bpf tools: Replace fd array to union array
bpf tools: Save engine type in bpf_program
bpf tools: Introduce ubpf_vm to program instance union
bpf tools: Load ubpf program
bpf tools: Add API for fetching ubpf_vm
bpf tools: Register extern functions for ubpf programs
perf tools: Register basic UBPF helpers
perf bpf: Accept ubpf programs
perf record: Add UBPF hooks at beginning and end of perf record
perf tests: Add UBPF test case

tools/build/Makefile.feature | 6 +-
tools/build/feature/Makefile | 6 +-
tools/build/feature/test-ubpf.c | 11 ++
tools/lib/bpf/Makefile | 16 +-
tools/lib/bpf/bpf.c | 24 +++
tools/lib/bpf/bpf.h | 2 +
tools/lib/bpf/libbpf.c | 281 ++++++++++++++++++++++++++++----
tools/lib/bpf/libbpf.h | 21 ++-
tools/perf/Makefile.perf | 2 +
tools/perf/builtin-record.c | 4 +
tools/perf/config/Makefile | 7 +
tools/perf/perf.c | 3 +
tools/perf/tests/Build | 8 +
tools/perf/tests/bpf-script-test-ubpf.c | 68 ++++++++
tools/perf/tests/bpf.c | 77 ++++++++-
tools/perf/tests/llvm.c | 4 +
tools/perf/tests/llvm.h | 2 +
tools/perf/util/Build | 1 +
tools/perf/util/bpf-loader.c | 22 +++
tools/perf/util/ubpf-helpers-list.h | 7 +
tools/perf/util/ubpf-helpers.c | 66 ++++++++
tools/perf/util/ubpf-helpers.h | 21 +++
tools/perf/util/ubpf-hooks-list.h | 10 ++
tools/perf/util/ubpf-hooks.c | 81 +++++++++
tools/perf/util/ubpf-hooks.h | 35 ++++
25 files changed, 745 insertions(+), 40 deletions(-)
create mode 100644 tools/build/feature/test-ubpf.c
create mode 100644 tools/perf/tests/bpf-script-test-ubpf.c
create mode 100644 tools/perf/util/ubpf-helpers-list.h
create mode 100644 tools/perf/util/ubpf-helpers.c
create mode 100644 tools/perf/util/ubpf-helpers.h
create mode 100644 tools/perf/util/ubpf-hooks-list.h
create mode 100644 tools/perf/util/ubpf-hooks.c
create mode 100644 tools/perf/util/ubpf-hooks.h

--
1.8.3.4


2016-04-20 18:02:28

by Wang Nan

[permalink] [raw]
Subject: [RFC PATCH 01/13] bpf tools: Add map related BPF helper

Add BPF map helpers so user can read from bpf map in user space.
Following commits will introduce user space BPF, bpf_map will be
used to pass information between kernel and user BPF programs.

Signed-off-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Li Zefan <[email protected]>
---
tools/lib/bpf/bpf.c | 24 ++++++++++++++++++++++++
tools/lib/bpf/bpf.h | 2 ++
2 files changed, 26 insertions(+)

diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index 1f91cc9..337d9dc 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -97,3 +97,27 @@ int bpf_map_update_elem(int fd, void *key, void *value,

return sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
}
+
+int bpf_map_lookup_elem(int fd, void *key, void *value)
+{
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.map_fd = fd;
+ attr.key = ptr_to_u64(key);
+ attr.value = ptr_to_u64(value);
+
+ return sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
+}
+
+int bpf_map_get_next_key(int fd, void *key, void *next_key)
+{
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.map_fd = fd;
+ attr.key = ptr_to_u64(key);
+ attr.next_key = ptr_to_u64(next_key);
+
+ return sys_bpf(BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr));
+}
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
index a764655..c06ea21 100644
--- a/tools/lib/bpf/bpf.h
+++ b/tools/lib/bpf/bpf.h
@@ -22,4 +22,6 @@ int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns,

int bpf_map_update_elem(int fd, void *key, void *value,
u64 flags);
+int bpf_map_lookup_elem(int fd, void *key, void *value);
+int bpf_map_get_next_key(int fd, void *key, void *next_key);
#endif
--
1.8.3.4

2016-04-20 18:02:33

by Wang Nan

[permalink] [raw]
Subject: [RFC PATCH 07/13] bpf tools: Load ubpf program

In bpf_program__load(), load ubpf program according to its engine type.

API is improvemented to hold 'struct ubpf_vm *'.

Signed-off-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Li Zefan <[email protected]>
---
tools/lib/bpf/libbpf.c | 66 +++++++++++++++++++++++++++++++++++++++-----------
tools/lib/bpf/libbpf.h | 11 +++++++--
2 files changed, 61 insertions(+), 16 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 3a969fd..e4a1e77 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -81,6 +81,7 @@ static const char *libbpf_strerror_table[NR_ERRNO] = {
[ERRCODE_OFFSET(PROG2BIG)] = "Program too big",
[ERRCODE_OFFSET(KVER)] = "Incorrect kernel version",
[ERRCODE_OFFSET(NOUBPF)] = "UBPF support is not compiled",
+ [ERRCODE_OFFSET(LOADUBPF)] = "Failed to load user space BPF program",
};

int libbpf_strerror(int err, char *buf, size_t size)
@@ -949,6 +950,31 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
return 0;
}

+#ifdef HAVE_UBPF_SUPPORT
+static int
+load_ubpf_program(struct bpf_insn *insns, int insns_cnt,
+ struct ubpf_vm **pvm)
+{
+ struct ubpf_vm *vm = ubpf_create();
+ char *message;
+ int err;
+
+ if (!vm) {
+ pr_warning("Failed to create ubpf vm\n");
+ return -LIBBPF_ERRNO__LOADUBPF;
+ }
+
+ err = ubpf_load(vm, insns, insns_cnt * sizeof(insns[0]), &message);
+ if (err < 0) {
+ pr_warning("Failed to load ubpf program: %s\n", message);
+ return -LIBBPF_ERRNO__LOADUBPF;
+ }
+
+ *pvm = vm;
+ return 0;
+}
+#endif
+
static int
load_program(struct bpf_insn *insns, int insns_cnt,
char *license, u32 kern_version, int *pfd)
@@ -1002,11 +1028,12 @@ bpf_program__load(struct bpf_program *prog,
char *license, u32 kern_version)
{
int err = 0, fd, i;
+#ifdef HAVE_UBPF_SUPPORT
+ struct ubpf_vm *vm;
+#endif

if (prog->engine == ENGINE_UNKNOWN)
prog->engine = ENGINE_KBPF;
- if (prog->engine != ENGINE_KBPF)
- return -EINVAL;

if (prog->instances.nr < 0 || !prog->instances.array) {
if (prog->preprocessor) {
@@ -1029,10 +1056,15 @@ bpf_program__load(struct bpf_program *prog,
pr_warning("Program '%s' is inconsistent: nr(%d) != 1\n",
prog->section_name, prog->instances.nr);
}
- err = load_program(prog->insns, prog->insns_cnt,
- license, kern_version, &fd);
+
+ if_engine(prog,
+ (err = load_program(prog->insns, prog->insns_cnt,
+ license, kern_version, &fd)),
+ (err = load_ubpf_program(prog->insns, prog->insns_cnt,
+ &vm)));
+
if (!err)
- prog->instan_fd(0) = fd;
+ set_instance(prog, 0, fd, vm);
goto out;
}

@@ -1052,15 +1084,21 @@ bpf_program__load(struct bpf_program *prog,
if (!result.new_insn_ptr || !result.new_insn_cnt) {
pr_debug("Skip loading the %dth instance of program '%s'\n",
i, prog->section_name);
- prog->instan_fd(i) = -1;
- if (result.pfd)
- *result.pfd = -1;
+
+ if_engine(prog, prog->instan_fd(i) = -1,
+ prog->instan_vm(i) = NULL);
+ if (result.ptr)
+ if_engine(prog, *result.pfd = -1, *result.pvm = NULL);
continue;
}

- err = load_program(result.new_insn_ptr,
- result.new_insn_cnt,
- license, kern_version, &fd);
+ if_engine(prog,
+ (err = load_program(result.new_insn_ptr,
+ result.new_insn_cnt,
+ license, kern_version, &fd)),
+ (err = load_ubpf_program(result.new_insn_ptr,
+ result.new_insn_cnt,
+ &vm)));

if (err) {
pr_warning("Loading the %dth instance of program '%s' failed\n",
@@ -1068,9 +1106,9 @@ bpf_program__load(struct bpf_program *prog,
goto out;
}

- if (result.pfd)
- *result.pfd = fd;
- prog->instan_fd(i) = fd;
+ if (result.ptr)
+ if_engine(prog, *result.pfd = fd, *result.pvm = vm);
+ set_instance(prog, i, fd, vm);
}
out:
if (err)
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index f6965ce..8e69c6f 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -27,6 +27,7 @@ enum libbpf_errno {
LIBBPF_ERRNO__PROG2BIG, /* Program too big */
LIBBPF_ERRNO__KVER, /* Incorrect kernel version */
LIBBPF_ERRNO__NOUBPF, /* UBPF support is not compiled */
+ LIBBPF_ERRNO__LOADUBPF, /* Failed to load user space BPF program */
__LIBBPF_ERRNO__END,
};

@@ -123,6 +124,8 @@ struct bpf_insn;
* bpf_program__nth_fd(prog, 0).
*/

+struct ubpf_vm;
+
struct bpf_prog_prep_result {
/*
* If not NULL, load new instruction array.
@@ -131,8 +134,12 @@ struct bpf_prog_prep_result {
struct bpf_insn *new_insn_ptr;
int new_insn_cnt;

- /* If not NULL, result fd is set to it */
- int *pfd;
+ /* If not NULL, result is set to it */
+ union {
+ void *ptr;
+ int *pfd;
+ struct ubpf_vm **pvm;
+ };
};

/*
--
1.8.3.4

2016-04-20 18:02:39

by Wang Nan

[permalink] [raw]
Subject: [RFC PATCH 13/13] perf tests: Add UBPF test case

Introduce a BPF script use UBPF, test compiling, helper and hook.
Validate passing information through helper and hooks.

Signed-off-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Li Zefan <[email protected]>
---
tools/perf/tests/Build | 8 ++++
tools/perf/tests/bpf-script-test-ubpf.c | 68 +++++++++++++++++++++++++++++
tools/perf/tests/bpf.c | 77 ++++++++++++++++++++++++++++++++-
tools/perf/tests/llvm.c | 4 ++
tools/perf/tests/llvm.h | 2 +
5 files changed, 157 insertions(+), 2 deletions(-)
create mode 100644 tools/perf/tests/bpf-script-test-ubpf.c

diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index 449fe97..0050ff8 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -32,6 +32,7 @@ perf-y += parse-no-sample-id-all.o
perf-y += kmod-path.o
perf-y += thread-map.o
perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o llvm-src-prologue.o llvm-src-relocation.o
+perf-y += llvm-src-ubpf.o
perf-y += bpf.o
perf-y += topology.o
perf-y += cpumap.o
@@ -67,6 +68,13 @@ $(OUTPUT)tests/llvm-src-relocation.c: tests/bpf-script-test-relocation.c tests/B
$(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@
$(Q)echo ';' >> $@

+$(OUTPUT)tests/llvm-src-ubpf.c: tests/bpf-script-test-ubpf.c tests/Build
+ $(call rule_mkdir)
+ $(Q)echo '#include <tests/llvm.h>' > $@
+ $(Q)echo 'const char test_llvm__bpf_test_ubpf[] =' >> $@
+ $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@
+ $(Q)echo ';' >> $@
+
ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64))
perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
endif
diff --git a/tools/perf/tests/bpf-script-test-ubpf.c b/tools/perf/tests/bpf-script-test-ubpf.c
new file mode 100644
index 0000000..67000d5
--- /dev/null
+++ b/tools/perf/tests/bpf-script-test-ubpf.c
@@ -0,0 +1,68 @@
+/*
+ * bpf-script-test-ubpf.c
+ * Test user space BPF
+ */
+
+#ifndef LINUX_VERSION_CODE
+# error Need LINUX_VERSION_CODE
+# error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig'
+#endif
+#define BPF_ANY 0
+#define BPF_MAP_TYPE_ARRAY 2
+#define BPF_FUNC_map_lookup_elem 1
+#define BPF_FUNC_map_update_elem 2
+
+static void *(*bpf_map_lookup_elem)(void *map, void *key) =
+ (void *) BPF_FUNC_map_lookup_elem;
+static void *(*bpf_map_update_elem)(void *map, void *key, void *value, int flags) =
+ (void *) BPF_FUNC_map_update_elem;
+
+struct bpf_map_def {
+ unsigned int type;
+ unsigned int key_size;
+ unsigned int value_size;
+ unsigned int max_entries;
+};
+
+#define SEC(NAME) __attribute__((section(NAME), used))
+struct bpf_map_def SEC("maps") counter = {
+ .type = BPF_MAP_TYPE_ARRAY,
+ .key_size = sizeof(int),
+ .value_size = sizeof(int),
+ .max_entries = 1,
+};
+
+SEC("func=sys_epoll_pwait")
+int bpf_func__sys_epoll_pwait(void *ctx)
+{
+ int ind = 0;
+ int *flag = bpf_map_lookup_elem(&counter, &ind);
+ if (!flag)
+ return 0;
+ __sync_fetch_and_add(flag, 1);
+ return 0;
+}
+char _license[] SEC("license") = "GPL";
+int _version SEC("version") = LINUX_VERSION_CODE;
+
+static void (*ubpf_map_lookup_elem)(struct bpf_map_def *, void *, void *) = (void *)2;
+static void (*ubpf_map_update_elem)(struct bpf_map_def *, void *, void *) = (void *)3;
+static void (*ubpf_test_report)(int) = (void *)63;
+SEC("UBPF;perf_record_start")
+int perf_record_start(void)
+{
+ int idx = 0, val = 1000;
+
+ ubpf_map_update_elem(&counter, &idx, &val);
+ return 0;
+}
+
+SEC("UBPF;perf_record_end")
+int perf_record_end(int *x)
+{
+ int idx = 0, val;
+
+ ubpf_map_lookup_elem(&counter, &idx, &val);
+ ubpf_test_report(val + *x);
+ return 0;
+}
diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
index f31eed3..2841718 100644
--- a/tools/perf/tests/bpf.c
+++ b/tools/perf/tests/bpf.c
@@ -5,10 +5,13 @@
#include <util/evlist.h>
#include <linux/bpf.h>
#include <linux/filter.h>
+#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include "tests.h"
#include "llvm.h"
#include "debug.h"
+#include "ubpf-helpers.h"
+#include "ubpf-hooks.h"
#define NR_ITERS 111

#ifdef HAVE_LIBBPF_SUPPORT
@@ -46,6 +49,34 @@ static int llseek_loop(void)

#endif

+union testcase_context {
+ void *ptr;
+ unsigned long num;
+};
+
+#ifdef HAVE_UBPF_SUPPORT
+static int __ubpf_report_val;
+
+static void test_report(int val)
+{
+ __ubpf_report_val = val;
+}
+
+static int ubpf_prepare(union testcase_context *ctx __maybe_unused)
+{
+ ubpf_hook_perf_record_start(0);
+ return 0;
+}
+
+static int ubpf_verify(union testcase_context *ctx __maybe_unused)
+{
+ ubpf_hook_perf_record_end(0, 1234);
+ if (__ubpf_report_val != 2345)
+ return TEST_FAIL;
+ return TEST_OK;
+}
+#endif
+
static struct {
enum test_llvm__testcase prog_id;
const char *desc;
@@ -54,6 +85,10 @@ static struct {
const char *msg_load_fail;
int (*target_func)(void);
int expect_result;
+
+ union testcase_context context;
+ int (*prepare)(union testcase_context *);
+ int (*verify)(union testcase_context *);
} bpf_testcase_table[] = {
{
LLVM_TESTCASE_BASE,
@@ -63,6 +98,7 @@ static struct {
"load bpf object failed",
&epoll_pwait_loop,
(NR_ITERS + 1) / 2,
+ {0}, NULL, NULL
},
#ifdef HAVE_BPF_PROLOGUE
{
@@ -73,6 +109,7 @@ static struct {
"check your vmlinux setting?",
&llseek_loop,
(NR_ITERS + 1) / 4,
+ {0}, NULL, NULL
},
#endif
{
@@ -83,11 +120,27 @@ static struct {
"libbpf error when dealing with relocation",
NULL,
0,
+ {0}, NULL, NULL
},
+#ifdef HAVE_UBPF_SUPPORT
+ {
+ LLVM_TESTCASE_BPF_UBPF,
+ "Test UBPF support",
+ "[bpf_ubpf_test]",
+ "fix 'perf test LLVM' first",
+ "failed to load UBPF",
+ &epoll_pwait_loop,
+ 0,
+ {0}, ubpf_prepare, ubpf_verify,
+ }
+#endif
};

static int do_test(struct bpf_object *obj, int (*func)(void),
- int expect)
+ int expect,
+ int (*prepare)(union testcase_context *),
+ int (*verify)(union testcase_context *),
+ union testcase_context *ctx)
{
struct record_opts opts = {
.target = {
@@ -154,6 +207,14 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
goto out_delete_evlist;
}

+ if (prepare) {
+ err = prepare(ctx);
+ if (err < 0) {
+ pr_debug("prepare fail\n");
+ goto out_delete_evlist;
+ }
+ }
+
perf_evlist__enable(evlist);
(*func)();
perf_evlist__disable(evlist);
@@ -176,6 +237,8 @@ static int do_test(struct bpf_object *obj, int (*func)(void),

ret = TEST_OK;

+ if (verify)
+ ret = verify(ctx);
out_delete_evlist:
perf_evlist__delete(evlist);
return ret;
@@ -201,6 +264,13 @@ static int __test__bpf(int idx)
size_t obj_buf_sz;
struct bpf_object *obj;

+#ifdef HAVE_UBPF_SUPPORT
+ ret = libbpf_register_ubpf_func(63, "test_report", test_report);
+ if (ret) {
+ pr_debug("Unable to register UBPF helper\n");
+ return TEST_FAIL;
+ }
+#endif
ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz,
bpf_testcase_table[idx].prog_id,
true, NULL);
@@ -229,7 +299,10 @@ static int __test__bpf(int idx)
if (obj)
ret = do_test(obj,
bpf_testcase_table[idx].target_func,
- bpf_testcase_table[idx].expect_result);
+ bpf_testcase_table[idx].expect_result,
+ bpf_testcase_table[idx].prepare,
+ bpf_testcase_table[idx].verify,
+ &bpf_testcase_table[idx].context);
out:
bpf__clear();
return ret;
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
index cff564f..92b0a42 100644
--- a/tools/perf/tests/llvm.c
+++ b/tools/perf/tests/llvm.c
@@ -48,6 +48,10 @@ static struct {
.desc = "Compile source for BPF relocation test",
.should_load_fail = true,
},
+ [LLVM_TESTCASE_BPF_UBPF] = {
+ .source = test_llvm__bpf_test_ubpf,
+ .desc = "Compile source for UBPF test",
+ },
};

int
diff --git a/tools/perf/tests/llvm.h b/tools/perf/tests/llvm.h
index 0eaa604..8ae4aae 100644
--- a/tools/perf/tests/llvm.h
+++ b/tools/perf/tests/llvm.h
@@ -8,12 +8,14 @@ extern const char test_llvm__bpf_base_prog[];
extern const char test_llvm__bpf_test_kbuild_prog[];
extern const char test_llvm__bpf_test_prologue_prog[];
extern const char test_llvm__bpf_test_relocation[];
+extern const char test_llvm__bpf_test_ubpf[];

enum test_llvm__testcase {
LLVM_TESTCASE_BASE,
LLVM_TESTCASE_KBUILD,
LLVM_TESTCASE_BPF_PROLOGUE,
LLVM_TESTCASE_BPF_RELOCATION,
+ LLVM_TESTCASE_BPF_UBPF,
__LLVM_TESTCASE_MAX,
};

--
1.8.3.4

2016-04-20 18:02:35

by Wang Nan

[permalink] [raw]
Subject: [RFC PATCH 09/13] bpf tools: Register extern functions for ubpf programs

Introduce libbpf_register_ubpf_func(), allow caller (perf) to
register extern functions. Registered functions are stored into
an array and registered to each ubpf_vm when loading.

Signed-off-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Li Zefan <[email protected]>
---
tools/lib/bpf/libbpf.c | 38 +++++++++++++++++++++++++++++++++++++-
tools/lib/bpf/libbpf.h | 4 ++++
2 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 4045a7e..f6df743 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -951,19 +951,48 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
}

#ifdef HAVE_UBPF_SUPPORT
+static struct {
+ const char *name;
+ void *func;
+} ubpf_funcs[MAX_UBPF_FUNC];
+
+int libbpf_register_ubpf_func(unsigned int idx, const char *name, void *func)
+{
+ if (idx >= MAX_UBPF_FUNC)
+ return -E2BIG;
+ if (!func)
+ return -EINVAL;
+
+ ubpf_funcs[idx].name = name;
+ ubpf_funcs[idx].func = func;
+ return 0;
+}
+
static int
load_ubpf_program(struct bpf_insn *insns, int insns_cnt,
struct ubpf_vm **pvm)
{
struct ubpf_vm *vm = ubpf_create();
char *message;
- int err;
+ int i, err;

if (!vm) {
pr_warning("Failed to create ubpf vm\n");
return -LIBBPF_ERRNO__LOADUBPF;
}

+ for (i = 0; i < MAX_UBPF_FUNC; i++) {
+ if (ubpf_funcs[i].func) {
+ err = ubpf_register(vm, i, ubpf_funcs[i].name,
+ ubpf_funcs[i].func);
+ if (err) {
+ pr_warning("Failed to register %s\n",
+ ubpf_funcs[i].name);
+ return -LIBBPF_ERRNO__LOADUBPF;
+ }
+ }
+ }
+
err = ubpf_load(vm, insns, insns_cnt * sizeof(insns[0]), &message);
if (err < 0) {
pr_warning("Failed to load ubpf program: %s\n", message);
@@ -973,6 +1002,13 @@ load_ubpf_program(struct bpf_insn *insns, int insns_cnt,
*pvm = vm;
return 0;
}
+#else
+int libbpf_register_ubpf_func(unsigned int idx __maybe_unused,
+ const char *name __maybe_unused,
+ void *func __maybe_unused)
+{
+ return -LIBBPF_ERRNO__NOUBPF;
+}
#endif

static int
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 41c35fd..a764dee 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -45,6 +45,10 @@ void libbpf_set_print(libbpf_print_fn_t warn,
libbpf_print_fn_t info,
libbpf_print_fn_t debug);

+#define MAX_UBPF_FUNC 64
+
+int libbpf_register_ubpf_func(unsigned int idx, const char *name, void *func);
+
/* Hide internal to user */
struct bpf_object;

--
1.8.3.4

2016-04-20 18:02:47

by Wang Nan

[permalink] [raw]
Subject: [RFC PATCH 10/13] perf tools: Register basic UBPF helpers

Reigster basic extern functions for ubpf programs.

Signed-off-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Li Zefan <[email protected]>
---
tools/perf/perf.c | 3 ++
tools/perf/util/Build | 1 +
tools/perf/util/ubpf-helpers-list.h | 7 ++++
tools/perf/util/ubpf-helpers.c | 66 +++++++++++++++++++++++++++++++++++++
tools/perf/util/ubpf-helpers.h | 21 ++++++++++++
5 files changed, 98 insertions(+)
create mode 100644 tools/perf/util/ubpf-helpers-list.h
create mode 100644 tools/perf/util/ubpf-helpers.c
create mode 100644 tools/perf/util/ubpf-helpers.h

diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 7b2df2b..f246364 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -16,6 +16,7 @@
#include "util/parse-events.h"
#include <subcmd/parse-options.h>
#include "util/bpf-loader.h"
+#include "util/ubpf-helpers.h"
#include "util/debug.h"
#include <api/fs/tracing_path.h>
#include <pthread.h>
@@ -618,6 +619,8 @@ int main(int argc, const char **argv)

perf_debug_setup();

+ register_ubpf_helpers();
+
while (1) {
static int done_help;
int was_alias = run_argv(&argc, &argv);
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 90229a8..e193e6b 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -87,6 +87,7 @@ libperf-y += mem-events.o

libperf-$(CONFIG_LIBBPF) += bpf-loader.o
libperf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o
+libperf-$(CONFIG_UBPF) += ubpf-helpers.o
libperf-$(CONFIG_LIBELF) += symbol-elf.o
libperf-$(CONFIG_LIBELF) += probe-file.o
libperf-$(CONFIG_LIBELF) += probe-event.o
diff --git a/tools/perf/util/ubpf-helpers-list.h b/tools/perf/util/ubpf-helpers-list.h
new file mode 100644
index 0000000..b457963
--- /dev/null
+++ b/tools/perf/util/ubpf-helpers-list.h
@@ -0,0 +1,7 @@
+DEF_UBPF_HELPER(int, ubpf_memcmp, (void *s1, void *s2, unsigned int n))
+DEF_UBPF_HELPER(void, ubpf_memcpy, (void *d, void *s, unsigned int size))
+DEF_UBPF_HELPER(int, ubpf_strcmp, (char *s1, char *s2))
+DEF_UBPF_HELPER(int, ubpf_printf, (char *fmt, ...))
+DEF_UBPF_HELPER(int, ubpf_map_lookup_elem, (void *map_desc, void *key, void *value))
+DEF_UBPF_HELPER(int, ubpf_map_update_elem, (void *map_desc, void *key, void *value, unsigned long long flags))
+DEF_UBPF_HELPER(int, ubpf_map_get_next_key, (void *map_desc, void *key, void *next_key))
diff --git a/tools/perf/util/ubpf-helpers.c b/tools/perf/util/ubpf-helpers.c
new file mode 100644
index 0000000..b05f495
--- /dev/null
+++ b/tools/perf/util/ubpf-helpers.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016, Wang Nan <[email protected]>
+ * Copyright (C) 2016, Huawei Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <bpf/libbpf.h>
+#include <bpf/bpf.h>
+#include <ubpf.h>
+
+#include "ubpf-helpers.h"
+
+static int ubpf_memcmp(void *s1, void *s2, unsigned int n)
+{
+ return memcmp(s1, s2, n);
+}
+
+static void ubpf_memcpy(void *d, void *s, unsigned int n)
+{
+ memcpy(d, s, n);
+}
+
+static int ubpf_strcmp(char *s1, char *s2)
+{
+ return strcmp(s1, s2);
+}
+
+static int ubpf_printf(char *fmt, ...)
+{
+ int ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = vprintf(fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+static int ubpf_map_lookup_elem(int map_fd, void *key, void *value)
+{
+ return bpf_map_lookup_elem(map_fd, key, value);
+}
+
+static int ubpf_map_update_elem(int map_fd, void *key, void *value,
+ unsigned long long flags)
+{
+ return bpf_map_update_elem(map_fd, key, value, (u64)flags);
+}
+
+static int ubpf_map_get_next_key(int map_fd, void *key, void *next_key)
+{
+ return bpf_map_get_next_key(map_fd, key, next_key);
+}
+
+void register_ubpf_helpers(void)
+{
+#define DEF_UBPF_HELPER(type, name, param) \
+ libbpf_register_ubpf_func(UBPF_FUNC_##name, #name, name);
+#include "ubpf-helpers-list.h"
+#undef DEF_UBPF_HELPER
+}
diff --git a/tools/perf/util/ubpf-helpers.h b/tools/perf/util/ubpf-helpers.h
new file mode 100644
index 0000000..b4e778b
--- /dev/null
+++ b/tools/perf/util/ubpf-helpers.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016, Wang Nan <[email protected]>
+ * Copyright (C) 2016, Huawei Inc.
+ */
+#ifndef __UBPF_HELPERS_H
+#define __UBPF_HELPERS_H
+
+#define DEF_UBPF_HELPER(type, name, param) UBPF_FUNC_##name,
+enum {
+ UBPF_FUNC_unspec = 0,
+#include "ubpf-helpers-list.h"
+ UBPF_FUNC_MAX
+};
+#undef DEF_UBPF_HELPER
+
+#ifdef HAVE_UBPF_SUPPORT
+void register_ubpf_helpers(void);
+#else
+inline static void register_ubpf_helpers(void) {};
+#endif
+#endif
--
1.8.3.4

2016-04-20 18:02:55

by Wang Nan

[permalink] [raw]
Subject: [RFC PATCH 12/13] perf record: Add UBPF hooks at beginning and end of perf record

Like tracepoint in kernel code, UBPF hooks can be added in perf code and
trigger UBPF programs passed by BPF scripts. The first two UBPF hooks
added are record start/end. UBPF scripts can initial BPF maps in
record start, and report result when record finished.

Signed-off-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Li Zefan <[email protected]>
---
tools/perf/builtin-record.c | 4 ++
tools/perf/util/Build | 2 +-
tools/perf/util/ubpf-hooks-list.h | 10 +++++
tools/perf/util/ubpf-hooks.c | 81 +++++++++++++++++++++++++++++++++++++++
tools/perf/util/ubpf-hooks.h | 35 +++++++++++++++++
5 files changed, 131 insertions(+), 1 deletion(-)
create mode 100644 tools/perf/util/ubpf-hooks-list.h
create mode 100644 tools/perf/util/ubpf-hooks.c
create mode 100644 tools/perf/util/ubpf-hooks.h

diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index c7f92cc..0c49249 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -35,6 +35,7 @@
#include "util/llvm-utils.h"
#include "util/bpf-loader.h"
#include "util/trigger.h"
+#include "util/ubpf-hooks.h"
#include "asm/bug.h"

#include <unistd.h>
@@ -824,6 +825,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)

auxtrace_snapshot_ready();
switch_output_ready();
+ ubpf_hook_perf_record_start(0);
for (;;) {
unsigned long long hits = rec->samples;

@@ -888,6 +890,8 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
disabled = true;
}
}
+ ubpf_hook_perf_record_end(0, rec->samples);
+
auxtrace_snapshot_off();
switch_output_off();

diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index e193e6b..c3b8763 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -87,7 +87,7 @@ libperf-y += mem-events.o

libperf-$(CONFIG_LIBBPF) += bpf-loader.o
libperf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o
-libperf-$(CONFIG_UBPF) += ubpf-helpers.o
+libperf-$(CONFIG_UBPF) += ubpf-helpers.o ubpf-hooks.o
libperf-$(CONFIG_LIBELF) += symbol-elf.o
libperf-$(CONFIG_LIBELF) += probe-file.o
libperf-$(CONFIG_LIBELF) += probe-event.o
diff --git a/tools/perf/util/ubpf-hooks-list.h b/tools/perf/util/ubpf-hooks-list.h
new file mode 100644
index 0000000..626d18a
--- /dev/null
+++ b/tools/perf/util/ubpf-hooks-list.h
@@ -0,0 +1,10 @@
+UBPF_HOOK(perf_record_start,,)
+
+UBPF_HOOK(perf_record_end,
+ __UBPF_HOOK_ARG(
+ __arg(int, samples)
+ ),
+ __UBPF_HOOK_ASSIGN(
+ __entry.samples = samples;
+ )
+ )
diff --git a/tools/perf/util/ubpf-hooks.c b/tools/perf/util/ubpf-hooks.c
new file mode 100644
index 0000000..9a53f7c
--- /dev/null
+++ b/tools/perf/util/ubpf-hooks.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016, Wang Nan <[email protected]>
+ * Copyright (C) 2016, Huawei Inc.
+ */
+
+#include <bpf/libbpf.h>
+#include <asm/bug.h>
+#include <ubpf.h>
+#include "ubpf-hooks.h"
+#include "debug.h"
+
+static int run_ubpf_program(struct bpf_program *prog, void *mem, size_t len)
+{
+ struct ubpf_vm *vm;
+ int ret;
+
+ vm = bpf_program__vm(prog);
+ if (!vm) {
+ WARN_ONCE(!vm, "Unable to fetch vm from UBPF program\n");
+ return -EINVAL;
+ }
+
+ ret = ubpf_exec(vm, mem, len);
+ pr_debug("program %s returns %d\n",
+ bpf_program__title(prog, false), ret);
+ return ret;
+}
+
+static int
+run_ubpf_programs(unsigned long flags, const char *expect_title,
+ void *mem, size_t len)
+{
+
+ struct bpf_object *obj, *tmp;
+ struct bpf_program *prog;
+ const char *title;
+ int err;
+
+ if (!len)
+ mem = NULL;
+
+ bpf_object__for_each_safe(obj, tmp) {
+ bpf_object__for_each_program(prog, obj) {
+ if (bpf_program__is_ubpf(prog)) {
+ title = bpf_program__title(prog, false);
+ if (!title)
+ continue;
+ if (strcmp(title, expect_title) != 0)
+ continue;
+ err = run_ubpf_program(prog, mem, len);
+ if (err && (flags & UBPF_HOOK_BREAKABLE))
+ return err;
+ }
+ }
+ }
+
+ return 0;
+}
+
+#define __arg(t, n) t n;
+#define __UBPF_HOOK_ARG(args) args
+#define UBPF_HOOK(n, args, assign) struct ubpf_hook_##n##_proto {args};
+#include "ubpf-hooks-list.h"
+#undef UBPF_HOOK
+#undef __UBPF_HOOK_ARG
+#undef __arg
+
+#define __arg(t, n) , t n
+#define __UBPF_HOOK_ASSIGN(code) do {code;} while(0)
+#define __UBPF_HOOK_ARG(args) args
+#define UBPF_HOOK(n, a, assign) \
+int ubpf_hook_##n(unsigned long flags a) { \
+ struct ubpf_hook_##n##_proto __entry; \
+ assign; \
+ return run_ubpf_programs(flags, "UBPF;"#n, &__entry, sizeof(__entry));\
+}
+#include "ubpf-hooks-list.h"
+#undef UBPF_HOOK
+#undef __UBPF_HOOK_ARG
+#undef __UBPF_HOOK_ASSIGN
+#undef __arg
diff --git a/tools/perf/util/ubpf-hooks.h b/tools/perf/util/ubpf-hooks.h
new file mode 100644
index 0000000..165f52d
--- /dev/null
+++ b/tools/perf/util/ubpf-hooks.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016, Wang Nan <[email protected]>
+ * Copyright (C) 2016, Huawei Inc.
+ */
+#ifndef __PERF_UBPF_HOOKS_H
+#define __PERF_UBPF_HOOKS_H
+
+#include <linux/compiler.h>
+
+#define UBPF_HOOK_BREAKABLE 1
+
+#ifdef HAVE_UBPF_SUPPORT
+
+#define __arg(t, n) , t n
+#define __UBPF_HOOK_ARG(args) args
+#define UBPF_HOOK(n, args, assign) int ubpf_hook_##n(unsigned long flags args);
+#include "ubpf-hooks-list.h"
+#undef UBPF_HOOK
+#undef __UBPF_HOOK_ARG
+#undef __arg
+
+#else
+
+#define __arg(t, n) , t n __maybe_unused
+#define __UBPF_HOOK_ARG(args) args
+#define UBPF_HOOK(n, args, assign) \
+static inline int ubpf_hook_##n(unsigned long flags __maybe_unused args) {return 0;};
+#include "ubpf-hooks-list.h"
+#undef UBPF_HOOK
+#undef __UBPF_HOOK_ARG
+#undef __arg
+
+#endif
+
+#endif
--
1.8.3.4

2016-04-20 18:02:46

by Wang Nan

[permalink] [raw]
Subject: [RFC PATCH 03/13] bpf tools: Add ubpf include and makefile options

Prepare to use libubpf. Add ubpf.h into libbpf.c. Add corresponding
makefile options.

Allow ignoring UBPF when building libbpf by setting NO_UBPF=1 for
libbpf building.

Allow perf controling ubpf using NO_UBPF=1.

Signed-off-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Li Zefan <[email protected]>
---
tools/lib/bpf/Makefile | 16 ++++++++++++++--
tools/lib/bpf/libbpf.c | 3 +++
tools/perf/Makefile.perf | 2 ++
tools/perf/config/Makefile | 7 +++++++
4 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile
index 8dfa512..6944d02 100644
--- a/tools/lib/bpf/Makefile
+++ b/tools/lib/bpf/Makefile
@@ -64,9 +64,17 @@ ifndef VERBOSE
VERBOSE = 0
endif

+ifndef NO_UBPF
+ FEATURE_UBPF := ubpf
+ CFLAGS_UBPF := -DHAVE_UBPF_SUPPORT
+else
+ FEATURE_UBPF :=
+ CFLAGS_UBPF :=
+endif
+
FEATURE_USER = .libbpf
-FEATURE_TESTS = libelf libelf-getphdrnum libelf-mmap bpf ubpf
-FEATURE_DISPLAY = libelf bpf ubpf
+FEATURE_TESTS = libelf libelf-getphdrnum libelf-mmap bpf $(FEATURE_UBPF)
+FEATURE_DISPLAY = libelf bpf $(FEATURE_UBPF)

INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/arch/$(ARCH)/include/uapi -I$(srctree)/include/uapi
FEATURE_CHECK_CFLAGS-bpf = $(INCLUDES)
@@ -120,6 +128,10 @@ ifeq ($(feature-libelf-getphdrnum), 1)
override CFLAGS += -DHAVE_ELF_GETPHDRNUM_SUPPORT
endif

+ifeq ($(feature-ubpf), 1)
+ override CFLAGS += $(CFLAGS_UBPF)
+endif
+
# Append required CFLAGS
override CFLAGS += $(EXTRA_WARNINGS)
override CFLAGS += -Werror -Wall
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 7e543c3..55b739e 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -21,6 +21,9 @@
#include <libelf.h>
#include <gelf.h>

+#ifdef HAVE_UBPF_SUPPORT
+# include <ubpf.h>
+#endif
#include "libbpf.h"
#include "bpf.h"

diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index bde8cba..5334e29 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -81,6 +81,8 @@ include ../scripts/utilities.mak
#
# Define NO_LIBBPF if you do not want BPF support
#
+# Define NO_UBPF if you do not want user space BPF support
+#
# Define FEATURES_DUMP to provide features detection dump file
# and bypass the feature detection

diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 1e46277..89d0470 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -315,6 +315,13 @@ ifndef NO_LIBELF
ifeq ($(feature-bpf), 1)
CFLAGS += -DHAVE_LIBBPF_SUPPORT
$(call detected,CONFIG_LIBBPF)
+ ifndef NO_UBPF
+ ifeq ($(feature-ubpf), 1)
+ CFLAGS += -DHAVE_UBPF_SUPPORT
+ EXTLIBS += -lubpf
+ $(call detected,CONFIG_UBPF)
+ endif
+ endif
endif

ifndef NO_DWARF
--
1.8.3.4

2016-04-20 18:02:44

by Wang Nan

[permalink] [raw]
Subject: [RFC PATCH 06/13] bpf tools: Introduce ubpf_vm to program instance union

Add 'struct ubpf_vm *' into prog_instance union. Introduce if_engine()
macro to merge common code.

Signed-off-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Li Zefan <[email protected]>
---
tools/lib/bpf/libbpf.c | 76 ++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 68 insertions(+), 8 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 3755846..3a969fd 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -141,6 +141,9 @@ int libbpf_strerror(int err, char *buf, size_t size)

union prog_instance {
int fd;
+#ifdef HAVE_UBPF_SUPPORT
+ struct ubpf_vm *vm;
+#endif
};

/*
@@ -176,6 +179,56 @@ struct bpf_program {
bpf_program_clear_priv_t clear_priv;
};

+#ifdef HAVE_UBPF_SUPPORT
+# define __if_engine(p, d, k, u) \
+ do { \
+ switch (p->engine) { \
+ default: \
+ case ENGINE_UNKNOWN: { \
+ d; \
+ } \
+ case ENGINE_KBPF: { \
+ k; break; \
+ } \
+ case ENGINE_UBPF: { \
+ u; break; \
+ } \
+ } \
+ } while(0)
+
+/*
+ * ubpf_destroy() doesn't accept NULL input. This wrapper makes
+ * it similar to zclose.
+ */
+# define __ubpf_destroy(vm) do { \
+ if (vm) \
+ ubpf_destroy(vm); \
+ (vm) = NULL; \
+} while(0)
+
+#else
+# define __if_engine(p, d, k, u) \
+ do { k; } while(0)
+#endif
+
+#define instan_fd(i) instances.array[i].fd
+#define instan_vm(i) instances.array[i].vm
+
+#define if_engine(p, k, u) __if_engine(p, do { } while(0), k, u)
+#define set_instance(p, i, k, u) \
+ if_engine(p, \
+ p->instan_fd(i) = k, \
+ p->instan_vm(i) = u)
+
+static inline void init_instance_array(struct bpf_program *prog)
+{
+ size_t size = sizeof(prog->instances.array[0]) * prog->instances.nr;
+
+ if_engine(prog,
+ memset(prog->instances.array, -1, size),
+ memset(prog->instances.array, 0, size));
+}
+
struct bpf_map {
int fd;
char *name;
@@ -239,7 +292,9 @@ static void bpf_program__unload(struct bpf_program *prog)
*/
if (prog->instances.nr > 0) {
for (i = 0; i < prog->instances.nr; i++)
- zclose(prog->instances.array[i].fd);
+ if_engine(prog,
+ zclose(prog->instan_fd(i)),
+ __ubpf_destroy(prog->instan_vm(i)));
} else if (prog->instances.nr != -1) {
pr_warning("Internal error: instances.nr is %d\n",
prog->instances.nr);
@@ -966,7 +1021,7 @@ bpf_program__load(struct bpf_program *prog,
return -ENOMEM;
}
prog->instances.nr = 1;
- prog->instances.array[0].fd = -1;
+ set_instance(prog, 0, -1, NULL);
}

if (!prog->preprocessor) {
@@ -977,7 +1032,7 @@ bpf_program__load(struct bpf_program *prog,
err = load_program(prog->insns, prog->insns_cnt,
license, kern_version, &fd);
if (!err)
- prog->instances.array[0].fd = fd;
+ prog->instan_fd(0) = fd;
goto out;
}

@@ -997,7 +1052,7 @@ bpf_program__load(struct bpf_program *prog,
if (!result.new_insn_ptr || !result.new_insn_cnt) {
pr_debug("Skip loading the %dth instance of program '%s'\n",
i, prog->section_name);
- prog->instances.array[i].fd = -1;
+ prog->instan_fd(i) = -1;
if (result.pfd)
*result.pfd = -1;
continue;
@@ -1015,7 +1070,7 @@ bpf_program__load(struct bpf_program *prog,

if (result.pfd)
*result.pfd = fd;
- prog->instances.array[i].fd = fd;
+ prog->instan_fd(i) = fd;
}
out:
if (err)
@@ -1301,12 +1356,11 @@ int bpf_program__set_prep(struct bpf_program *prog, int nr_instances,
return -ENOMEM;
}

- /* fill all fd with -1 */
- memset(array, -1, sizeof(array[0]) * nr_instances);
-
prog->instances.nr = nr_instances;
prog->instances.array = array;
prog->preprocessor = prep;
+
+ init_instance_array(prog);
return 0;
}

@@ -1314,6 +1368,12 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n)
{
int fd;

+ if (prog->engine != ENGINE_KBPF) {
+ pr_warning("Can't get fd from program %s: engine not KBPF or not loaded\n",
+ prog->section_name);
+ return -EINVAL;
+ }
+
if (n >= prog->instances.nr || n < 0) {
pr_warning("Can't get the %dth fd from program %s: only %d instances\n",
n, prog->section_name, prog->instances.nr);
--
1.8.3.4

2016-04-20 18:02:43

by Wang Nan

[permalink] [raw]
Subject: [RFC PATCH 04/13] bpf tools: Replace fd array to union array

Following commits will add new types for instance of a bpf program.
For kernel bpf program, a program instance is fd. For ubpf program,
the instance of a program is a 'struct ubpf_vm'. This patch promote
original 'fds' array to 'union prog_instance' array, prepare for
further extending.

Signed-off-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Li Zefan <[email protected]>
---
tools/lib/bpf/libbpf.c | 44 ++++++++++++++++++++++++--------------------
1 file changed, 24 insertions(+), 20 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 55b739e..dd5a107 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -138,6 +138,10 @@ int libbpf_strerror(int err, char *buf, size_t size)
# define LIBBPF_ELF_C_READ_MMAP ELF_C_READ
#endif

+union prog_instance {
+ int fd;
+};
+
/*
* bpf_prog should be a better name but it has been used in
* linux/filter.h.
@@ -157,7 +161,7 @@ struct bpf_program {

struct {
int nr;
- int *fds;
+ union prog_instance *array;
} instances;
bpf_program_prep_t preprocessor;

@@ -229,14 +233,14 @@ static void bpf_program__unload(struct bpf_program *prog)
*/
if (prog->instances.nr > 0) {
for (i = 0; i < prog->instances.nr; i++)
- zclose(prog->instances.fds[i]);
+ zclose(prog->instances.array[i].fd);
} else if (prog->instances.nr != -1) {
pr_warning("Internal error: instances.nr is %d\n",
prog->instances.nr);
}

prog->instances.nr = -1;
- zfree(&prog->instances.fds);
+ zfree(&prog->instances.array);
}

static void bpf_program__exit(struct bpf_program *prog)
@@ -287,7 +291,7 @@ bpf_program__init(void *data, size_t size, char *name, int idx,
memcpy(prog->insns, data,
prog->insns_cnt * sizeof(struct bpf_insn));
prog->idx = idx;
- prog->instances.fds = NULL;
+ prog->instances.array = NULL;
prog->instances.nr = -1;

return 0;
@@ -937,20 +941,20 @@ bpf_program__load(struct bpf_program *prog,
{
int err = 0, fd, i;

- if (prog->instances.nr < 0 || !prog->instances.fds) {
+ if (prog->instances.nr < 0 || !prog->instances.array) {
if (prog->preprocessor) {
pr_warning("Internal error: can't load program '%s'\n",
prog->section_name);
return -LIBBPF_ERRNO__INTERNAL;
}

- prog->instances.fds = malloc(sizeof(int));
- if (!prog->instances.fds) {
- pr_warning("Not enough memory for BPF fds\n");
+ prog->instances.array = malloc(sizeof(union prog_instance));
+ if (!prog->instances.array) {
+ pr_warning("Not enough memory for BPF instance\n");
return -ENOMEM;
}
prog->instances.nr = 1;
- prog->instances.fds[0] = -1;
+ prog->instances.array[0].fd = -1;
}

if (!prog->preprocessor) {
@@ -961,7 +965,7 @@ bpf_program__load(struct bpf_program *prog,
err = load_program(prog->insns, prog->insns_cnt,
license, kern_version, &fd);
if (!err)
- prog->instances.fds[0] = fd;
+ prog->instances.array[0].fd = fd;
goto out;
}

@@ -981,7 +985,7 @@ bpf_program__load(struct bpf_program *prog,
if (!result.new_insn_ptr || !result.new_insn_cnt) {
pr_debug("Skip loading the %dth instance of program '%s'\n",
i, prog->section_name);
- prog->instances.fds[i] = -1;
+ prog->instances.array[i].fd = -1;
if (result.pfd)
*result.pfd = -1;
continue;
@@ -999,7 +1003,7 @@ bpf_program__load(struct bpf_program *prog,

if (result.pfd)
*result.pfd = fd;
- prog->instances.fds[i] = fd;
+ prog->instances.array[i].fd = fd;
}
out:
if (err)
@@ -1269,27 +1273,27 @@ int bpf_program__fd(struct bpf_program *prog)
int bpf_program__set_prep(struct bpf_program *prog, int nr_instances,
bpf_program_prep_t prep)
{
- int *instances_fds;
+ union prog_instance *array;

if (nr_instances <= 0 || !prep)
return -EINVAL;

- if (prog->instances.nr > 0 || prog->instances.fds) {
+ if (prog->instances.nr > 0 || prog->instances.array) {
pr_warning("Can't set pre-processor after loading\n");
return -EINVAL;
}

- instances_fds = malloc(sizeof(int) * nr_instances);
- if (!instances_fds) {
- pr_warning("alloc memory failed for fds\n");
+ array = malloc(sizeof(array[0]) * nr_instances);
+ if (!array) {
+ pr_warning("alloc memory failed for instances\n");
return -ENOMEM;
}

/* fill all fd with -1 */
- memset(instances_fds, -1, sizeof(int) * nr_instances);
+ memset(array, -1, sizeof(array[0]) * nr_instances);

prog->instances.nr = nr_instances;
- prog->instances.fds = instances_fds;
+ prog->instances.array = array;
prog->preprocessor = prep;
return 0;
}
@@ -1304,7 +1308,7 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n)
return -EINVAL;
}

- fd = prog->instances.fds[n];
+ fd = prog->instances.array[n].fd;
if (fd < 0) {
pr_warning("%dth instance of program '%s' is invalid\n",
n, prog->section_name);
--
1.8.3.4

2016-04-20 18:02:38

by Wang Nan

[permalink] [raw]
Subject: [RFC PATCH 11/13] perf bpf: Accept ubpf programs

This patch allows user passing a UBPF function through bpf file with
section name leading with 'UBPF;'. For example:

#define SEC(NAME) __attribute__((section(NAME), used))
SEC("UBPF;perf_record_exit")
void record_exit(int *samples)
{
char fmt[] = "Hello! receive %d samples\n";
ubpf_printf(fmt, *samples);
}

Signed-off-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Li Zefan <[email protected]>
---
tools/perf/util/bpf-loader.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 493307d..20a0370 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -305,6 +305,19 @@ config_bpf_program(struct bpf_program *prog)
return PTR_ERR(config_str);
}

+ if (strncmp(config_str, "UBPF;", 5) == 0) {
+ pr_debug("bpf: load a UBPF program %s\n", config_str);
+ err = bpf_program__set_ubpf(prog);
+ if (err) {
+ char errbuf[BUFSIZ];
+
+ libbpf_strerror(err, errbuf, sizeof(errbuf));
+ pr_warning("Set %s to ubpf failed: %s\n", config_str, errbuf);
+ return err;
+ }
+ return 0;
+ }
+
priv = calloc(sizeof(*priv), 1);
if (!priv) {
pr_debug("bpf: failed to alloc priv\n");
@@ -607,6 +620,9 @@ int bpf__probe(struct bpf_object *obj)
if (err)
goto out;

+ if (bpf_program__is_ubpf(prog))
+ continue;
+
err = bpf_program__get_private(prog, (void **)&priv);
if (err || !priv)
goto out;
@@ -650,6 +666,9 @@ int bpf__unprobe(struct bpf_object *obj)
bpf_object__for_each_program(prog, obj) {
int i;

+ if (bpf_program__is_ubpf(prog))
+ continue;
+
err = bpf_program__get_private(prog, (void **)&priv);
if (err || !priv)
continue;
@@ -707,6 +726,9 @@ int bpf__foreach_tev(struct bpf_object *obj,
struct bpf_prog_priv *priv;
int i, fd;

+ if (bpf_program__is_ubpf(prog))
+ continue;
+
err = bpf_program__get_private(prog,
(void **)&priv);
if (err || !priv) {
--
1.8.3.4

2016-04-20 18:04:37

by Wang Nan

[permalink] [raw]
Subject: [RFC PATCH 08/13] bpf tools: Add API for fetching ubpf_vm

Introduce bpf_program__vm and bpf_program__nth_vm for fetching ubpf
program instance.

Signed-off-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Li Zefan <[email protected]>
---
tools/lib/bpf/libbpf.c | 38 ++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 2 ++
2 files changed, 40 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index e4a1e77..4045a7e 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -1375,6 +1375,11 @@ int bpf_program__fd(struct bpf_program *prog)
return bpf_program__nth_fd(prog, 0);
}

+struct ubpf_vm *bpf_program__vm(struct bpf_program *prog)
+{
+ return bpf_program__nth_vm(prog, 0);
+}
+
int bpf_program__set_prep(struct bpf_program *prog, int nr_instances,
bpf_program_prep_t prep)
{
@@ -1429,6 +1434,32 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n)
}

#ifdef HAVE_UBPF_SUPPORT
+struct ubpf_vm *bpf_program__nth_vm(struct bpf_program *prog, int n)
+{
+ struct ubpf_vm *vm;
+
+ if (prog->engine != ENGINE_UBPF) {
+ pr_warning("Can't get ubpf_vm from program %s: engine not UBPF or not loaded\n",
+ prog->section_name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (n >= prog->instances.nr || n < 0) {
+ pr_warning("Can't get the %dth vm from program %s: only %d instances\n",
+ n, prog->section_name, prog->instances.nr);
+ return ERR_PTR(-EINVAL);
+ }
+
+ vm = prog->instances.array[n].vm;
+ if (!vm) {
+ pr_warning("%dth instance of program '%s' is invalid\n",
+ n, prog->section_name);
+ return ERR_PTR(-ENOENT);
+ }
+
+ return vm;
+}
+
int bpf_program__set_ubpf(struct bpf_program *prog)
{
if (prog->engine != ENGINE_UNKNOWN) {
@@ -1454,6 +1485,13 @@ bool bpf_program__is_ubpf(struct bpf_program *prog __maybe_unused)
{
return false;
}
+
+struct ubpf_vm *
+bpf_program__nth_vm(struct bpf_program *prog __maybe_unused,
+ int n __maybe_unused)
+{
+ return ERR_PTR(-LIBBPF_ERRNO__NOUBPF);
+}
#endif

int bpf_map__get_fd(struct bpf_map *map)
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 8e69c6f..41c35fd 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -92,6 +92,7 @@ int bpf_program__set_ubpf(struct bpf_program *prog);
bool bpf_program__is_ubpf(struct bpf_program *prog);

int bpf_program__fd(struct bpf_program *prog);
+struct ubpf_vm *bpf_program__vm(struct bpf_program *prog);

struct bpf_insn;

@@ -162,6 +163,7 @@ int bpf_program__set_prep(struct bpf_program *prog, int nr_instance,
bpf_program_prep_t prep);

int bpf_program__nth_fd(struct bpf_program *prog, int n);
+struct ubpf_vm *bpf_program__nth_vm(struct bpf_program *prog, int n);

/*
* We don't need __attribute__((packed)) now since it is
--
1.8.3.4

2016-04-20 18:04:57

by Wang Nan

[permalink] [raw]
Subject: [RFC PATCH 05/13] bpf tools: Save engine type in bpf_program

Add an 'engine' field in bpf_program to indicate whether a program is a
ubpf program or kernel bpf program. For compatibility, the default
engine is kernel bpf, unless explicitly set to ubpf using
bpf_program__set_ubpf().

Signed-off-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Li Zefan <[email protected]>
---
tools/lib/bpf/libbpf.c | 40 ++++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 4 ++++
2 files changed, 44 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index dd5a107..3755846 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -80,6 +80,7 @@ static const char *libbpf_strerror_table[NR_ERRNO] = {
[ERRCODE_OFFSET(VERIFY)] = "Kernel verifier blocks program loading",
[ERRCODE_OFFSET(PROG2BIG)] = "Program too big",
[ERRCODE_OFFSET(KVER)] = "Incorrect kernel version",
+ [ERRCODE_OFFSET(NOUBPF)] = "UBPF support is not compiled",
};

int libbpf_strerror(int err, char *buf, size_t size)
@@ -147,6 +148,11 @@ union prog_instance {
* linux/filter.h.
*/
struct bpf_program {
+ enum {
+ ENGINE_UNKNOWN,
+ ENGINE_KBPF,
+ ENGINE_UBPF,
+ } engine;
/* Index in elf obj file, for relocation use. */
int idx;
char *section_name;
@@ -291,6 +297,7 @@ bpf_program__init(void *data, size_t size, char *name, int idx,
memcpy(prog->insns, data,
prog->insns_cnt * sizeof(struct bpf_insn));
prog->idx = idx;
+ prog->engine = ENGINE_UNKNOWN;
prog->instances.array = NULL;
prog->instances.nr = -1;

@@ -941,6 +948,11 @@ bpf_program__load(struct bpf_program *prog,
{
int err = 0, fd, i;

+ if (prog->engine == ENGINE_UNKNOWN)
+ prog->engine = ENGINE_KBPF;
+ if (prog->engine != ENGINE_KBPF)
+ return -EINVAL;
+
if (prog->instances.nr < 0 || !prog->instances.array) {
if (prog->preprocessor) {
pr_warning("Internal error: can't load program '%s'\n",
@@ -1318,6 +1330,34 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n)
return fd;
}

+#ifdef HAVE_UBPF_SUPPORT
+int bpf_program__set_ubpf(struct bpf_program *prog)
+{
+ if (prog->engine != ENGINE_UNKNOWN) {
+ pr_warning("Can't set program %s to ubpf\n",
+ prog->section_name);
+ return -EINVAL;
+ }
+ prog->engine = ENGINE_UBPF;
+ return 0;
+}
+
+bool bpf_program__is_ubpf(struct bpf_program *prog)
+{
+ return prog->engine == ENGINE_UBPF;
+}
+#else
+int bpf_program__set_ubpf(struct bpf_program *prog __maybe_unused)
+{
+ return -LIBBPF_ERRNO__NOUBPF;
+}
+
+bool bpf_program__is_ubpf(struct bpf_program *prog __maybe_unused)
+{
+ return false;
+}
+#endif
+
int bpf_map__get_fd(struct bpf_map *map)
{
if (!map)
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index a51594c..f6965ce 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -26,6 +26,7 @@ enum libbpf_errno {
LIBBPF_ERRNO__VERIFY, /* Kernel verifier blocks program loading */
LIBBPF_ERRNO__PROG2BIG, /* Program too big */
LIBBPF_ERRNO__KVER, /* Incorrect kernel version */
+ LIBBPF_ERRNO__NOUBPF, /* UBPF support is not compiled */
__LIBBPF_ERRNO__END,
};

@@ -86,6 +87,9 @@ int bpf_program__get_private(struct bpf_program *prog,

const char *bpf_program__title(struct bpf_program *prog, bool needs_copy);

+int bpf_program__set_ubpf(struct bpf_program *prog);
+bool bpf_program__is_ubpf(struct bpf_program *prog);
+
int bpf_program__fd(struct bpf_program *prog);

struct bpf_insn;
--
1.8.3.4

2016-04-20 18:02:27

by Wang Nan

[permalink] [raw]
Subject: [RFC PATCH 02/13] tools: Add ubpf feature test

Add test-ubpf.c to find libubpf, which can be found from [1].

ubpf is user space BPF engine. Following commits will utilize ubpf
to make perf dynamically run some profiling scripts in user space.

[1] https://github.com/iovisor/ubpf.git

Signed-off-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Li Zefan <[email protected]>
---
tools/build/Makefile.feature | 6 ++++--
tools/build/feature/Makefile | 6 +++++-
tools/build/feature/test-ubpf.c | 11 +++++++++++
tools/lib/bpf/Makefile | 4 ++--
4 files changed, 22 insertions(+), 5 deletions(-)
create mode 100644 tools/build/feature/test-ubpf.c

diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
index 9f87861..eb84b6a 100644
--- a/tools/build/Makefile.feature
+++ b/tools/build/Makefile.feature
@@ -56,7 +56,8 @@ FEATURE_TESTS_BASIC := \
zlib \
lzma \
get_cpuid \
- bpf
+ bpf \
+ ubpf

# FEATURE_TESTS_BASIC + FEATURE_TESTS_EXTRA is the complete list
# of all feature tests
@@ -96,7 +97,8 @@ FEATURE_DISPLAY ?= \
zlib \
lzma \
get_cpuid \
- bpf
+ bpf \
+ ubpf

# Set FEATURE_CHECK_(C|LD)FLAGS-all for all FEATURE_TESTS features.
# If in the future we need per-feature checks/flags for features not
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
index 4ae94db..7480e0f 100644
--- a/tools/build/feature/Makefile
+++ b/tools/build/feature/Makefile
@@ -37,7 +37,8 @@ FILES= \
test-zlib.bin \
test-lzma.bin \
test-bpf.bin \
- test-get_cpuid.bin
+ test-get_cpuid.bin \
+ test-ubpf.bin

FILES := $(addprefix $(OUTPUT),$(FILES))

@@ -182,6 +183,9 @@ $(OUTPUT)test-get_cpuid.bin:
$(OUTPUT)test-bpf.bin:
$(BUILD)

+$(OUTPUT)test-ubpf.bin:
+ $(BUILD) -lubpf
+
-include $(OUTPUT)*.d

###############################
diff --git a/tools/build/feature/test-ubpf.c b/tools/build/feature/test-ubpf.c
new file mode 100644
index 0000000..3c0a257
--- /dev/null
+++ b/tools/build/feature/test-ubpf.c
@@ -0,0 +1,11 @@
+#include <stdlib.h>
+#include <ubpf.h>
+
+int main(void)
+{
+ struct ubpf_vm *vm;
+
+ vm = ubpf_create();
+ ubpf_destroy(vm);
+ return 0;
+}
diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile
index fc1bc75..8dfa512 100644
--- a/tools/lib/bpf/Makefile
+++ b/tools/lib/bpf/Makefile
@@ -65,8 +65,8 @@ ifndef VERBOSE
endif

FEATURE_USER = .libbpf
-FEATURE_TESTS = libelf libelf-getphdrnum libelf-mmap bpf
-FEATURE_DISPLAY = libelf bpf
+FEATURE_TESTS = libelf libelf-getphdrnum libelf-mmap bpf ubpf
+FEATURE_DISPLAY = libelf bpf ubpf

INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/arch/$(ARCH)/include/uapi -I$(srctree)/include/uapi
FEATURE_CHECK_CFLAGS-bpf = $(INCLUDES)
--
1.8.3.4

2016-04-20 19:12:01

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [RFC PATCH 02/13] tools: Add ubpf feature test

Em Wed, Apr 20, 2016 at 06:01:42PM +0000, Wang Nan escreveu:
> Add test-ubpf.c to find libubpf, which can be found from [1].
>
> ubpf is user space BPF engine. Following commits will utilize ubpf
> to make perf dynamically run some profiling scripts in user space.
>
> [1] https://github.com/iovisor/ubpf.git

Ok, but the rule we've been following is that if a library is not generally
available in distros, we don't try to build it, because it will fail in almost
all cases, causing confusion and adding overhead to the build for no reason.

So, please make it behave like babeltrace, as described in the changeset below,
i.e. to build with UBPF, one will need to do:

$ make UBPF=1

I'm applying the patches and testing, but will need the above to proceed wrt
pushing it upstream.

- Arnaldo

commit 6ab2b762befd192b90704c5c7898f5abf8ebb387
Author: Jiri Olsa <[email protected]>
Date: Sat Mar 28 11:30:30 2015 +0100

perf build: Disable libbabeltrace check by default

Disabling libbabeltrace check by default and replacing the
NO_LIBBABELTRACE make variable with LIBBABELTRACE.

Users wanting the libbabeltrace feature need to build via:

$ make LIBBABELTRACE=1

The reason for this is that the libababeltrace interface we use (version
1.3) hasn't been packaged/released yet, thus the failing feature check
only slows down build and confuses other (non CTF) developers.

Requested-by: Ingo Molnar <[email protected]>
Signed-off-by: Jiri Olsa <[email protected]>
Acked-by: Ingo Molnar <[email protected]>


> Signed-off-by: Wang Nan <[email protected]>
> Cc: Arnaldo Carvalho de Melo <[email protected]>
> Cc: Alexei Starovoitov <[email protected]>
> Cc: Brendan Gregg <[email protected]>
> Cc: Jiri Olsa <[email protected]>
> Cc: Li Zefan <[email protected]>
> ---
> tools/build/Makefile.feature | 6 ++++--
> tools/build/feature/Makefile | 6 +++++-
> tools/build/feature/test-ubpf.c | 11 +++++++++++
> tools/lib/bpf/Makefile | 4 ++--
> 4 files changed, 22 insertions(+), 5 deletions(-)
> create mode 100644 tools/build/feature/test-ubpf.c
>
> diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
> index 9f87861..eb84b6a 100644
> --- a/tools/build/Makefile.feature
> +++ b/tools/build/Makefile.feature
> @@ -56,7 +56,8 @@ FEATURE_TESTS_BASIC := \
> zlib \
> lzma \
> get_cpuid \
> - bpf
> + bpf \
> + ubpf
>
> # FEATURE_TESTS_BASIC + FEATURE_TESTS_EXTRA is the complete list
> # of all feature tests
> @@ -96,7 +97,8 @@ FEATURE_DISPLAY ?= \
> zlib \
> lzma \
> get_cpuid \
> - bpf
> + bpf \
> + ubpf
>
> # Set FEATURE_CHECK_(C|LD)FLAGS-all for all FEATURE_TESTS features.
> # If in the future we need per-feature checks/flags for features not
> diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
> index 4ae94db..7480e0f 100644
> --- a/tools/build/feature/Makefile
> +++ b/tools/build/feature/Makefile
> @@ -37,7 +37,8 @@ FILES= \
> test-zlib.bin \
> test-lzma.bin \
> test-bpf.bin \
> - test-get_cpuid.bin
> + test-get_cpuid.bin \
> + test-ubpf.bin
>
> FILES := $(addprefix $(OUTPUT),$(FILES))
>
> @@ -182,6 +183,9 @@ $(OUTPUT)test-get_cpuid.bin:
> $(OUTPUT)test-bpf.bin:
> $(BUILD)
>
> +$(OUTPUT)test-ubpf.bin:
> + $(BUILD) -lubpf
> +
> -include $(OUTPUT)*.d
>
> ###############################
> diff --git a/tools/build/feature/test-ubpf.c b/tools/build/feature/test-ubpf.c
> new file mode 100644
> index 0000000..3c0a257
> --- /dev/null
> +++ b/tools/build/feature/test-ubpf.c
> @@ -0,0 +1,11 @@
> +#include <stdlib.h>
> +#include <ubpf.h>
> +
> +int main(void)
> +{
> + struct ubpf_vm *vm;
> +
> + vm = ubpf_create();
> + ubpf_destroy(vm);
> + return 0;
> +}
> diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile
> index fc1bc75..8dfa512 100644
> --- a/tools/lib/bpf/Makefile
> +++ b/tools/lib/bpf/Makefile
> @@ -65,8 +65,8 @@ ifndef VERBOSE
> endif
>
> FEATURE_USER = .libbpf
> -FEATURE_TESTS = libelf libelf-getphdrnum libelf-mmap bpf
> -FEATURE_DISPLAY = libelf bpf
> +FEATURE_TESTS = libelf libelf-getphdrnum libelf-mmap bpf ubpf
> +FEATURE_DISPLAY = libelf bpf ubpf
>
> INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/arch/$(ARCH)/include/uapi -I$(srctree)/include/uapi
> FEATURE_CHECK_CFLAGS-bpf = $(INCLUDES)
> --
> 1.8.3.4

2016-04-20 19:20:48

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [RFC PATCH 04/13] bpf tools: Replace fd array to union array

Em Wed, Apr 20, 2016 at 06:01:44PM +0000, Wang Nan escreveu:
> Following commits will add new types for instance of a bpf program.
> For kernel bpf program, a program instance is fd. For ubpf program,
> the instance of a program is a 'struct ubpf_vm'. This patch promote
> original 'fds' array to 'union prog_instance' array, prepare for
> further extending.
>
> Signed-off-by: Wang Nan <[email protected]>
> Cc: Arnaldo Carvalho de Melo <[email protected]>
> Cc: Alexei Starovoitov <[email protected]>
> Cc: Brendan Gregg <[email protected]>
> Cc: Jiri Olsa <[email protected]>
> Cc: Li Zefan <[email protected]>
> ---
> tools/lib/bpf/libbpf.c | 44 ++++++++++++++++++++++++--------------------
> 1 file changed, 24 insertions(+), 20 deletions(-)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 55b739e..dd5a107 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -138,6 +138,10 @@ int libbpf_strerror(int err, char *buf, size_t size)
> # define LIBBPF_ELF_C_READ_MMAP ELF_C_READ
> #endif
>
> +union prog_instance {
> + int fd;
> +};
> +
> /*
> * bpf_prog should be a better name but it has been used in
> * linux/filter.h.
> @@ -157,7 +161,7 @@ struct bpf_program {
>
> struct {
> int nr;
> - int *fds;
> + union prog_instance *array;
> } instances;

Why not just do:

struct {
int nr;
union {
int *fds;
};
} instances;

And then do no further changes and when going to the ubpf (haven't
looked at the actual patch, proposing this based on your above
description), just transform it into:

struct {
int nr;
union {
int *fds;
struct ubpf_vm *vms;
};
} instances;

Or plain use a void pointer and use:

struct {
int nr;
void *entries;
} instances;

if (prog->instances.nr > 0) {
int *fds = prog->instances.entries;

for (i = 0; i < prog->instances.nr; i++)
zclose(fd[i]);
}

> bpf_program_prep_t preprocessor;
>
> @@ -229,14 +233,14 @@ static void bpf_program__unload(struct bpf_program *prog)
> */
> if (prog->instances.nr > 0) {
> for (i = 0; i < prog->instances.nr; i++)
> - zclose(prog->instances.fds[i]);
> + zclose(prog->instances.array[i].fd);
> } else if (prog->instances.nr != -1) {
> pr_warning("Internal error: instances.nr is %d\n",
> prog->instances.nr);
> }
>
> prog->instances.nr = -1;
> - zfree(&prog->instances.fds);
> + zfree(&prog->instances.array);
> }
>
> static void bpf_program__exit(struct bpf_program *prog)
> @@ -287,7 +291,7 @@ bpf_program__init(void *data, size_t size, char *name, int idx,
> memcpy(prog->insns, data,
> prog->insns_cnt * sizeof(struct bpf_insn));
> prog->idx = idx;
> - prog->instances.fds = NULL;
> + prog->instances.array = NULL;
> prog->instances.nr = -1;
>
> return 0;
> @@ -937,20 +941,20 @@ bpf_program__load(struct bpf_program *prog,
> {
> int err = 0, fd, i;
>
> - if (prog->instances.nr < 0 || !prog->instances.fds) {
> + if (prog->instances.nr < 0 || !prog->instances.array) {
> if (prog->preprocessor) {
> pr_warning("Internal error: can't load program '%s'\n",
> prog->section_name);
> return -LIBBPF_ERRNO__INTERNAL;
> }
>
> - prog->instances.fds = malloc(sizeof(int));
> - if (!prog->instances.fds) {
> - pr_warning("Not enough memory for BPF fds\n");
> + prog->instances.array = malloc(sizeof(union prog_instance));
> + if (!prog->instances.array) {
> + pr_warning("Not enough memory for BPF instance\n");
> return -ENOMEM;
> }
> prog->instances.nr = 1;
> - prog->instances.fds[0] = -1;
> + prog->instances.array[0].fd = -1;
> }
>
> if (!prog->preprocessor) {
> @@ -961,7 +965,7 @@ bpf_program__load(struct bpf_program *prog,
> err = load_program(prog->insns, prog->insns_cnt,
> license, kern_version, &fd);
> if (!err)
> - prog->instances.fds[0] = fd;
> + prog->instances.array[0].fd = fd;
> goto out;
> }
>
> @@ -981,7 +985,7 @@ bpf_program__load(struct bpf_program *prog,
> if (!result.new_insn_ptr || !result.new_insn_cnt) {
> pr_debug("Skip loading the %dth instance of program '%s'\n",
> i, prog->section_name);
> - prog->instances.fds[i] = -1;
> + prog->instances.array[i].fd = -1;
> if (result.pfd)
> *result.pfd = -1;
> continue;
> @@ -999,7 +1003,7 @@ bpf_program__load(struct bpf_program *prog,
>
> if (result.pfd)
> *result.pfd = fd;
> - prog->instances.fds[i] = fd;
> + prog->instances.array[i].fd = fd;
> }
> out:
> if (err)
> @@ -1269,27 +1273,27 @@ int bpf_program__fd(struct bpf_program *prog)
> int bpf_program__set_prep(struct bpf_program *prog, int nr_instances,
> bpf_program_prep_t prep)
> {
> - int *instances_fds;
> + union prog_instance *array;
>
> if (nr_instances <= 0 || !prep)
> return -EINVAL;
>
> - if (prog->instances.nr > 0 || prog->instances.fds) {
> + if (prog->instances.nr > 0 || prog->instances.array) {
> pr_warning("Can't set pre-processor after loading\n");
> return -EINVAL;
> }
>
> - instances_fds = malloc(sizeof(int) * nr_instances);
> - if (!instances_fds) {
> - pr_warning("alloc memory failed for fds\n");
> + array = malloc(sizeof(array[0]) * nr_instances);
> + if (!array) {
> + pr_warning("alloc memory failed for instances\n");
> return -ENOMEM;
> }
>
> /* fill all fd with -1 */
> - memset(instances_fds, -1, sizeof(int) * nr_instances);
> + memset(array, -1, sizeof(array[0]) * nr_instances);
>
> prog->instances.nr = nr_instances;
> - prog->instances.fds = instances_fds;
> + prog->instances.array = array;
> prog->preprocessor = prep;
> return 0;
> }
> @@ -1304,7 +1308,7 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n)
> return -EINVAL;
> }
>
> - fd = prog->instances.fds[n];
> + fd = prog->instances.array[n].fd;
> if (fd < 0) {
> pr_warning("%dth instance of program '%s' is invalid\n",
> n, prog->section_name);
> --
> 1.8.3.4

2016-04-20 19:23:22

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [RFC PATCH 05/13] bpf tools: Save engine type in bpf_program

Em Wed, Apr 20, 2016 at 06:01:45PM +0000, Wang Nan escreveu:
> Add an 'engine' field in bpf_program to indicate whether a program is a
> ubpf program or kernel bpf program. For compatibility, the default
> engine is kernel bpf, unless explicitly set to ubpf using
> bpf_program__set_ubpf().
>
> Signed-off-by: Wang Nan <[email protected]>
> Cc: Arnaldo Carvalho de Melo <[email protected]>
> Cc: Alexei Starovoitov <[email protected]>
> Cc: Brendan Gregg <[email protected]>
> Cc: Jiri Olsa <[email protected]>
> Cc: Li Zefan <[email protected]>
> ---
> tools/lib/bpf/libbpf.c | 40 ++++++++++++++++++++++++++++++++++++++++
> tools/lib/bpf/libbpf.h | 4 ++++
> 2 files changed, 44 insertions(+)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index dd5a107..3755846 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -80,6 +80,7 @@ static const char *libbpf_strerror_table[NR_ERRNO] = {
> [ERRCODE_OFFSET(VERIFY)] = "Kernel verifier blocks program loading",
> [ERRCODE_OFFSET(PROG2BIG)] = "Program too big",
> [ERRCODE_OFFSET(KVER)] = "Incorrect kernel version",
> + [ERRCODE_OFFSET(NOUBPF)] = "UBPF support is not compiled",
> };
>
> int libbpf_strerror(int err, char *buf, size_t size)
> @@ -147,6 +148,11 @@ union prog_instance {
> * linux/filter.h.
> */
> struct bpf_program {
> + enum {
> + ENGINE_UNKNOWN,
> + ENGINE_KBPF,
> + ENGINE_UBPF,
> + } engine;
> /* Index in elf obj file, for relocation use. */
> int idx;
> char *section_name;
> @@ -291,6 +297,7 @@ bpf_program__init(void *data, size_t size, char *name, int idx,
> memcpy(prog->insns, data,
> prog->insns_cnt * sizeof(struct bpf_insn));
> prog->idx = idx;
> + prog->engine = ENGINE_UNKNOWN;
> prog->instances.array = NULL;
> prog->instances.nr = -1;
>
> @@ -941,6 +948,11 @@ bpf_program__load(struct bpf_program *prog,
> {
> int err = 0, fd, i;
>
> + if (prog->engine == ENGINE_UNKNOWN)
> + prog->engine = ENGINE_KBPF;
> + if (prog->engine != ENGINE_KBPF)

else if (prog->engine != ENGINE_KBPF)

Nitpicking a bit :-)

> + return -EINVAL;
> +
> if (prog->instances.nr < 0 || !prog->instances.array) {
> if (prog->preprocessor) {
> pr_warning("Internal error: can't load program '%s'\n",
> @@ -1318,6 +1330,34 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n)
> return fd;
> }
>
> +#ifdef HAVE_UBPF_SUPPORT
> +int bpf_program__set_ubpf(struct bpf_program *prog)
> +{
> + if (prog->engine != ENGINE_UNKNOWN) {
> + pr_warning("Can't set program %s to ubpf\n",
> + prog->section_name);
> + return -EINVAL;
> + }
> + prog->engine = ENGINE_UBPF;
> + return 0;
> +}
> +
> +bool bpf_program__is_ubpf(struct bpf_program *prog)
> +{
> + return prog->engine == ENGINE_UBPF;
> +}
> +#else
> +int bpf_program__set_ubpf(struct bpf_program *prog __maybe_unused)
> +{
> + return -LIBBPF_ERRNO__NOUBPF;
> +}
> +
> +bool bpf_program__is_ubpf(struct bpf_program *prog __maybe_unused)
> +{
> + return false;
> +}
> +#endif
> +
> int bpf_map__get_fd(struct bpf_map *map)
> {
> if (!map)
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index a51594c..f6965ce 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -26,6 +26,7 @@ enum libbpf_errno {
> LIBBPF_ERRNO__VERIFY, /* Kernel verifier blocks program loading */
> LIBBPF_ERRNO__PROG2BIG, /* Program too big */
> LIBBPF_ERRNO__KVER, /* Incorrect kernel version */
> + LIBBPF_ERRNO__NOUBPF, /* UBPF support is not compiled */
> __LIBBPF_ERRNO__END,
> };
>
> @@ -86,6 +87,9 @@ int bpf_program__get_private(struct bpf_program *prog,
>
> const char *bpf_program__title(struct bpf_program *prog, bool needs_copy);
>
> +int bpf_program__set_ubpf(struct bpf_program *prog);
> +bool bpf_program__is_ubpf(struct bpf_program *prog);
> +
> int bpf_program__fd(struct bpf_program *prog);
>
> struct bpf_insn;
> --
> 1.8.3.4

2016-04-20 19:31:03

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [RFC PATCH 06/13] bpf tools: Introduce ubpf_vm to program instance union

Em Wed, Apr 20, 2016 at 06:01:46PM +0000, Wang Nan escreveu:
> Add 'struct ubpf_vm *' into prog_instance union. Introduce if_engine()
> macro to merge common code.
>
> Signed-off-by: Wang Nan <[email protected]>
> Cc: Arnaldo Carvalho de Melo <[email protected]>
> Cc: Alexei Starovoitov <[email protected]>
> Cc: Brendan Gregg <[email protected]>
> Cc: Jiri Olsa <[email protected]>
> Cc: Li Zefan <[email protected]>
> ---
> tools/lib/bpf/libbpf.c | 76 ++++++++++++++++++++++++++++++++++++++++++++------
> 1 file changed, 68 insertions(+), 8 deletions(-)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 3755846..3a969fd 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -141,6 +141,9 @@ int libbpf_strerror(int err, char *buf, size_t size)
>
> union prog_instance {
> int fd;
> +#ifdef HAVE_UBPF_SUPPORT
> + struct ubpf_vm *vm;
> +#endif
> };
>
> /*
> @@ -176,6 +179,56 @@ struct bpf_program {
> bpf_program_clear_priv_t clear_priv;
> };
>
> +#ifdef HAVE_UBPF_SUPPORT
> +# define __if_engine(p, d, k, u) \
> + do { \
> + switch (p->engine) { \
> + default: \
> + case ENGINE_UNKNOWN: { \
> + d; \
> + } \
> + case ENGINE_KBPF: { \
> + k; break; \
> + } \
> + case ENGINE_UBPF: { \
> + u; break; \
> + } \
> + } \
> + } while(0)

These macro tricks are getting unecessarily overly complicated :-\

> +
> +/*
> + * ubpf_destroy() doesn't accept NULL input. This wrapper makes
> + * it similar to zclose.
> + */
> +# define __ubpf_destroy(vm) do { \
> + if (vm) \
> + ubpf_destroy(vm); \
> + (vm) = NULL; \
> +} while(0)
> +
> +#else
> +# define __if_engine(p, d, k, u) \
> + do { k; } while(0)
> +#endif

Why use just one letter parameters, give them proper names

> +
> +#define instan_fd(i) instances.array[i].fd
> +#define instan_vm(i) instances.array[i].vm
> +
> +#define if_engine(p, k, u) __if_engine(p, do { } while(0), k, u)
> +#define set_instance(p, i, k, u) \
> + if_engine(p, \
> + p->instan_fd(i) = k, \
> + p->instan_vm(i) = u)

If you had used a void pointer for instances->entries you wouldn't have
to play such tricks, right?

> +
> +static inline void init_instance_array(struct bpf_program *prog)
> +{
> + size_t size = sizeof(prog->instances.array[0]) * prog->instances.nr;
> +
> + if_engine(prog,
> + memset(prog->instances.array, -1, size),
> + memset(prog->instances.array, 0, size));
> +}



> +
> struct bpf_map {
> int fd;
> char *name;
> @@ -239,7 +292,9 @@ static void bpf_program__unload(struct bpf_program *prog)
> */
> if (prog->instances.nr > 0) {
> for (i = 0; i < prog->instances.nr; i++)
> - zclose(prog->instances.array[i].fd);
> + if_engine(prog,
> + zclose(prog->instan_fd(i)),
> + __ubpf_destroy(prog->instan_vm(i)));

So if we have more types of instances this will become a
switch_engine()?

> } else if (prog->instances.nr != -1) {
> pr_warning("Internal error: instances.nr is %d\n",
> prog->instances.nr);
> @@ -966,7 +1021,7 @@ bpf_program__load(struct bpf_program *prog,
> return -ENOMEM;
> }
> prog->instances.nr = 1;
> - prog->instances.array[0].fd = -1;
> + set_instance(prog, 0, -1, NULL);

And here we would go on adding more and more values? Why not have some

struct bpf_engine {
void (*init)(struct bpf_program *prog);
void (*fini)(struct bpf_program *prog);
}

One for the kernel "engine", the other for the userspace one?

> }
>
> if (!prog->preprocessor) {
> @@ -977,7 +1032,7 @@ bpf_program__load(struct bpf_program *prog,
> err = load_program(prog->insns, prog->insns_cnt,
> license, kern_version, &fd);
> if (!err)
> - prog->instances.array[0].fd = fd;
> + prog->instan_fd(0) = fd;
> goto out;
> }
>
> @@ -997,7 +1052,7 @@ bpf_program__load(struct bpf_program *prog,
> if (!result.new_insn_ptr || !result.new_insn_cnt) {
> pr_debug("Skip loading the %dth instance of program '%s'\n",
> i, prog->section_name);
> - prog->instances.array[i].fd = -1;
> + prog->instan_fd(i) = -1;
> if (result.pfd)
> *result.pfd = -1;
> continue;
> @@ -1015,7 +1070,7 @@ bpf_program__load(struct bpf_program *prog,
>
> if (result.pfd)
> *result.pfd = fd;
> - prog->instances.array[i].fd = fd;
> + prog->instan_fd(i) = fd;
> }
> out:
> if (err)
> @@ -1301,12 +1356,11 @@ int bpf_program__set_prep(struct bpf_program *prog, int nr_instances,
> return -ENOMEM;
> }
>
> - /* fill all fd with -1 */
> - memset(array, -1, sizeof(array[0]) * nr_instances);
> -
> prog->instances.nr = nr_instances;
> prog->instances.array = array;
> prog->preprocessor = prep;
> +
> + init_instance_array(prog);
> return 0;
> }
>
> @@ -1314,6 +1368,12 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n)
> {
> int fd;
>
> + if (prog->engine != ENGINE_KBPF) {
> + pr_warning("Can't get fd from program %s: engine not KBPF or not loaded\n",
> + prog->section_name);
> + return -EINVAL;
> + }
> +
> if (n >= prog->instances.nr || n < 0) {
> pr_warning("Can't get the %dth fd from program %s: only %d instances\n",
> n, prog->section_name, prog->instances.nr);
> --
> 1.8.3.4

2016-04-20 19:34:39

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [RFC PATCH 07/13] bpf tools: Load ubpf program

Em Wed, Apr 20, 2016 at 06:01:47PM +0000, Wang Nan escreveu:
> In bpf_program__load(), load ubpf program according to its engine type.
>
> API is improvemented to hold 'struct ubpf_vm *'.
>
> Signed-off-by: Wang Nan <[email protected]>
> Cc: Arnaldo Carvalho de Melo <[email protected]>
> Cc: Alexei Starovoitov <[email protected]>
> Cc: Brendan Gregg <[email protected]>
> Cc: Jiri Olsa <[email protected]>
> Cc: Li Zefan <[email protected]>
> ---
> tools/lib/bpf/libbpf.c | 66 +++++++++++++++++++++++++++++++++++++++-----------
> tools/lib/bpf/libbpf.h | 11 +++++++--
> 2 files changed, 61 insertions(+), 16 deletions(-)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 3a969fd..e4a1e77 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -81,6 +81,7 @@ static const char *libbpf_strerror_table[NR_ERRNO] = {
> [ERRCODE_OFFSET(PROG2BIG)] = "Program too big",
> [ERRCODE_OFFSET(KVER)] = "Incorrect kernel version",
> [ERRCODE_OFFSET(NOUBPF)] = "UBPF support is not compiled",
> + [ERRCODE_OFFSET(LOADUBPF)] = "Failed to load user space BPF program",
> };
>
> int libbpf_strerror(int err, char *buf, size_t size)
> @@ -949,6 +950,31 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
> return 0;
> }
>
> +#ifdef HAVE_UBPF_SUPPORT
> +static int
> +load_ubpf_program(struct bpf_insn *insns, int insns_cnt,
> + struct ubpf_vm **pvm)
> +{
> + struct ubpf_vm *vm = ubpf_create();
> + char *message;
> + int err;
> +
> + if (!vm) {
> + pr_warning("Failed to create ubpf vm\n");
> + return -LIBBPF_ERRNO__LOADUBPF;
> + }
> +
> + err = ubpf_load(vm, insns, insns_cnt * sizeof(insns[0]), &message);
> + if (err < 0) {
> + pr_warning("Failed to load ubpf program: %s\n", message);
> + return -LIBBPF_ERRNO__LOADUBPF;
> + }
> +
> + *pvm = vm;
> + return 0;
> +}
> +#endif
> +
> static int
> load_program(struct bpf_insn *insns, int insns_cnt,
> char *license, u32 kern_version, int *pfd)
> @@ -1002,11 +1028,12 @@ bpf_program__load(struct bpf_program *prog,
> char *license, u32 kern_version)
> {
> int err = 0, fd, i;
> +#ifdef HAVE_UBPF_SUPPORT
> + struct ubpf_vm *vm;
> +#endif
>
> if (prog->engine == ENGINE_UNKNOWN)
> prog->engine = ENGINE_KBPF;
> - if (prog->engine != ENGINE_KBPF)
> - return -EINVAL;
>
> if (prog->instances.nr < 0 || !prog->instances.array) {
> if (prog->preprocessor) {
> @@ -1029,10 +1056,15 @@ bpf_program__load(struct bpf_program *prog,
> pr_warning("Program '%s' is inconsistent: nr(%d) != 1\n",
> prog->section_name, prog->instances.nr);
> }
> - err = load_program(prog->insns, prog->insns_cnt,
> - license, kern_version, &fd);
> +
> + if_engine(prog,
> + (err = load_program(prog->insns, prog->insns_cnt,
> + license, kern_version, &fd)),
> + (err = load_ubpf_program(prog->insns, prog->insns_cnt,
> + &vm)));


Same thing here, with that struct bpf_engine fops you would just do:

err = engine->load(prog, arg);

> +
> if (!err)
> - prog->instan_fd(0) = fd;
> + set_instance(prog, 0, fd, vm);
> goto out;
> }
>
> @@ -1052,15 +1084,21 @@ bpf_program__load(struct bpf_program *prog,
> if (!result.new_insn_ptr || !result.new_insn_cnt) {
> pr_debug("Skip loading the %dth instance of program '%s'\n",
> i, prog->section_name);
> - prog->instan_fd(i) = -1;
> - if (result.pfd)
> - *result.pfd = -1;
> +
> + if_engine(prog, prog->instan_fd(i) = -1,
> + prog->instan_vm(i) = NULL);
> + if (result.ptr)
> + if_engine(prog, *result.pfd = -1, *result.pvm = NULL);
> continue;
> }
>
> - err = load_program(result.new_insn_ptr,
> - result.new_insn_cnt,
> - license, kern_version, &fd);
> + if_engine(prog,
> + (err = load_program(result.new_insn_ptr,
> + result.new_insn_cnt,
> + license, kern_version, &fd)),
> + (err = load_ubpf_program(result.new_insn_ptr,
> + result.new_insn_cnt,
> + &vm)));
>
> if (err) {
> pr_warning("Loading the %dth instance of program '%s' failed\n",
> @@ -1068,9 +1106,9 @@ bpf_program__load(struct bpf_program *prog,
> goto out;
> }
>
> - if (result.pfd)
> - *result.pfd = fd;
> - prog->instan_fd(i) = fd;
> + if (result.ptr)
> + if_engine(prog, *result.pfd = fd, *result.pvm = vm);
> + set_instance(prog, i, fd, vm);
> }
> out:
> if (err)
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index f6965ce..8e69c6f 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -27,6 +27,7 @@ enum libbpf_errno {
> LIBBPF_ERRNO__PROG2BIG, /* Program too big */
> LIBBPF_ERRNO__KVER, /* Incorrect kernel version */
> LIBBPF_ERRNO__NOUBPF, /* UBPF support is not compiled */
> + LIBBPF_ERRNO__LOADUBPF, /* Failed to load user space BPF program */
> __LIBBPF_ERRNO__END,
> };
>
> @@ -123,6 +124,8 @@ struct bpf_insn;
> * bpf_program__nth_fd(prog, 0).
> */
>
> +struct ubpf_vm;
> +
> struct bpf_prog_prep_result {
> /*
> * If not NULL, load new instruction array.
> @@ -131,8 +134,12 @@ struct bpf_prog_prep_result {
> struct bpf_insn *new_insn_ptr;
> int new_insn_cnt;
>
> - /* If not NULL, result fd is set to it */
> - int *pfd;
> + /* If not NULL, result is set to it */
> + union {
> + void *ptr;
> + int *pfd;
> + struct ubpf_vm **pvm;
> + };
> };

see? Here you use anonymous unions, but after all I wrote, I think that
you really need a fops struct, like we have so many in the kernel
sources, see struct inode_operations, file_operations, etc, and then
that bpf_program->instance.entries needs just to be a void pointer.

- Arnaldo

2016-04-20 22:06:19

by Alexei Starovoitov

[permalink] [raw]
Subject: Re: [RFC PATCH 00/13] perf tools: Support uBPF script

On Wed, Apr 20, 2016 at 06:01:40PM +0000, Wang Nan wrote:
> This patch set allows to perf invoke some user space BPF scripts on some
> point. uBPF scripts and kernel BPF scripts reside in one BPF object.
> They communicate with each other with BPF maps. uBPF scripts can invoke
> helper functions provided by perf.
>
> At least following new features can be achieved based on uBPF support:
>
> 1) Report statistical result:
> Like DTrace, perf print statistical report before quit. No need to
> extract data using 'perf report'. Statistical method is controled by
> user.
>
> 2) Control perf's behavior:
> Dynamically adjust period of different events. Policy is defined by
> user.
>
> uBPF library is required before compile. It can be found from github:
>
> https://github.com/iovisor/ubpf.git
>
> Following is an example:
>
> Using BPF script attached at the bottom of this commit message, one
> can print histogram of write size before perf exit like this:
>
> # ~/perf record -a -e ./test_ubpf.c &
> [1] 16800
> # dd if=/dev/zero of=/dev/null bs=512 count=5000
> 5000+0 records in
> 5000+0 records out
> 2560000 bytes (2.6 MB) copied, 0.00552838 s, 463 MB/s
> # dd if=/dev/zero of=/dev/null bs=2048 count=5000
> 5000+0 records in
> 5000+0 records out
> 10240000 bytes (10 MB) copied, 0.0188971 s, 542 MB/s
> # fg
> ^C <--- *Press Ctrl-c*
> 2^^0: 47
> 2^^1: 13
> 2^^2: 4
> 2^^3: 130
> 2^^4: 11
> 2^^5: 1051
> 2^^6: 486
> 2^^7: 4863
> 2^^8: 0
> 2^^9: 5003
> 2^^10: 4
> 2^^11: 5003
> 2^^12: 1
> 2^^13: 0
> 2^^14: 0
> 2^^15: 0
> 2^^16: 0
> 2^^17: 0
> 2^^18: 0
> 2^^19: 0
> 2^^20: 0
> [ perf record: Woken up 1 times to write data ]
> [ perf record: Captured and wrote 0.788 MB perf.data ]
>
> Here is test_ubpf.c.
>
> /************ BEGIN ***************/
> #include <uapi/linux/bpf.h>
> #define SEC(NAME) __attribute__((section(NAME), used))
> struct bpf_map_def {
> unsigned int type;
> unsigned int key_size;
> unsigned int value_size;
> unsigned int max_entries;
> };
>
> #define BPF_ANY 0
>
> static void *(*map_lookup_elem)(struct bpf_map_def *, void *) =
> (void *)BPF_FUNC_map_lookup_elem;
>
> static inline unsigned int log2(unsigned int v)
> {
> unsigned int r;
> unsigned int shift;
>
> r = (v > 0xFFFF) << 4; v >>= r;
> shift = (v > 0xFF) << 3; v >>= shift; r |= shift;
> shift = (v > 0xF) << 2; v >>= shift; r |= shift;
> shift = (v > 0x3) << 1; v >>= shift; r |= shift;
> r |= (v >> 1);
> return r;
> }
>
> static inline unsigned int log2l(unsigned long v)
> {
> unsigned int hi = v >> 32;
> if (hi)
> return log2(hi) + 32;
> else
> return log2(v);
> }
>
> struct bpf_map_def SEC("maps") my_hist_map = {
> .type = BPF_MAP_TYPE_ARRAY,
> .key_size = sizeof(int),
> .value_size = sizeof(long),
> .max_entries = 21,
> };
>
> SEC("sys_write=sys_write count")
> int sys_write(void *ctx, int err, long write_size)
> {
> long *value;
> int key = 0;
>
> if (err)
> return 0;
>
> key = log2l(write_size);
> if (key > 20)
> key = 20;
> value = map_lookup_elem(&my_hist_map, &key);
> if (!value)
> return 0;
> __sync_fetch_and_add(value, 1);
> return 0;
> }
> char _license[] SEC("license") = "GPL";
> u32 _version SEC("version") = LINUX_VERSION_CODE;
>
> /* Following ugly magic numbers can be find from tools/perf/util/ubpf-helpers-list.h */
> static int (*ubpf_memcmp)(void *s1, void *s2, unsigned int n) = (void *)1;
> static void (*ubpf_memcpy)(void *d, void *s, unsigned int size) = (void *)2;
> static int (*ubpf_strcmp)(void *s1, void *s2) = (void *)3;
> static int (*ubpf_printf)(char *fmt, ...) = (void *)4;
> static int (*ubpf_map_lookup_elem)(void *map_desc, void *key, void *value) = (void *)5;
> static int (*ubpf_map_update_elem)(void *map_desc, void *key, void *value, unsigned long long flags) = (void *)6;
> static int (*ubpf_map_get_next_key)(void *map_desc, void *key, void *value) = (void *)7;
>
> SEC("UBPF;perf_record_end")
> int perf_record_end(int samples)
> {
> int i, key;
> long value;
> char fmt[] = "2^^%d: %d\n";
>
> for (i = 0; i < 21; i++) {
> ubpf_map_lookup_elem(&my_hist_map, &i, &value);
> ubpf_printf(fmt, i, value);
> }
> return 0;
> }

Interesting!
If bpf is used for both kernel and user side programs, we can allow
almost arbitrary C code for the user side.
There is no need to be limited to a fixed set of helpers.
There is no verifier in user space either.
Just call 'printf("string")' directly.
Wouldn't even need to change interpreter.
Also ubpf was written from scratch with apache2, while perf is gpl,
so you can just link kernel/bpf/core.o directly instead of using external
libraries.
I really meant link .o file compiled for kernel.
Advertize dummy kfree/kmalloc and it will link fine, since perf
will only be calling __bpf_prog_run() which is 99% indepdendent from kernel.
I used to do exactly that long ago while performance tunning the interpreter.
Another option is to fork the interpreter for perf, but I don't like it at all.
Compiling the same bpf/core.c once for kernel and once for perf is another option,
but imo linking core.o is easier.

In general this set and overall bpf in user space makes sense only
if we allow much more flexible C code for user space.
If it's limited to ubpf_* helpers, that will quickly become suboptimal.
Another alternative is to use luajit for user space scripting like
we do in bcc. That gives full flexibility with good performance.
If we can do 'restricted C into bpf' for kernel and 'full C into bpf'
for user space that would be a great model. Note llvm doesn't care
how C looks like. You can call any function in C and use loops.

2016-04-21 08:18:16

by Wang Nan

[permalink] [raw]
Subject: Re: [RFC PATCH 00/13] perf tools: Support uBPF script



On 2016/4/21 6:06, Alexei Starovoitov wrote:
> On Wed, Apr 20, 2016 at 06:01:40PM +0000, Wang Nan wrote:
>> This patch set allows to perf invoke some user space BPF scripts on some
>> point. uBPF scripts and kernel BPF scripts reside in one BPF object.
>> They communicate with each other with BPF maps. uBPF scripts can invoke
>> helper functions provided by perf.
>>
>> At least following new features can be achieved based on uBPF support:
>>
>> 1) Report statistical result:
>> Like DTrace, perf print statistical report before quit. No need to
>> extract data using 'perf report'. Statistical method is controled by
>> user.
>>
>> 2) Control perf's behavior:
>> Dynamically adjust period of different events. Policy is defined by
>> user.
>>
>> uBPF library is required before compile. It can be found from github:
>>
>> https://github.com/iovisor/ubpf.git

[SNIP]

> Interesting!
> If bpf is used for both kernel and user side programs, we can allow
> almost arbitrary C code for the user side.
> There is no need to be limited to a fixed set of helpers.
> There is no verifier in user space either.
> Just call 'printf("string")' directly.

Calling 'printf("string")' would be cool, but it still need
some extra work: .rodata section should be extracted, programs
should be relocated to it.

> Wouldn't even need to change interpreter.
> Also ubpf was written from scratch with apache2, while perf is gpl,
> so you can just link kernel/bpf/core.o directly instead of using external
> libraries.
> I really meant link .o file compiled for kernel.
> Advertize dummy kfree/kmalloc and it will link fine, since perf
> will only be calling __bpf_prog_run() which is 99% indepdendent from kernel.
> I used to do exactly that long ago while performance tunning the interpreter.
> Another option is to fork the interpreter for perf, but I don't like it at all.
> Compiling the same bpf/core.c once for kernel and once for perf is another option,
> but imo linking core.o is easier.

I just realized we can't link apache2 static library into perf. (is that
true?)

Current perf building doesn't support directly linking like
this, because such linking makes perf rely on kernel building, so we
can't build perf before building kernel any more.

One possible solution: providing a kernel build dir to perf builder
and find the corresponding '.o' file from it:

$ make KBUILD_DIR=/kernel/build/dir

/lib/`uname -r`/build can be made as default position.

JIT compiler can also be linked this way.

Another possible solution: using macro trick to allow building bpf/core.c
in perf building. It is possible and simpler, but we could be broken by
kernel modification.

> In general this set and overall bpf in user space makes sense only
> if we allow much more flexible C code for user space.
> If it's limited to ubpf_* helpers, that will quickly become suboptimal.

Yes. I tried to reimplement tracex2 in sample but find it is not an easy
work.
However, in two of my usecase(reporting and controlling), only reporting
require flexible C code. Even we have full featured C, doing statistical
still
require relative complex code, because the lacking of data structure support
such as associate array (dict in python). For controling (for example,
dynamically
period adjustments; output perf.data when something unusual detected), uBPF
programs describe rules and invoke actions (actions should be provided
by perf
helpers), similary to their kernel side counterparts.

So I think no matter uBPF can be 'full C', we should consider making strong
and flexiblity ubpf helpers. Basic reporting method, such as histogram,
should be
provided by a perf helper directly, no need to be rewritten by uBPF. We
can even make a ubpf helper to bridge uBPF and lua scripts, then invokes
lua scripts
at perf hooks. For example:

const char lua_script[] SEC("UBPF-lua;perf_record_exit") "<lua script>";

Thank you.

2016-04-20 19:30:12

by Wang Nan

[permalink] [raw]
Subject: Re: [RFC PATCH 05/13] bpf tools: Save engine type in bpf_program



?????ҵ? iPhone

> ?? 2016??4??21?գ?????3:23??Arnaldo Carvalho de Melo <[email protected]> д????
>
> Em Wed, Apr 20, 2016 at 06:01:45PM +0000, Wang Nan escreveu:
>>

[SNIP]

>>
>> + if (prog->engine == ENGINE_UNKNOWN)
>> + prog->engine = ENGINE_KBPF;
>> + if (prog->engine != ENGINE_KBPF)
>
> else if (prog->engine != ENGINE_KBPF)
>
> Nitpicking a bit :-)
>

We set it to ENGINE_KBPF in previous "if",
so can't use "else".

Do this because I want the engine is KBPF
if not explicitly set to UNPF.

Thank you.

>> + return -EINVAL;
>> +
>> if (prog->instances.nr < 0 || !prog->instances.array) {
>> if (prog->preprocessor) {
>> pr_warning("Internal error: can't load program '%s'\n",
>> @@ -1318,6 +1330,34 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n)
>> return fd;
>> }
>>
>> +#ifdef HAVE_UBPF_SUPPORT
>> +int bpf_program__set_ubpf(struct bpf_program *prog)
>> +{
>> + if (prog->engine != ENGINE_UNKNOWN) {
>> + pr_warning("Can't set program %s to ubpf\n",
>> + prog->section_name);
>> + return -EINVAL;
>> + }
>> + prog->engine = ENGINE_UBPF;
>> + return 0;
>> +}
>> +
>> +bool bpf_program__is_ubpf(struct bpf_program *prog)
>> +{
>> + return prog->engine == ENGINE_UBPF;
>> +}
>> +#else
>> +int bpf_program__set_ubpf(struct bpf_program *prog __maybe_unused)
>> +{
>> + return -LIBBPF_ERRNO__NOUBPF;
>> +}
>> +
>> +bool bpf_program__is_ubpf(struct bpf_program *prog __maybe_unused)
>> +{
>> + return false;
>> +}
>> +#endif
>> +
>> int bpf_map__get_fd(struct bpf_map *map)
>> {
>> if (!map)
>> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
>> index a51594c..f6965ce 100644
>> --- a/tools/lib/bpf/libbpf.h
>> +++ b/tools/lib/bpf/libbpf.h
>> @@ -26,6 +26,7 @@ enum libbpf_errno {
>> LIBBPF_ERRNO__VERIFY, /* Kernel verifier blocks program loading */
>> LIBBPF_ERRNO__PROG2BIG, /* Program too big */
>> LIBBPF_ERRNO__KVER, /* Incorrect kernel version */
>> + LIBBPF_ERRNO__NOUBPF, /* UBPF support is not compiled */
>> __LIBBPF_ERRNO__END,
>> };
>>
>> @@ -86,6 +87,9 @@ int bpf_program__get_private(struct bpf_program *prog,
>>
>> const char *bpf_program__title(struct bpf_program *prog, bool needs_copy);
>>
>> +int bpf_program__set_ubpf(struct bpf_program *prog);
>> +bool bpf_program__is_ubpf(struct bpf_program *prog);
>> +
>> int bpf_program__fd(struct bpf_program *prog);
>>
>> struct bpf_insn;
>> --
>> 1.8.3.4