This is the 6th version which tries to introduce eBPF programs to perf.
It enables 'perf record' to filter events using eBPF programs like:
# perf record --event bpf-file.c sleep 1
and
# perf record --event bpf-file.o sleep 1
This patch series is based on tip/perf/core (a3d8654).
Compared with v5 patch, this series allows user to pass BPF
scriptlet source code file directly, instead of requiring user to
pre-compile it into an ELF object. Which is done mainly by patch 24/32.
In addition, to help users setup LLVM, [bpf] perf config section is
appended. 4 command line options are also appended to 'perf record'
for setting pathes and options of clang and llc. Which is done by patch
25/32 and 32/32.
Other improvements:
1. Adjust context.
2. Get rid of 'trailing whitespace' and 'space before tab in indent'
problems.
3. Error messages are improved when BPF API checking fail.
In this series:
Patch 1/32 adds a feature check to check version of eBPF API.
Patch 2/32 - 21/32 introduce libbpf, which first parse eBPF object
files then load maps and programs into kernel. These patches are
already received 'Acked-by' from Alexei Starovoitov except patch
5/32, which enables opening a memory-based object file image using
bpf_object__open_buffer().
Patch 22/32 - 32/32 are perf related. Patch 24, 25 and 32 are new to
support dynamical compiling.
Here is an example shows the using of dynamic compiling:
# perf record --event lock_page.c ls /
Added new event:
perf_bpf_probe:lock_page (on __lock_page)
You can now use it in all perf tools, such as:
perf record -e perf_bpf_probe:lock_page -aR sleep 1
Added new event:
perf_bpf_probe:lock_page_ret (on __lock_page%return)
You can now use it in all perf tools, such as:
perf record -e perf_bpf_probe:lock_page_ret -aR sleep 1
Added new event:
perf_bpf_probe:unlock_page (on unlock_page)
You can now use it in all perf tools, such as:
perf record -e perf_bpf_probe:unlock_page -aR sleep 1
bin boot dev etc home init lib lib64 linuxrc media mnt proc root run sbin sys tmp usr var
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.002 MB perf.data ]
Removed event: perf_bpf_probe:lock_page
Removed event: perf_bpf_probe:lock_page_ret
Removed event: perf_bpf_probe:unlock_page
Where lock_page.c can be found from:
http://lkml.kernel.org/r/[email protected]
Wang Nan (32):
tools build: Add feature check for eBPF API
bpf tools: Introduce 'bpf' library to tools
bpf tools: Allow caller to set printing function
bpf tools: Open eBPF object file and do basic validation
bpf tools: Read eBPF object from buffer
bpf tools: Check endianess and make libbpf fail early
bpf tools: Iterate over ELF sections to collect information
bpf tools: Collect version and license from ELF sections
bpf tools: Collect map definitions from 'maps' section
bpf tools: Collect symbol table from SHT_SYMTAB section
bpf tools: Collect eBPF programs from their own sections
bpf tools: Collect relocation sections from SHT_REL sections
bpf tools: Record map accessing instructions for each program
bpf tools: Add bpf.c/h for common bpf operations
bpf tools: Create eBPF maps defined in an object file
bpf tools: Relocate eBPF programs
bpf tools: Introduce bpf_load_program() to bpf.c
bpf tools: Load eBPF programs in object files into kernel
bpf tools: Introduce accessors for struct bpf_program
bpf tools: Introduce accessors for struct bpf_object
bpf tools: Link all bpf objects onto a list
perf tools: Make perf depend on libbpf
perf record: Enable passing bpf object file to --event
perf record: Compile scriptlets if pass '.c' to --event
perf tools: Add 'bpf.' config section to perf default config
perf tools: Parse probe points of eBPF programs during preparation
perf probe: Attach trace_probe_event with perf_probe_event
perf record: Probe at kprobe points
perf record: Load all eBPF object into kernel
perf tools: Add bpf_fd field to evsel and config it
perf tools: Attach eBPF program to perf event
perf record: Add LLVM options for compiling BPF scripts
tools/build/Makefile.feature | 6 +-
tools/build/feature/Makefile | 6 +-
tools/build/feature/test-bpf.c | 18 +
tools/lib/bpf/.gitignore | 2 +
tools/lib/bpf/Build | 1 +
tools/lib/bpf/Makefile | 195 ++++++++
tools/lib/bpf/bpf.c | 84 ++++
tools/lib/bpf/bpf.h | 23 +
tools/lib/bpf/libbpf.c | 1037 ++++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 85 ++++
tools/perf/MANIFEST | 3 +
tools/perf/Makefile.perf | 17 +-
tools/perf/builtin-probe.c | 2 +-
tools/perf/builtin-record.c | 40 +-
tools/perf/config/Makefile | 19 +-
tools/perf/tests/make | 4 +-
tools/perf/util/Build | 1 +
tools/perf/util/bpf-loader.c | 477 ++++++++++++++++++
tools/perf/util/bpf-loader.h | 54 +++
tools/perf/util/config.c | 3 +
tools/perf/util/debug.c | 5 +
tools/perf/util/debug.h | 1 +
tools/perf/util/evlist.c | 51 ++
tools/perf/util/evlist.h | 1 +
tools/perf/util/evsel.c | 17 +
tools/perf/util/evsel.h | 1 +
tools/perf/util/parse-events.c | 16 +
tools/perf/util/parse-events.h | 2 +
tools/perf/util/parse-events.l | 6 +
tools/perf/util/parse-events.y | 29 +-
tools/perf/util/probe-event.c | 57 ++-
tools/perf/util/probe-event.h | 5 +-
32 files changed, 2229 insertions(+), 39 deletions(-)
create mode 100644 tools/build/feature/test-bpf.c
create mode 100644 tools/lib/bpf/.gitignore
create mode 100644 tools/lib/bpf/Build
create mode 100644 tools/lib/bpf/Makefile
create mode 100644 tools/lib/bpf/bpf.c
create mode 100644 tools/lib/bpf/bpf.h
create mode 100644 tools/lib/bpf/libbpf.c
create mode 100644 tools/lib/bpf/libbpf.h
create mode 100644 tools/perf/util/bpf-loader.c
create mode 100644 tools/perf/util/bpf-loader.h
--
1.8.3.4
In this patch, eBPF API is checked by compiling a c source file which
uses fields in bpf_attr which will be used by libbpf.
Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
---
tools/build/Makefile.feature | 6 ++++--
tools/build/feature/Makefile | 6 +++++-
tools/build/feature/test-bpf.c | 18 ++++++++++++++++++
3 files changed, 27 insertions(+), 3 deletions(-)
create mode 100644 tools/build/feature/test-bpf.c
diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
index 2975632..5ec6b37 100644
--- a/tools/build/Makefile.feature
+++ b/tools/build/Makefile.feature
@@ -51,7 +51,8 @@ FEATURE_TESTS ?= \
timerfd \
libdw-dwarf-unwind \
zlib \
- lzma
+ lzma \
+ bpf
FEATURE_DISPLAY ?= \
dwarf \
@@ -67,7 +68,8 @@ FEATURE_DISPLAY ?= \
libunwind \
libdw-dwarf-unwind \
zlib \
- lzma
+ lzma \
+ bpf
# 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 463ed8f..1c0d69f 100644
--- a/tools/build/feature/Makefile
+++ b/tools/build/feature/Makefile
@@ -33,7 +33,8 @@ FILES= \
test-compile-32.bin \
test-compile-x32.bin \
test-zlib.bin \
- test-lzma.bin
+ test-lzma.bin \
+ test-bpf.bin
CC := $(CROSS_COMPILE)gcc -MD
PKG_CONFIG := $(CROSS_COMPILE)pkg-config
@@ -156,6 +157,9 @@ test-zlib.bin:
test-lzma.bin:
$(BUILD) -llzma
+test-bpf.bin:
+ $(BUILD)
+
-include *.d
###############################
diff --git a/tools/build/feature/test-bpf.c b/tools/build/feature/test-bpf.c
new file mode 100644
index 0000000..a96160b
--- /dev/null
+++ b/tools/build/feature/test-bpf.c
@@ -0,0 +1,18 @@
+#include <linux/bpf.h>
+
+int main()
+{
+ union bpf_attr attr;
+
+ attr.prog_type = BPF_PROG_TYPE_KPROBE;
+ attr.insn_cnt = 0;
+ attr.insns = 0;
+ attr.license = 0;
+ attr.log_buf = 0;
+ attr.log_size = 0;
+ attr.log_level = 0;
+ attr.kern_version = 0;
+
+ attr = attr;
+ return 0;
+}
--
1.8.3.4
This is the first patch of libbpf. The goal of libbpf is to create a
standard way for accessing eBPF object files. This patch creates
'Makefile' and 'Build' for it, allows 'make' to build libbpf.a and
libbpf.so, 'make install' to put them into proper directories.
Most part of Makefile is borrowed from traceevent.
Before building, it checks the existance of libelf in Makefile, and deny
to build if not found. Instead of throwing an error if libelf not found,
the error raises in a phony target "elfdep". This design is to ensure
'make clean' still workable even if libelf is not found.
Kernel BPF API is also checked. "bpfdep" is used for it.
Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
---
tools/lib/bpf/.gitignore | 2 +
tools/lib/bpf/Build | 1 +
tools/lib/bpf/Makefile | 195 +++++++++++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.c | 14 ++++
tools/lib/bpf/libbpf.h | 11 +++
5 files changed, 223 insertions(+)
create mode 100644 tools/lib/bpf/.gitignore
create mode 100644 tools/lib/bpf/Build
create mode 100644 tools/lib/bpf/Makefile
create mode 100644 tools/lib/bpf/libbpf.c
create mode 100644 tools/lib/bpf/libbpf.h
diff --git a/tools/lib/bpf/.gitignore b/tools/lib/bpf/.gitignore
new file mode 100644
index 0000000..812aeed
--- /dev/null
+++ b/tools/lib/bpf/.gitignore
@@ -0,0 +1,2 @@
+libbpf_version.h
+FEATURE-DUMP
diff --git a/tools/lib/bpf/Build b/tools/lib/bpf/Build
new file mode 100644
index 0000000..a316484
--- /dev/null
+++ b/tools/lib/bpf/Build
@@ -0,0 +1 @@
+libbpf-y := libbpf.o
diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile
new file mode 100644
index 0000000..f68d23a
--- /dev/null
+++ b/tools/lib/bpf/Makefile
@@ -0,0 +1,195 @@
+# Most of this file is copied from tools/lib/traceevent/Makefile
+
+BPF_VERSION = 0
+BPF_PATCHLEVEL = 0
+BPF_EXTRAVERSION = 1
+
+MAKEFLAGS += --no-print-directory
+
+
+# Makefiles suck: This macro sets a default value of $(2) for the
+# variable named by $(1), unless the variable has been set by
+# environment or command line. This is necessary for CC and AR
+# because make sets default values, so the simpler ?= approach
+# won't work as expected.
+define allow-override
+ $(if $(or $(findstring environment,$(origin $(1))),\
+ $(findstring command line,$(origin $(1)))),,\
+ $(eval $(1) = $(2)))
+endef
+
+# Allow setting CC and AR, or setting CROSS_COMPILE as a prefix.
+$(call allow-override,CC,$(CROSS_COMPILE)gcc)
+$(call allow-override,AR,$(CROSS_COMPILE)ar)
+
+INSTALL = install
+
+# Use DESTDIR for installing into a different root directory.
+# This is useful for building a package. The program will be
+# installed in this directory as if it was the root directory.
+# Then the build tool can move it later.
+DESTDIR ?=
+DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))'
+
+LP64 := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1)
+ifeq ($(LP64), 1)
+ libdir_relative = lib64
+else
+ libdir_relative = lib
+endif
+
+prefix ?= /usr/local
+libdir = $(prefix)/$(libdir_relative)
+man_dir = $(prefix)/share/man
+man_dir_SQ = '$(subst ','\'',$(man_dir))'
+
+export man_dir man_dir_SQ INSTALL
+export DESTDIR DESTDIR_SQ
+
+include ../../scripts/Makefile.include
+
+# copy a bit from Linux kbuild
+
+ifeq ("$(origin V)", "command line")
+ VERBOSE = $(V)
+endif
+ifndef VERBOSE
+ VERBOSE = 0
+endif
+
+ifeq ($(srctree),)
+srctree := $(patsubst %/,%,$(dir $(shell pwd)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+#$(info Determined 'srctree' to be $(srctree))
+endif
+
+FEATURE_DISPLAY = libelf libelf-getphdrnum libelf-mmap bpf
+FEATURE_TESTS = libelf bpf
+
+INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/arch/$(ARCH)/include/uapi -I$(srctree)/include/uapi
+FEATURE_CHECK_CFLAGS-bpf = $(INCLUDES)
+
+include $(srctree)/tools/build/Makefile.feature
+
+export prefix libdir src obj
+
+# Shell quotes
+libdir_SQ = $(subst ','\'',$(libdir))
+libdir_relative_SQ = $(subst ','\'',$(libdir_relative))
+plugin_dir_SQ = $(subst ','\'',$(plugin_dir))
+
+LIB_FILE = libbpf.a libbpf.so
+
+VERSION = $(BPF_VERSION)
+PATCHLEVEL = $(BPF_PATCHLEVEL)
+EXTRAVERSION = $(BPF_EXTRAVERSION)
+
+OBJ = $@
+N =
+
+LIBBPF_VERSION = $(BPF_VERSION).$(BPF_PATCHLEVEL).$(BPF_EXTRAVERSION)
+
+# Set compile option CFLAGS
+ifdef EXTRA_CFLAGS
+ CFLAGS := $(EXTRA_CFLAGS)
+else
+ CFLAGS := -g -Wall
+endif
+
+ifeq ($(feature-libelf-mmap), 1)
+ override CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT
+endif
+
+ifeq ($(feature-libelf-getphdrnum), 1)
+ override CFLAGS += -DHAVE_ELF_GETPHDRNUM_SUPPORT
+endif
+
+# Append required CFLAGS
+override CFLAGS += $(EXTRA_WARNINGS)
+override CFLAGS += -Werror -Wall
+override CFLAGS += -fPIC
+override CFLAGS += $(INCLUDES)
+
+ifeq ($(VERBOSE),1)
+ Q =
+else
+ Q = @
+endif
+
+# Disable command line variables (CFLAGS) overide from top
+# level Makefile (perf), otherwise build Makefile will get
+# the same command line setup.
+MAKEOVERRIDES=
+
+export srctree OUTPUT CC LD CFLAGS V
+build := -f $(srctree)/tools/build/Makefile.build dir=. obj
+
+BPF_IN := $(OUTPUT)libbpf-in.o
+LIB_FILE := $(addprefix $(OUTPUT),$(LIB_FILE))
+
+CMD_TARGETS = $(LIB_FILE)
+
+TARGETS = $(CMD_TARGETS)
+
+all: $(VERSION_FILES) all_cmd
+
+all_cmd: $(CMD_TARGETS)
+
+$(BPF_IN): force elfdep bpfdep
+ $(Q)$(MAKE) $(build)=libbpf
+
+$(OUTPUT)libbpf.so: $(BPF_IN)
+ $(QUIET_LINK)$(CC) --shared $^ -o $@
+
+$(OUTPUT)libbpf.a: $(BPF_IN)
+ $(QUIET_LINK)$(RM) $@; $(AR) rcs $@ $^
+
+define update_dir
+ (echo $1 > [email protected]; \
+ if [ -r $@ ] && cmp -s $@ [email protected]; then \
+ rm -f [email protected]; \
+ else \
+ echo ' UPDATE $@'; \
+ mv -f [email protected] $@; \
+ fi);
+endef
+
+define do_install
+ if [ ! -d '$(DESTDIR_SQ)$2' ]; then \
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \
+ fi; \
+ $(INSTALL) $1 '$(DESTDIR_SQ)$2'
+endef
+
+install_lib: all_cmd
+ $(call QUIET_INSTALL, $(LIB_FILE)) \
+ $(call do_install,$(LIB_FILE),$(libdir_SQ))
+
+install: install_lib
+
+### Cleaning rules
+
+config-clean:
+ $(call QUIET_CLEAN, config)
+ $(Q)$(MAKE) -C $(srctree)/tools/build/feature/ clean >/dev/null
+
+clean:
+ $(call QUIET_CLEAN, libbpf) $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d \
+ $(RM) LIBBPF-CFLAGS
+ $(call QUIET_CLEAN, core-gen) $(RM) $(OUTPUT)FEATURE-DUMP
+
+
+
+PHONY += force elfdep bpfdep
+force:
+
+elfdep:
+ @if [ "$(feature-libelf)" != "1" ]; then echo "No libelf found"; exit -1 ; fi
+
+bpfdep:
+ @if [ "$(feature-bpf)" != "1" ]; then echo "BPF API too old"; exit -1 ; fi
+
+# Declare the contents of the .PHONY variable as phony. We keep that
+# information in a variable so we can use it in if_changed and friends.
+.PHONY: $(PHONY)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
new file mode 100644
index 0000000..c08d6bc
--- /dev/null
+++ b/tools/lib/bpf/libbpf.c
@@ -0,0 +1,14 @@
+/*
+ * Common eBPF ELF object loading operations.
+ *
+ * Copyright (C) 2013-2015 Alexei Starovoitov <[email protected]>
+ * Copyright (C) 2015 Wang Nan <[email protected]>
+ * Copyright (C) 2015 Huawei Inc.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <asm/unistd.h>
+#include <linux/bpf.h>
+
+#include "libbpf.h"
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
new file mode 100644
index 0000000..a6f46d9
--- /dev/null
+++ b/tools/lib/bpf/libbpf.h
@@ -0,0 +1,11 @@
+/*
+ * Common eBPF ELF object loading operations.
+ *
+ * Copyright (C) 2013-2015 Alexei Starovoitov <[email protected]>
+ * Copyright (C) 2015 Wang Nan <[email protected]>
+ * Copyright (C) 2015 Huawei Inc.
+ */
+#ifndef __BPF_LIBBPF_H
+#define __BPF_LIBBPF_H
+
+#endif
--
1.8.3.4
By libbpf_set_print(), users of libbpf are allowed to register he/she
own debug, info and warning printing functions. Libbpf will use those
functions to print messages. If not provided, default info and warning
printing functions are fprintf(stderr, ...); default debug printing
is NULL.
This API is designed to be used by perf, enables it to register its own
logging functions to make all logs uniform, instead of separated
logging level control.
Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
---
tools/lib/bpf/libbpf.c | 40 ++++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 12 ++++++++++++
2 files changed, 52 insertions(+)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index c08d6bc..ded37a0 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -7,8 +7,48 @@
*/
#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
#include <unistd.h>
#include <asm/unistd.h>
#include <linux/bpf.h>
#include "libbpf.h"
+
+#define __printf(a, b) __attribute__((format(printf, a, b)))
+
+__printf(1, 2)
+static int __base_pr(const char *format, ...)
+{
+ va_list args;
+ int err;
+
+ va_start(args, format);
+ err = vfprintf(stderr, format, args);
+ va_end(args);
+ return err;
+}
+
+static __printf(1, 2) libbpf_print_fn_t __pr_warning = __base_pr;
+static __printf(1, 2) libbpf_print_fn_t __pr_info = __base_pr;
+static __printf(1, 2) libbpf_print_fn_t __pr_debug = NULL;
+
+#define __pr(func, fmt, ...) \
+do { \
+ if ((func)) \
+ (func)("libbpf: " fmt, ##__VA_ARGS__); \
+} while(0)
+
+#define pr_warning(fmt, ...) __pr(__pr_warning, fmt, ##__VA_ARGS__)
+#define pr_info(fmt, ...) __pr(__pr_info, fmt, ##__VA_ARGS__)
+#define pr_debug(fmt, ...) __pr(__pr_debug, fmt, ##__VA_ARGS__)
+
+void libbpf_set_print(libbpf_print_fn_t warn,
+ libbpf_print_fn_t info,
+ libbpf_print_fn_t debug)
+{
+ __pr_warning = warn;
+ __pr_info = info;
+ __pr_debug = debug;
+}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index a6f46d9..8d1eeba 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -8,4 +8,16 @@
#ifndef __BPF_LIBBPF_H
#define __BPF_LIBBPF_H
+/*
+ * In include/linux/compiler-gcc.h, __printf is defined. However
+ * it should be better if libbpf.h doesn't depend on Linux header file.
+ * So instead of __printf, here we use gcc attribute directly.
+ */
+typedef int (*libbpf_print_fn_t)(const char *, ...)
+ __attribute__((format(printf, 1, 2)));
+
+void libbpf_set_print(libbpf_print_fn_t warn,
+ libbpf_print_fn_t info,
+ libbpf_print_fn_t debug);
+
#endif
--
1.8.3.4
This patch defines basic interface of libbpf. 'struct bpf_object' will
be the handler of each object file. Its internal structure is hide to
user. eBPF object files are compiled by LLVM as ELF format. In this
patch, libelf is used to open those files, read EHDR and do basic
validation according to e_type and e_machine.
All elf related staffs are grouped together and reside in efile field of
'struct bpf_object'. bpf_object__elf_finish() is introduced to clear it.
After all eBPF programs in an object file are loaded, related ELF
information is useless. Close the object file and free those memory.
zfree() and zclose() are introduced to ensure setting NULL pointers and
negative file descriptors after resources are released.
Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
---
tools/lib/bpf/libbpf.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 8 +++
2 files changed, 166 insertions(+)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index ded37a0..197ab5e 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -11,8 +11,12 @@
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
#include <asm/unistd.h>
#include <linux/bpf.h>
+#include <libelf.h>
+#include <gelf.h>
#include "libbpf.h"
@@ -52,3 +56,157 @@ void libbpf_set_print(libbpf_print_fn_t warn,
__pr_info = info;
__pr_debug = debug;
}
+
+/* Copied from tools/perf/util/util.h */
+#ifndef zfree
+# define zfree(ptr) ({ free(*ptr); *ptr = NULL; })
+#endif
+
+#ifndef zclose
+# define zclose(fd) ({ \
+ int ___err = 0; \
+ if ((fd) >= 0) \
+ ___err = close((fd)); \
+ fd = -1; \
+ ___err;})
+#endif
+
+#ifdef HAVE_LIBELF_MMAP_SUPPORT
+# define LIBBPF_ELF_C_READ_MMAP ELF_C_READ_MMAP
+#else
+# define LIBBPF_ELF_C_READ_MMAP ELF_C_READ
+#endif
+
+struct bpf_object {
+ /*
+ * Information when doing elf related work. Only valid if fd
+ * is valid.
+ */
+ struct {
+ int fd;
+ Elf *elf;
+ GElf_Ehdr ehdr;
+ } efile;
+ char path[];
+};
+#define obj_elf_valid(o) ((o)->efile.elf)
+
+static struct bpf_object *bpf_object__new(const char *path)
+{
+ struct bpf_object *obj;
+
+ obj = calloc(1, sizeof(struct bpf_object) + strlen(path) + 1);
+ if (!obj) {
+ pr_warning("alloc memory failed for %s\n", path);
+ return NULL;
+ }
+
+ strcpy(obj->path, path);
+ obj->efile.fd = -1;
+ return obj;
+}
+
+static void bpf_object__elf_finish(struct bpf_object *obj)
+{
+ if (!obj_elf_valid(obj))
+ return;
+
+ if (obj->efile.elf) {
+ elf_end(obj->efile.elf);
+ obj->efile.elf = NULL;
+ }
+ zclose(obj->efile.fd);
+}
+
+static int bpf_object__elf_init(struct bpf_object *obj)
+{
+ int err = 0;
+ GElf_Ehdr *ep;
+
+ if (obj_elf_valid(obj)) {
+ pr_warning("elf init: internal error\n");
+ return -EEXIST;
+ }
+
+ obj->efile.fd = open(obj->path, O_RDONLY);
+ if (obj->efile.fd < 0) {
+ pr_warning("failed to open %s: %s\n", obj->path,
+ strerror(errno));
+ return -errno;
+ }
+
+ obj->efile.elf = elf_begin(obj->efile.fd,
+ LIBBPF_ELF_C_READ_MMAP,
+ NULL);
+ if (!obj->efile.elf) {
+ pr_warning("failed to open %s as ELF file\n",
+ obj->path);
+ err = -EINVAL;
+ goto errout;
+ }
+
+ if (!gelf_getehdr(obj->efile.elf, &obj->efile.ehdr)) {
+ pr_warning("failed to get EHDR from %s\n",
+ obj->path);
+ err = -EINVAL;
+ goto errout;
+ }
+ ep = &obj->efile.ehdr;
+
+ if ((ep->e_type != ET_REL) || (ep->e_machine != 0)) {
+ pr_warning("%s is not an eBPF object file\n",
+ obj->path);
+ err = -EINVAL;
+ goto errout;
+ }
+
+ return 0;
+errout:
+ bpf_object__elf_finish(obj);
+ return err;
+}
+
+static struct bpf_object *
+__bpf_object__open(const char *path)
+{
+ struct bpf_object *obj;
+
+ if (elf_version(EV_CURRENT) == EV_NONE) {
+ pr_warning("failed to init libelf for %s\n", path);
+ return NULL;
+ }
+
+ obj = bpf_object__new(path);
+ if (!obj)
+ return NULL;
+
+ if (bpf_object__elf_init(obj))
+ goto out;
+
+ bpf_object__elf_finish(obj);
+ return obj;
+out:
+ bpf_object__close(obj);
+ return NULL;
+}
+
+struct bpf_object *bpf_object__open(const char *path)
+{
+ /* param validation */
+ if (!path)
+ return NULL;
+
+ pr_debug("loading %s\n", path);
+
+ return __bpf_object__open(path);
+}
+
+void bpf_object__close(struct bpf_object *obj)
+{
+ if (!obj)
+ return;
+
+ bpf_object__elf_finish(obj);
+
+ free(obj);
+}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 8d1eeba..ec3301c 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -8,6 +8,8 @@
#ifndef __BPF_LIBBPF_H
#define __BPF_LIBBPF_H
+#include <stdio.h>
+
/*
* In include/linux/compiler-gcc.h, __printf is defined. However
* it should be better if libbpf.h doesn't depend on Linux header file.
@@ -20,4 +22,10 @@ void libbpf_set_print(libbpf_print_fn_t warn,
libbpf_print_fn_t info,
libbpf_print_fn_t debug);
+/* Hide internal to user */
+struct bpf_object;
+
+struct bpf_object *bpf_object__open(const char *path);
+void bpf_object__close(struct bpf_object *object);
+
#endif
--
1.8.3.4
To support dynamic compiling, this patch allows caller to pass a
in-memory buffer to libbpf by bpf_object__open_buffer(). libbpf
calls elf_memory() to open it as ELF object file.
Because __bpf_object__open() collects all required data and won't need
that buffer anymore, libbpf uses that buffer directly instead of clone
a new buffer. Caller of libbpf can free that buffer or use it do other
things after bpf_object__open_buffer() return.
Signed-off-by: Wang Nan <[email protected]>
---
tools/lib/bpf/libbpf.c | 62 ++++++++++++++++++++++++++++++++++++++++----------
tools/lib/bpf/libbpf.h | 2 ++
2 files changed, 52 insertions(+), 12 deletions(-)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 197ab5e..8ba6ac6 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -84,6 +84,8 @@ struct bpf_object {
*/
struct {
int fd;
+ void *obj_buf;
+ size_t obj_buf_sz;
Elf *elf;
GElf_Ehdr ehdr;
} efile;
@@ -91,7 +93,9 @@ struct bpf_object {
};
#define obj_elf_valid(o) ((o)->efile.elf)
-static struct bpf_object *bpf_object__new(const char *path)
+static struct bpf_object *bpf_object__new(const char *path,
+ void *obj_buf,
+ size_t obj_buf_sz)
{
struct bpf_object *obj;
@@ -103,6 +107,16 @@ static struct bpf_object *bpf_object__new(const char *path)
strcpy(obj->path, path);
obj->efile.fd = -1;
+
+ /*
+ * Caller of this function should also calls
+ * bpf_object__elf_finish() after data collection to return
+ * obj_buf to user. If not, we should duplicate the buffer to
+ * avoid user freeing them before elf finish.
+ */
+ obj->efile.obj_buf = obj_buf;
+ obj->efile.obj_buf_sz = obj_buf_sz;
+
return obj;
}
@@ -116,6 +130,8 @@ static void bpf_object__elf_finish(struct bpf_object *obj)
obj->efile.elf = NULL;
}
zclose(obj->efile.fd);
+ obj->efile.obj_buf = NULL;
+ obj->efile.obj_buf_sz = 0;
}
static int bpf_object__elf_init(struct bpf_object *obj)
@@ -128,16 +144,26 @@ static int bpf_object__elf_init(struct bpf_object *obj)
return -EEXIST;
}
- obj->efile.fd = open(obj->path, O_RDONLY);
- if (obj->efile.fd < 0) {
- pr_warning("failed to open %s: %s\n", obj->path,
- strerror(errno));
- return -errno;
+ if (obj->efile.obj_buf_sz > 0) {
+ /*
+ * obj_buf should have been validated by
+ * bpf_object__open_buffer().
+ */
+ obj->efile.elf = elf_memory(obj->efile.obj_buf,
+ obj->efile.obj_buf_sz);
+ } else {
+ obj->efile.fd = open(obj->path, O_RDONLY);
+ if (obj->efile.fd < 0) {
+ pr_warning("failed to open %s: %s\n", obj->path,
+ strerror(errno));
+ return -errno;
+ }
+
+ obj->efile.elf = elf_begin(obj->efile.fd,
+ LIBBPF_ELF_C_READ_MMAP,
+ NULL);
}
- obj->efile.elf = elf_begin(obj->efile.fd,
- LIBBPF_ELF_C_READ_MMAP,
- NULL);
if (!obj->efile.elf) {
pr_warning("failed to open %s as ELF file\n",
obj->path);
@@ -167,7 +193,7 @@ errout:
}
static struct bpf_object *
-__bpf_object__open(const char *path)
+__bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
{
struct bpf_object *obj;
@@ -176,7 +202,7 @@ __bpf_object__open(const char *path)
return NULL;
}
- obj = bpf_object__new(path);
+ obj = bpf_object__new(path, obj_buf, obj_buf_sz);
if (!obj)
return NULL;
@@ -198,7 +224,19 @@ struct bpf_object *bpf_object__open(const char *path)
pr_debug("loading %s\n", path);
- return __bpf_object__open(path);
+ return __bpf_object__open(path, NULL, 0);
+}
+
+struct bpf_object *bpf_object__open_buffer(void *obj_buf,
+ size_t obj_buf_sz)
+{
+ /* param validation */
+ if (!obj_buf || obj_buf_sz <= 0)
+ return NULL;
+
+ pr_debug("loading object from buffer\n");
+
+ return __bpf_object__open("[buffer]", obj_buf, obj_buf_sz);
}
void bpf_object__close(struct bpf_object *obj)
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index ec3301c..dc966dd 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -26,6 +26,8 @@ void libbpf_set_print(libbpf_print_fn_t warn,
struct bpf_object;
struct bpf_object *bpf_object__open(const char *path);
+struct bpf_object *bpf_object__open_buffer(void *obj_buf,
+ size_t obj_buf_sz);
void bpf_object__close(struct bpf_object *object);
#endif
--
1.8.3.4
Check endianess according to EHDR. Code is taken from
tools/perf/util/symbol-elf.c.
Libbpf doesn't magically convert missmatched endianess. Even if we swap
eBPF instructions to correct byte order, we are unable to deal with
endianess in code logical generated by LLVM.
Therefore, libbpf should simply reject missmatched ELF object, and let
LLVM to create good code.
Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
---
tools/lib/bpf/libbpf.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 8ba6ac6..472b296 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -192,6 +192,34 @@ errout:
return err;
}
+static int
+bpf_object__check_endianess(struct bpf_object *obj)
+{
+ static unsigned int const endian = 1;
+
+ switch (obj->efile.ehdr.e_ident[EI_DATA]) {
+ case ELFDATA2LSB:
+ /* We are big endian, BPF obj is little endian. */
+ if (*(unsigned char const *)&endian != 1)
+ goto mismatch;
+ break;
+
+ case ELFDATA2MSB:
+ /* We are little endian, BPF obj is big endian. */
+ if (*(unsigned char const *)&endian != 0)
+ goto mismatch;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+
+mismatch:
+ pr_warning("Error: endianess mismatch.\n");
+ return -EINVAL;
+}
+
static struct bpf_object *
__bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
{
@@ -208,6 +236,8 @@ __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
if (bpf_object__elf_init(obj))
goto out;
+ if (bpf_object__check_endianess(obj))
+ goto out;
bpf_object__elf_finish(obj);
return obj;
--
1.8.3.4
bpf_obj_elf_collect() is introduced to iterate over each elf sections
to collection informations in eBPF object files. This function will
futher enhanced to collect license, kernel version, programs, configs
and map information.
Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
---
tools/lib/bpf/libbpf.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 472b296..1c4da03 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -220,6 +220,57 @@ mismatch:
return -EINVAL;
}
+static int bpf_object__elf_collect(struct bpf_object *obj)
+{
+ Elf *elf = obj->efile.elf;
+ GElf_Ehdr *ep = &obj->efile.ehdr;
+ Elf_Scn *scn = NULL;
+ int idx = 0, err = 0;
+
+ /* Elf is corrupted/truncated, avoid calling elf_strptr. */
+ if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) {
+ pr_warning("failed to get e_shstrndx from %s\n",
+ obj->path);
+ return -EINVAL;
+ }
+
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
+ char *name;
+ GElf_Shdr sh;
+ Elf_Data *data;
+
+ idx++;
+ if (gelf_getshdr(scn, &sh) != &sh) {
+ pr_warning("failed to get section header from %s\n",
+ obj->path);
+ err = -EINVAL;
+ goto out;
+ }
+
+ name = elf_strptr(elf, ep->e_shstrndx, sh.sh_name);
+ if (!name) {
+ pr_warning("failed to get section name from %s\n",
+ obj->path);
+ err = -EINVAL;
+ goto out;
+ }
+
+ data = elf_getdata(scn, 0);
+ if (!data) {
+ pr_warning("failed to get section data from %s(%s)\n",
+ name, obj->path);
+ err = -EINVAL;
+ goto out;
+ }
+ pr_debug("section %s, size %ld, link %d, flags %lx, type=%d\n",
+ name, (unsigned long)data->d_size,
+ (int)sh.sh_link, (unsigned long)sh.sh_flags,
+ (int)sh.sh_type);
+ }
+out:
+ return err;
+}
+
static struct bpf_object *
__bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
{
@@ -238,6 +289,8 @@ __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
goto out;
if (bpf_object__check_endianess(obj))
goto out;
+ if (bpf_object__elf_collect(obj))
+ goto out;
bpf_object__elf_finish(obj);
return obj;
--
1.8.3.4
Expand bpf_obj_elf_collect() to collect license and kernel version
information in eBPF object file. eBPF object file should have a section
named 'license', which contains a string. It should also have a section
named 'version', contains a u32 LINUX_VERSION_CODE.
bpf_obj_validate() is introduced to validate object file after loaded.
Currently it only check existance of 'version' section.
Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
---
tools/lib/bpf/libbpf.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 1c4da03..3bd183c 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -14,6 +14,7 @@
#include <fcntl.h>
#include <errno.h>
#include <asm/unistd.h>
+#include <linux/kernel.h>
#include <linux/bpf.h>
#include <libelf.h>
#include <gelf.h>
@@ -78,6 +79,8 @@ void libbpf_set_print(libbpf_print_fn_t warn,
#endif
struct bpf_object {
+ char license[64];
+ u32 kern_version;
/*
* Information when doing elf related work. Only valid if fd
* is valid.
@@ -220,6 +223,32 @@ mismatch:
return -EINVAL;
}
+static int
+bpf_object__init_license(struct bpf_object *obj,
+ void *data, size_t size)
+{
+ memcpy(obj->license, data,
+ min(size, sizeof(obj->license) - 1));
+ pr_debug("license of %s is %s\n", obj->path, obj->license);
+ return 0;
+}
+
+static int
+bpf_object__init_kversion(struct bpf_object *obj,
+ void *data, size_t size)
+{
+ u32 kver;
+ if (size != sizeof(kver)) {
+ pr_warning("invalid kver section in %s\n", obj->path);
+ return -EINVAL;
+ }
+ memcpy(&kver, data, sizeof(kver));
+ obj->kern_version = kver;
+ pr_debug("kernel version of %s is %x\n", obj->path,
+ obj->kern_version);
+ return 0;
+}
+
static int bpf_object__elf_collect(struct bpf_object *obj)
{
Elf *elf = obj->efile.elf;
@@ -266,11 +295,32 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
name, (unsigned long)data->d_size,
(int)sh.sh_link, (unsigned long)sh.sh_flags,
(int)sh.sh_type);
+
+ if (strcmp(name, "license") == 0)
+ err = bpf_object__init_license(obj,
+ data->d_buf,
+ data->d_size);
+ else if (strcmp(name, "version") == 0)
+ err = bpf_object__init_kversion(obj,
+ data->d_buf,
+ data->d_size);
+ if (err)
+ goto out;
}
out:
return err;
}
+static int bpf_object__validate(struct bpf_object *obj)
+{
+ if (obj->kern_version == 0) {
+ pr_warning("%s doesn't provide kernel version\n",
+ obj->path);
+ return -EINVAL;
+ }
+ return 0;
+}
+
static struct bpf_object *
__bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
{
@@ -291,6 +341,8 @@ __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
goto out;
if (bpf_object__elf_collect(obj))
goto out;
+ if (bpf_object__validate(obj))
+ goto out;
bpf_object__elf_finish(obj);
return obj;
--
1.8.3.4
If maps are used by eBPF programs, corresponding object file(s) should
contain a section named 'map'. Which contains map definitions. This
patch copies the data of the whole section. Map data parsing should be
acted just before map loading.
Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
---
tools/lib/bpf/libbpf.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 3bd183c..3315f00 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -81,6 +81,9 @@ void libbpf_set_print(libbpf_print_fn_t warn,
struct bpf_object {
char license[64];
u32 kern_version;
+ void *maps_buf;
+ size_t maps_buf_sz;
+
/*
* Information when doing elf related work. Only valid if fd
* is valid.
@@ -249,6 +252,28 @@ bpf_object__init_kversion(struct bpf_object *obj,
return 0;
}
+static int
+bpf_object__init_maps(struct bpf_object *obj, void *data,
+ size_t size)
+{
+ if (size == 0) {
+ pr_debug("%s doesn't need map definition\n",
+ obj->path);
+ return 0;
+ }
+
+ obj->maps_buf = malloc(size);
+ if (!obj->maps_buf) {
+ pr_warning("malloc maps failed: %s\n", obj->path);
+ return -ENOMEM;
+ }
+
+ obj->maps_buf_sz = size;
+ memcpy(obj->maps_buf, data, size);
+ pr_debug("maps in %s: %ld bytes\n", obj->path, (long)size);
+ return 0;
+}
+
static int bpf_object__elf_collect(struct bpf_object *obj)
{
Elf *elf = obj->efile.elf;
@@ -304,6 +329,9 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
err = bpf_object__init_kversion(obj,
data->d_buf,
data->d_size);
+ else if (strcmp(name, "maps") == 0)
+ err = bpf_object__init_maps(obj, data->d_buf,
+ data->d_size);
if (err)
goto out;
}
@@ -381,5 +409,6 @@ void bpf_object__close(struct bpf_object *obj)
bpf_object__elf_finish(obj);
+ zfree(&obj->maps_buf);
free(obj);
}
--
1.8.3.4
This patch collects symbols section. This section is useful when
linking ELF maps.
Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
---
tools/lib/bpf/libbpf.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 3315f00..77ad7d3 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -94,6 +94,7 @@ struct bpf_object {
size_t obj_buf_sz;
Elf *elf;
GElf_Ehdr ehdr;
+ Elf_Data *symbols;
} efile;
char path[];
};
@@ -135,6 +136,7 @@ static void bpf_object__elf_finish(struct bpf_object *obj)
elf_end(obj->efile.elf);
obj->efile.elf = NULL;
}
+ obj->efile.symbols = NULL;
zclose(obj->efile.fd);
obj->efile.obj_buf = NULL;
obj->efile.obj_buf_sz = 0;
@@ -332,6 +334,14 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
else if (strcmp(name, "maps") == 0)
err = bpf_object__init_maps(obj, data->d_buf,
data->d_size);
+ else if (sh.sh_type == SHT_SYMTAB) {
+ if (obj->efile.symbols) {
+ pr_warning("bpf: multiple SYMTAB in %s\n",
+ obj->path);
+ err = -EEXIST;
+ } else
+ obj->efile.symbols = data;
+ }
if (err)
goto out;
}
--
1.8.3.4
This patch collects all programs in an object file into an array of
'struct bpf_program' for further processing. That structure is for
representing each eBPF program. 'bpf_prog' should be a better name, but
it has been used by linux/filter.h. Although it is a kernel space name,
I still prefer to call it 'bpf_program' to prevent possible confusion.
Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
---
tools/lib/bpf/libbpf.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 107 insertions(+)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 77ad7d3..91b2490 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -78,12 +78,27 @@ void libbpf_set_print(libbpf_print_fn_t warn,
# define LIBBPF_ELF_C_READ_MMAP ELF_C_READ
#endif
+/*
+ * bpf_prog should be a better name but it has been used in
+ * linux/filter.h.
+ */
+struct bpf_program {
+ /* Index in elf obj file, for relocation use. */
+ int idx;
+ char *section_name;
+ struct bpf_insn *insns;
+ size_t insns_cnt;
+};
+
struct bpf_object {
char license[64];
u32 kern_version;
void *maps_buf;
size_t maps_buf_sz;
+ struct bpf_program *programs;
+ size_t nr_programs;
+
/*
* Information when doing elf related work. Only valid if fd
* is valid.
@@ -100,6 +115,74 @@ struct bpf_object {
};
#define obj_elf_valid(o) ((o)->efile.elf)
+static void bpf_program__clear(struct bpf_program *prog)
+{
+ if (!prog)
+ return;
+
+ zfree(&prog->section_name);
+ zfree(&prog->insns);
+ prog->insns_cnt = 0;
+ prog->idx = -1;
+}
+
+static struct bpf_program *
+bpf_program__new(struct bpf_object *obj, void *data, size_t size,
+ char *name, int idx)
+{
+ struct bpf_program *prog, *progs;
+ int nr_progs;
+
+ if (size < sizeof(struct bpf_insn)) {
+ pr_warning("corrupted section '%s'\n", name);
+ return NULL;
+ }
+
+ progs = obj->programs;
+ nr_progs = obj->nr_programs;
+
+ progs = realloc(progs, sizeof(*prog) * (nr_progs + 1));
+ if (!progs) {
+ /*
+ * In this case the original obj->programs
+ * is still valid, so don't need special treat for
+ * bpf_close_object().
+ */
+ pr_warning("failed to alloc a new program '%s'\n",
+ name);
+ return NULL;
+ }
+
+ obj->programs = progs;
+
+ prog = &progs[nr_progs];
+ bzero(prog, sizeof(*prog));
+
+ obj->nr_programs = nr_progs + 1;
+
+ prog->section_name = strdup(name);
+ if (!prog->section_name) {
+ pr_warning("failed to alloc name for prog %s\n",
+ name);
+ goto out;
+ }
+
+ prog->insns = malloc(size);
+ if (!prog->insns) {
+ pr_warning("failed to alloc insns for %s\n", name);
+ goto out;
+ }
+ prog->insns_cnt = size / sizeof(struct bpf_insn);
+ memcpy(prog->insns, data,
+ prog->insns_cnt * sizeof(struct bpf_insn));
+ prog->idx = idx;
+
+ return prog;
+out:
+ bpf_program__clear(prog);
+ return NULL;
+}
+
static struct bpf_object *bpf_object__new(const char *path,
void *obj_buf,
size_t obj_buf_sz)
@@ -341,6 +424,21 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
err = -EEXIST;
} else
obj->efile.symbols = data;
+ } else if ((sh.sh_type == SHT_PROGBITS) &&
+ (sh.sh_flags & SHF_EXECINSTR) &&
+ (data->d_size > 0)) {
+ struct bpf_program *prog;
+
+ prog = bpf_program__new(obj, data->d_buf,
+ data->d_size, name,
+ idx);
+ if (!prog) {
+ pr_warning("failed to alloc program %s (%s)",
+ name, obj->path);
+ err = -ENOMEM;
+ } else
+ pr_debug("found program %s\n",
+ prog->section_name);
}
if (err)
goto out;
@@ -414,11 +512,20 @@ struct bpf_object *bpf_object__open_buffer(void *obj_buf,
void bpf_object__close(struct bpf_object *obj)
{
+ size_t i;
+
if (!obj)
return;
bpf_object__elf_finish(obj);
zfree(&obj->maps_buf);
+
+ if (obj->programs && obj->nr_programs) {
+ for (i = 0; i < obj->nr_programs; i++)
+ bpf_program__clear(&obj->programs[i]);
+ }
+ zfree(&obj->programs);
+
free(obj);
}
--
1.8.3.4
This patch collects relocation sections into 'struct object'.
Such sections are used for connecting maps to bpf programs.
'reloc' field in 'struct bpf_object' is introduced for storing
such informations.
This patch simply store the data into 'reloc' field. Following
patch will parse them to know the exact instructions which are
needed to be relocated.
Note that the collected data will be invalid after ELF object file
is closed.
Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
---
tools/lib/bpf/libbpf.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 91b2490..2b7f0f4 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -110,6 +110,11 @@ struct bpf_object {
Elf *elf;
GElf_Ehdr ehdr;
Elf_Data *symbols;
+ struct {
+ GElf_Shdr shdr;
+ Elf_Data *data;
+ } *reloc;
+ int nr_reloc;
} efile;
char path[];
};
@@ -220,6 +225,9 @@ static void bpf_object__elf_finish(struct bpf_object *obj)
obj->efile.elf = NULL;
}
obj->efile.symbols = NULL;
+
+ zfree(&obj->efile.reloc);
+ obj->efile.nr_reloc = 0;
zclose(obj->efile.fd);
obj->efile.obj_buf = NULL;
obj->efile.obj_buf_sz = 0;
@@ -439,6 +447,24 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
} else
pr_debug("found program %s\n",
prog->section_name);
+ } else if (sh.sh_type == SHT_REL) {
+ void *reloc = obj->efile.reloc;
+ int nr_reloc = obj->efile.nr_reloc;
+
+ reloc = realloc(reloc,
+ sizeof(*obj->efile.reloc) * (++nr_reloc));
+ if (!reloc) {
+ pr_warning("realloc failed\n");
+ err = -ENOMEM;
+ } else {
+ int n = nr_reloc - 1;
+
+ obj->efile.reloc = reloc;
+ obj->efile.nr_reloc = nr_reloc;
+
+ obj->efile.reloc[n].shdr = sh;
+ obj->efile.reloc[n].data = data;
+ }
}
if (err)
goto out;
--
1.8.3.4
This patch records the indics of instructions which are needed to be
relocated. Those information are saved in 'reloc_desc' field in
'struct bpf_program'. In loading phase (this patch takes effect in
opening phase), the collected instructions will be replaced by
map loading instructions.
Since we are going to close the ELF file and clear all data at the end
of 'opening' phase, ELF information will no longer be valid in
'loading' phase. We have to locate the instructions before maps are
loaded, instead of directly modifying the instruction.
'struct bpf_map_def' is introduce in this patch to let us know how many
maps defined in the object.
Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
---
tools/lib/bpf/libbpf.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 13 ++++++
2 files changed, 137 insertions(+)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 2b7f0f4..4ab0ce2 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -9,6 +9,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
+#include <inttypes.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
@@ -88,6 +89,12 @@ struct bpf_program {
char *section_name;
struct bpf_insn *insns;
size_t insns_cnt;
+
+ struct {
+ int insn_idx;
+ int map_idx;
+ } *reloc_desc;
+ int nr_reloc;
};
struct bpf_object {
@@ -127,6 +134,9 @@ static void bpf_program__clear(struct bpf_program *prog)
zfree(&prog->section_name);
zfree(&prog->insns);
+ zfree(&prog->reloc_desc);
+
+ prog->nr_reloc = 0;
prog->insns_cnt = 0;
prog->idx = -1;
}
@@ -473,6 +483,118 @@ out:
return err;
}
+static struct bpf_program *
+bpf_object__find_prog_by_idx(struct bpf_object *obj, int idx)
+{
+ struct bpf_program *prog;
+ size_t i;
+
+ for (i = 0; i < obj->nr_programs; i++) {
+ prog = &obj->programs[i];
+ if (prog->idx == idx)
+ return prog;
+ }
+ return NULL;
+}
+
+static int
+bpf_program__collect_reloc(struct bpf_program *prog,
+ size_t nr_maps, GElf_Shdr *shdr,
+ Elf_Data *data, Elf_Data *symbols)
+{
+ int i, nrels;
+
+ pr_debug("collecting relocating info for: '%s'\n",
+ prog->section_name);
+ nrels = shdr->sh_size / shdr->sh_entsize;
+
+ prog->reloc_desc = malloc(sizeof(*prog->reloc_desc) * nrels);
+ if (!prog->reloc_desc) {
+ pr_warning("failed to alloc memory in relocation\n");
+ return -ENOMEM;
+ }
+ prog->nr_reloc = nrels;
+
+ for (i = 0; i < nrels; i++) {
+ GElf_Sym sym;
+ GElf_Rel rel;
+ unsigned int insn_idx;
+ struct bpf_insn *insns = prog->insns;
+ size_t map_idx;
+
+ if (!gelf_getrel(data, i, &rel)) {
+ pr_warning("relocation: failed to get %d reloc\n", i);
+ return -EINVAL;
+ }
+
+ insn_idx = rel.r_offset / sizeof(struct bpf_insn);
+ pr_debug("relocation: insn_idx=%u\n", insn_idx);
+
+ if (!gelf_getsym(symbols,
+ GELF_R_SYM(rel.r_info),
+ &sym)) {
+ pr_warning("relocation: symbol %"PRIx64" not found\n",
+ GELF_R_SYM(rel.r_info));
+ return -EINVAL;
+ }
+
+ if (insns[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) {
+ pr_warning("bpf: relocation: invalid relo for insns[%d].code 0x%x\n",
+ insn_idx, insns[insn_idx].code);
+ return -EINVAL;
+ }
+
+ map_idx = sym.st_value / sizeof(struct bpf_map_def);
+ if (map_idx >= nr_maps) {
+ pr_warning("bpf relocation: map_idx %d large than %d\n",
+ (int)map_idx, (int)nr_maps - 1);
+ return -EINVAL;
+ }
+
+ prog->reloc_desc[i].insn_idx = insn_idx;
+ prog->reloc_desc[i].map_idx = map_idx;
+ }
+ return 0;
+}
+
+static int bpf_object__collect_reloc(struct bpf_object *obj)
+{
+ int i, err;
+
+ if (!obj_elf_valid(obj)) {
+ pr_warning("Internal error: elf object is closed\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < obj->efile.nr_reloc; i++) {
+ GElf_Shdr *shdr = &obj->efile.reloc[i].shdr;
+ Elf_Data *data = obj->efile.reloc[i].data;
+ int idx = shdr->sh_info;
+ struct bpf_program *prog;
+ size_t nr_maps = obj->maps_buf_sz /
+ sizeof(struct bpf_map_def);
+
+ if (shdr->sh_type != SHT_REL) {
+ pr_warning("internal error at %d\n", __LINE__);
+ return -EINVAL;
+ }
+
+ prog = bpf_object__find_prog_by_idx(obj, idx);
+ if (!prog) {
+ pr_warning("relocation failed: no %d section\n",
+ idx);
+ return -ENOENT;
+ }
+
+ err = bpf_program__collect_reloc(prog, nr_maps,
+ shdr, data,
+ obj->efile.symbols);
+ if (err)
+ return -EINVAL;
+ }
+ return 0;
+}
+
static int bpf_object__validate(struct bpf_object *obj)
{
if (obj->kern_version == 0) {
@@ -503,6 +625,8 @@ __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
goto out;
if (bpf_object__elf_collect(obj))
goto out;
+ if (bpf_object__collect_reloc(obj))
+ goto out;
if (bpf_object__validate(obj))
goto out;
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index dc966dd..6e75acd 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -30,4 +30,17 @@ struct bpf_object *bpf_object__open_buffer(void *obj_buf,
size_t obj_buf_sz);
void bpf_object__close(struct bpf_object *object);
+/*
+ * We don't need __attribute__((packed)) now since it is
+ * unnecessary for 'bpf_map_def' because they are all aligned.
+ * In addition, using it will trigger -Wpacked warning message,
+ * and will be treated as an error due to -Werror.
+ */
+struct bpf_map_def {
+ unsigned int type;
+ unsigned int key_size;
+ unsigned int value_size;
+ unsigned int max_entries;
+};
+
#endif
--
1.8.3.4
This patch introduces bpf.c and bpf.h, which hold common functions
issuing bpf syscall. The goal of these two files is to hide syscall
completly from user. Note that bpf.c and bpf.h only deal with kernel
interface. Things like structure of 'map' section in the ELF object is
not cared by of bpf.[ch].
We first introduce bpf_create_map().
Note that, since functions in bpf.[ch] are wrapper of sys_bpf, they
don't use OO style naming.
Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
---
tools/lib/bpf/Build | 2 +-
tools/lib/bpf/bpf.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/bpf.h | 16 ++++++++++++++++
3 files changed, 67 insertions(+), 1 deletion(-)
create mode 100644 tools/lib/bpf/bpf.c
create mode 100644 tools/lib/bpf/bpf.h
diff --git a/tools/lib/bpf/Build b/tools/lib/bpf/Build
index a316484..d874975 100644
--- a/tools/lib/bpf/Build
+++ b/tools/lib/bpf/Build
@@ -1 +1 @@
-libbpf-y := libbpf.o
+libbpf-y := libbpf.o bpf.o
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
new file mode 100644
index 0000000..e424418
--- /dev/null
+++ b/tools/lib/bpf/bpf.c
@@ -0,0 +1,50 @@
+/*
+ * common eBPF ELF operations.
+ *
+ * Copyright (C) 2013-2015 Alexei Starovoitov <[email protected]>
+ * Copyright (C) 2015 Wang Nan <[email protected]>
+ * Copyright (C) 2015 Huawei Inc.
+ */
+
+#include <stdlib.h>
+#include <memory.h>
+#include <unistd.h>
+#include <asm/unistd.h>
+#include <linux/bpf.h>
+#include "bpf.h"
+
+/*
+ * When building perf, unistd.h is override. Define __NR_bpf is
+ * required to be defined.
+ */
+#ifndef __NR_bpf
+# if defined(__i386__)
+# define __NR_bpf 357
+# elif defined(__x86_64__)
+# define __NR_bpf 321
+# elif defined(__aarch64__)
+# define __NR_bpf 280
+# else
+# error __NR_bpf not defined. libbpf does not support your arch.
+# endif
+#endif
+
+static int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
+ unsigned int size)
+{
+ return syscall(__NR_bpf, cmd, attr, size);
+}
+
+int bpf_create_map(enum bpf_map_type map_type, int key_size,
+ int value_size, int max_entries)
+{
+ union bpf_attr attr;
+ memset(&attr, '\0', sizeof(attr));
+
+ attr.map_type = map_type;
+ attr.key_size = key_size;
+ attr.value_size = value_size;
+ attr.max_entries = max_entries;
+
+ return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
+}
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
new file mode 100644
index 0000000..28f7942
--- /dev/null
+++ b/tools/lib/bpf/bpf.h
@@ -0,0 +1,16 @@
+/*
+ * common eBPF ELF operations.
+ *
+ * Copyright (C) 2013-2015 Alexei Starovoitov <[email protected]>
+ * Copyright (C) 2015 Wang Nan <[email protected]>
+ * Copyright (C) 2015 Huawei Inc.
+ */
+#ifndef __BPF_BPF_H
+#define __BPF_BPF_H
+
+#include <linux/bpf.h>
+
+int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
+ int max_entries);
+
+#endif
--
1.8.3.4
This patch creates maps based on 'map' section in object file using
bpf_create_map(), and stores the fds into an array in
'struct bpf_object'.
Previous patches parse ELF object file and collecte required data, but
doesn't play with kernel. They belong to 'opening' phase. This patch is
the first patch in 'loading' phase. 'loaded' field is introduced to
'struct bpf_object' to avoid loading one object twice, because loading
phase clears resources collected during opening which become useless
after loading. In this patch, maps_buf is cleared.
Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
---
tools/lib/bpf/libbpf.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 4 ++
2 files changed, 106 insertions(+)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 4ab0ce2..814721a 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -21,6 +21,7 @@
#include <gelf.h>
#include "libbpf.h"
+#include "bpf.h"
#define __printf(a, b) __attribute__((format(printf, a, b)))
@@ -105,6 +106,13 @@ struct bpf_object {
struct bpf_program *programs;
size_t nr_programs;
+ int *map_fds;
+ /*
+ * This field is required because maps_buf will be freed and
+ * maps_buf_sz will be set to 0 after loaded.
+ */
+ size_t nr_map_fds;
+ bool loaded;
/*
* Information when doing elf related work. Only valid if fd
@@ -222,6 +230,7 @@ static struct bpf_object *bpf_object__new(const char *path,
obj->efile.obj_buf = obj_buf;
obj->efile.obj_buf_sz = obj_buf_sz;
+ obj->loaded = false;
return obj;
}
@@ -557,6 +566,62 @@ bpf_program__collect_reloc(struct bpf_program *prog,
return 0;
}
+static int
+bpf_object__create_maps(struct bpf_object *obj)
+{
+ unsigned int i;
+ size_t nr_maps;
+ int *pfd;
+
+ nr_maps = obj->maps_buf_sz / sizeof(struct bpf_map_def);
+ if (!obj->maps_buf || !nr_maps) {
+ pr_debug("don't need create maps for %s\n",
+ obj->path);
+ return 0;
+ }
+
+ obj->map_fds = malloc(sizeof(int) * nr_maps);
+ if (!obj->map_fds) {
+ pr_warning("realloc perf_bpf_map_fds failed\n");
+ return -ENOMEM;
+ }
+ obj->nr_map_fds = nr_maps;
+
+ /* fill all fd with -1 */
+ memset(obj->map_fds, 0xff, sizeof(int) * nr_maps);
+
+ pfd = obj->map_fds;
+ for (i = 0; i < nr_maps; i++) {
+ struct bpf_map_def def;
+
+ def = *(struct bpf_map_def *)(obj->maps_buf +
+ i * sizeof(struct bpf_map_def));
+
+ *pfd = bpf_create_map(def.type,
+ def.key_size,
+ def.value_size,
+ def.max_entries);
+ if (*pfd < 0) {
+ size_t j;
+ int err = *pfd;
+
+ pr_warning("failed to create map: %s\n",
+ strerror(errno));
+ for (j = 0; j < i; j++)
+ zclose(obj->map_fds[j]);
+ obj->nr_map_fds = 0;
+ zfree(&obj->map_fds);
+ return err;
+ }
+ pr_debug("create map: fd=%d\n", *pfd);
+ pfd ++;
+ }
+
+ zfree(&obj->maps_buf);
+ obj->maps_buf_sz = 0;
+ return 0;
+}
+
static int bpf_object__collect_reloc(struct bpf_object *obj)
{
int i, err;
@@ -660,6 +725,42 @@ struct bpf_object *bpf_object__open_buffer(void *obj_buf,
return __bpf_object__open("[buffer]", obj_buf, obj_buf_sz);
}
+int bpf_object__unload(struct bpf_object *obj)
+{
+ size_t i;
+
+ if (!obj)
+ return -EINVAL;
+
+ for (i = 0; i < obj->nr_map_fds; i++)
+ zclose(obj->map_fds[i]);
+ zfree(&obj->map_fds);
+ obj->nr_map_fds = 0;
+
+ return 0;
+}
+
+int bpf_object__load(struct bpf_object *obj)
+{
+ if (!obj)
+ return -EINVAL;
+
+ if (obj->loaded) {
+ pr_warning("object should not be loaded twice\n");
+ return -EINVAL;
+ }
+
+ obj->loaded = true;
+ if (bpf_object__create_maps(obj))
+ goto out;
+
+ return 0;
+out:
+ bpf_object__unload(obj);
+ pr_warning("failed to load object '%s'\n", obj->path);
+ return -EINVAL;
+}
+
void bpf_object__close(struct bpf_object *obj)
{
size_t i;
@@ -668,6 +769,7 @@ void bpf_object__close(struct bpf_object *obj)
return;
bpf_object__elf_finish(obj);
+ bpf_object__unload(obj);
zfree(&obj->maps_buf);
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 6e75acd..3e69600 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -30,6 +30,10 @@ struct bpf_object *bpf_object__open_buffer(void *obj_buf,
size_t obj_buf_sz);
void bpf_object__close(struct bpf_object *object);
+/* Load/unload object into/from kernel */
+int bpf_object__load(struct bpf_object *obj);
+int bpf_object__unload(struct bpf_object *obj);
+
/*
* We don't need __attribute__((packed)) now since it is
* unnecessary for 'bpf_map_def' because they are all aligned.
--
1.8.3.4
If an eBPF program accesses a map, LLVM generates a load instruction
which loads an absolute address into a register, like this:
ld_64 r1, <MCOperand Expr:(mymap)>
...
call 2
That ld_64 instruction will be recorded in relocation section.
To enable the usage of that map, relocation must be done by replacing
the immediate value by real map file descriptor so it can be found by
eBPF map functions.
This patch to the relocation work based on information collected by
patch 'bpf tools: Collect relocation sections from SHT_REL sections'.
For each instruction which needs relocation, it inject corresponding
file descriptor to imm field. As a part of protocol, src_reg is set to
BPF_PSEUDO_MAP_FD to notify kernel this is a map loading instruction.
Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
---
tools/lib/bpf/libbpf.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 814721a..dbec69f 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -622,6 +622,55 @@ bpf_object__create_maps(struct bpf_object *obj)
return 0;
}
+static int
+bpf_program__relocate(struct bpf_program *prog, int *map_fds)
+{
+ int i;
+
+ if (!prog || !prog->reloc_desc)
+ return 0;
+
+ for (i = 0; i < prog->nr_reloc; i++) {
+ int insn_idx, map_idx;
+ struct bpf_insn *insns = prog->insns;
+
+ insn_idx = prog->reloc_desc[i].insn_idx;
+ map_idx = prog->reloc_desc[i].map_idx;
+
+ if (insn_idx >= (int)prog->insns_cnt) {
+ pr_warning("relocation out of range: '%s'\n",
+ prog->section_name);
+ return -ERANGE;
+ }
+ insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD;
+ insns[insn_idx].imm = map_fds[map_idx];
+ }
+
+ zfree(&prog->reloc_desc);
+ prog->nr_reloc = 0;
+ return 0;
+}
+
+
+static int
+bpf_object__relocate(struct bpf_object *obj)
+{
+ struct bpf_program *prog;
+ size_t i;
+ int err;
+
+ for (i = 0; i < obj->nr_programs; i++) {
+ prog = &obj->programs[i];
+
+ if ((err = bpf_program__relocate(prog, obj->map_fds))) {
+ pr_warning("failed to relocate '%s'\n",
+ prog->section_name);
+ return err;
+ }
+ }
+ return 0;
+}
+
static int bpf_object__collect_reloc(struct bpf_object *obj)
{
int i, err;
@@ -753,6 +802,8 @@ int bpf_object__load(struct bpf_object *obj)
obj->loaded = true;
if (bpf_object__create_maps(obj))
goto out;
+ if (bpf_object__relocate(obj))
+ goto out;
return 0;
out:
--
1.8.3.4
bpf_load_program() can be used to load bpf program into kernel. To make
loading faster, first try to load without logbuf. Try again with logbuf
if the first try failed.
Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
---
tools/lib/bpf/bpf.c | 34 ++++++++++++++++++++++++++++++++++
tools/lib/bpf/bpf.h | 7 +++++++
2 files changed, 41 insertions(+)
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index e424418..6f7f883 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -29,6 +29,11 @@
# endif
#endif
+static __u64 ptr_to_u64(void *ptr)
+{
+ return (__u64) (unsigned long) ptr;
+}
+
static int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
unsigned int size)
{
@@ -48,3 +53,32 @@ int bpf_create_map(enum bpf_map_type map_type, int key_size,
return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
}
+
+int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns,
+ size_t insns_cnt, char *license,
+ u32 kern_version, char *log_buf, size_t log_buf_sz)
+{
+ int fd;
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.prog_type = type;
+ attr.insn_cnt = (__u32)insns_cnt;
+ attr.insns = ptr_to_u64(insns);
+ attr.license = ptr_to_u64(license);
+ attr.log_buf = ptr_to_u64(NULL);
+ attr.log_size = 0;
+ attr.log_level = 0;
+ attr.kern_version = kern_version;
+
+ fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
+ if (fd >= 0 || !log_buf || !log_buf_sz)
+ return fd;
+
+ /* Try again with log */
+ attr.log_buf = ptr_to_u64(log_buf);
+ attr.log_size = log_buf_sz;
+ attr.log_level = 1;
+ log_buf[0] = 0;
+ return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
+}
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
index 28f7942..854b736 100644
--- a/tools/lib/bpf/bpf.h
+++ b/tools/lib/bpf/bpf.h
@@ -13,4 +13,11 @@
int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
int max_entries);
+/* Recommend log buffer size */
+#define BPF_LOG_BUF_SIZE 65536
+int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns,
+ size_t insns_cnt, char *license,
+ u32 kern_version, char *log_buf,
+ size_t log_buf_sz);
+
#endif
--
1.8.3.4
This patch utilizes previous introduced bpf_load_program to load
programs in the ELF file into kernel. Result is stored in 'fd' field
in 'struct bpf_program'.
During loading, it allocs a log buffer and free it before return.
Note that that buffer is not passed to bpf_load_program() if the first
loading try is successful. Doesn't use a statically allocated log
buffer to avoid potention multi-thread problem.
Instructions collected during opening is cleared after loading.
Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
---
tools/lib/bpf/libbpf.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 80 insertions(+)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index dbec69f..9b56832 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -96,6 +96,8 @@ struct bpf_program {
int map_idx;
} *reloc_desc;
int nr_reloc;
+
+ int fd;
};
struct bpf_object {
@@ -135,11 +137,20 @@ struct bpf_object {
};
#define obj_elf_valid(o) ((o)->efile.elf)
+static void bpf_program__unload(struct bpf_program *prog)
+{
+ if (!prog)
+ return;
+
+ zclose(prog->fd);
+}
+
static void bpf_program__clear(struct bpf_program *prog)
{
if (!prog)
return;
+ bpf_program__unload(prog);
zfree(&prog->section_name);
zfree(&prog->insns);
zfree(&prog->reloc_desc);
@@ -199,6 +210,7 @@ bpf_program__new(struct bpf_object *obj, void *data, size_t size,
memcpy(prog->insns, data,
prog->insns_cnt * sizeof(struct bpf_insn));
prog->idx = idx;
+ prog->fd = -1;
return prog;
out:
@@ -709,6 +721,69 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
return 0;
}
+static int
+bpf_program__load(struct bpf_program *prog,
+ char *license, u32 kern_version)
+{
+ int fd, err;
+ char *log_buf;
+
+ if (!prog->insns || !prog->insns_cnt)
+ return -EINVAL;
+
+ log_buf = malloc(BPF_LOG_BUF_SIZE);
+ if (!log_buf)
+ pr_warning("Alloc log buffer for bpf loader error, continue without log\n");
+
+ fd = bpf_load_program(BPF_PROG_TYPE_KPROBE, prog->insns,
+ prog->insns_cnt, license,
+ kern_version, log_buf,
+ BPF_LOG_BUF_SIZE);
+
+ if (fd >= 0) {
+ prog->fd = fd;
+ pr_debug("load bpf program '%s': fd = %d\n",
+ prog->section_name, prog->fd);
+ err = 0;
+ goto out;
+ }
+
+ err = -EINVAL;
+ pr_warning("load bpf program '%s' failed: %s\n",
+ prog->section_name, strerror(errno));
+
+ if (log_buf) {
+ pr_warning("bpf: load: failed to load program '%s':\n",
+ prog->section_name);
+ pr_warning("-- BEGIN DUMP LOG ---\n");
+ pr_warning("%s\n", log_buf);
+ pr_warning("-- END LOG --\n");
+ }
+
+out:
+ zfree(&prog->insns);
+ prog->insns_cnt = 0;
+ free(log_buf);
+ return err;
+}
+
+static int
+bpf_object__load_progs(struct bpf_object *obj)
+{
+ size_t i;
+ int err;
+
+ for (i = 0; i < obj->nr_programs; i++) {
+ err = bpf_program__load(&obj->programs[i],
+ obj->license,
+ obj->kern_version);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+
static int bpf_object__validate(struct bpf_object *obj)
{
if (obj->kern_version == 0) {
@@ -786,6 +861,9 @@ int bpf_object__unload(struct bpf_object *obj)
zfree(&obj->map_fds);
obj->nr_map_fds = 0;
+ for (i = 0; i < obj->nr_programs; i++)
+ bpf_program__unload(&obj->programs[i]);
+
return 0;
}
@@ -804,6 +882,8 @@ int bpf_object__load(struct bpf_object *obj)
goto out;
if (bpf_object__relocate(obj))
goto out;
+ if (bpf_object__load_progs(obj))
+ goto out;
return 0;
out:
--
1.8.3.4
This patch introduces accessors for user of libbpf to retrive section
name and fd of a opened/loaded eBPF program. 'struct bpf_prog_handler'
is used for that purpose. Accessors of programs section name and file
descriptor are provided. Set/get private data are also impelmented.
Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
---
tools/lib/bpf/libbpf.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 25 +++++++++++++++
2 files changed, 107 insertions(+)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 9b56832..acf9190 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -98,6 +98,10 @@ struct bpf_program {
int nr_reloc;
int fd;
+
+ struct bpf_object *obj;
+ void *priv;
+ bpf_program_clear_priv_t clear_priv;
};
struct bpf_object {
@@ -150,6 +154,12 @@ static void bpf_program__clear(struct bpf_program *prog)
if (!prog)
return;
+ if (prog->clear_priv)
+ prog->clear_priv(prog, prog->priv);
+
+ prog->priv = NULL;
+ prog->clear_priv = NULL;
+
bpf_program__unload(prog);
zfree(&prog->section_name);
zfree(&prog->insns);
@@ -211,6 +221,7 @@ bpf_program__new(struct bpf_object *obj, void *data, size_t size,
prog->insns_cnt * sizeof(struct bpf_insn));
prog->idx = idx;
prog->fd = -1;
+ prog->obj = obj;
return prog;
out:
@@ -912,3 +923,74 @@ void bpf_object__close(struct bpf_object *obj)
free(obj);
}
+
+struct bpf_program *
+bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
+{
+ size_t idx;
+
+ if (!obj->programs)
+ return NULL;
+ /* First handler */
+ if (prev == NULL)
+ return (&obj->programs[0]);
+
+ if (prev->obj != obj) {
+ pr_warning("error: program handler doesn't match object\n");
+ return NULL;
+ }
+
+ idx = (prev - obj->programs) + 1;
+ if (idx >= obj->nr_programs)
+ return NULL;
+ return &obj->programs[idx];
+}
+
+int bpf_program__set_private(struct bpf_program *prog,
+ void *priv,
+ bpf_program_clear_priv_t clear_priv)
+{
+ if (prog->priv && prog->clear_priv)
+ prog->clear_priv(prog, prog->priv);
+
+ prog->priv = priv;
+ prog->clear_priv = clear_priv;
+ return 0;
+}
+
+int bpf_program__get_private(struct bpf_program *prog, void **ppriv)
+{
+ *ppriv = prog->priv;
+ return 0;
+}
+
+int bpf_program__get_title(struct bpf_program *prog,
+ const char **ptitle, bool dup)
+{
+ const char *title;
+
+ if (!ptitle)
+ return -EINVAL;
+
+ title = prog->section_name;
+ if (dup) {
+ title = strdup(title);
+ if (!title) {
+ pr_warning("failed to strdup program title\n");
+ *ptitle = NULL;
+ return -ENOMEM;
+ }
+ }
+
+ *ptitle = title;
+ return 0;
+}
+
+int bpf_program__get_fd(struct bpf_program *prog, int *pfd)
+{
+ if (!pfd)
+ return -EINVAL;
+
+ *pfd = prog->fd;
+ return 0;
+}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 3e69600..3c54558 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -9,6 +9,7 @@
#define __BPF_LIBBPF_H
#include <stdio.h>
+#include <stdbool.h>
/*
* In include/linux/compiler-gcc.h, __printf is defined. However
@@ -34,6 +35,30 @@ void bpf_object__close(struct bpf_object *object);
int bpf_object__load(struct bpf_object *obj);
int bpf_object__unload(struct bpf_object *obj);
+/* Accessors of bpf_program. */
+struct bpf_program;
+struct bpf_program *bpf_program__next(struct bpf_program *prog,
+ struct bpf_object *obj);
+
+#define bpf_object__for_each_program(pos, obj) \
+ for ((pos) = bpf_program__next(NULL, (obj)); \
+ (pos) != NULL; \
+ (pos) = bpf_program__next((pos), (obj)))
+
+typedef void (*bpf_program_clear_priv_t)(struct bpf_program *,
+ void *);
+
+int bpf_program__set_private(struct bpf_program *prog, void *priv,
+ bpf_program_clear_priv_t clear_priv);
+
+int bpf_program__get_private(struct bpf_program *prog,
+ void **ppriv);
+
+int bpf_program__get_title(struct bpf_program *prog,
+ const char **ptitle, bool dup);
+
+int bpf_program__get_fd(struct bpf_program *prog, int *pfd);
+
/*
* We don't need __attribute__((packed)) now since it is
* unnecessary for 'bpf_map_def' because they are all aligned.
--
1.8.3.4
This patch add an accessor which allows caller to get count of programs
in an object file.
Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
---
tools/lib/bpf/libbpf.c | 9 +++++++++
tools/lib/bpf/libbpf.h | 3 +++
2 files changed, 12 insertions(+)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index acf9190..fec1624 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -924,6 +924,15 @@ void bpf_object__close(struct bpf_object *obj)
free(obj);
}
+int bpf_object__get_prog_cnt(struct bpf_object *obj, size_t *pcnt)
+{
+ if (!obj || !pcnt)
+ return -EINVAL;
+
+ *pcnt = obj->nr_programs;
+ return 0;
+}
+
struct bpf_program *
bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
{
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 3c54558..d11664b 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -35,6 +35,9 @@ void bpf_object__close(struct bpf_object *object);
int bpf_object__load(struct bpf_object *obj);
int bpf_object__unload(struct bpf_object *obj);
+/* Accessors of bpf_object */
+int bpf_object__get_prog_cnt(struct bpf_object *obj, size_t *pcnt);
+
/* Accessors of bpf_program. */
struct bpf_program;
struct bpf_program *bpf_program__next(struct bpf_program *prog,
--
1.8.3.4
To prevent caller from creating additional structures to hold
pointers of 'struct bpf_object', this patch link all such
structures onto a list (hidden to user). bpf_object__for_each() is
introduced to allow users iterate over each objects.
bpf_object__for_each() is safe even user close the object during
iteration.
Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
---
tools/lib/bpf/libbpf.c | 32 ++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 7 +++++++
2 files changed, 39 insertions(+)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index fec1624..5147a98 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -17,6 +17,7 @@
#include <asm/unistd.h>
#include <linux/kernel.h>
#include <linux/bpf.h>
+#include <linux/list.h>
#include <libelf.h>
#include <gelf.h>
@@ -104,6 +105,8 @@ struct bpf_program {
bpf_program_clear_priv_t clear_priv;
};
+static LIST_HEAD(bpf_objects_list);
+
struct bpf_object {
char license[64];
u32 kern_version;
@@ -137,6 +140,12 @@ struct bpf_object {
} *reloc;
int nr_reloc;
} efile;
+ /*
+ * All loaded bpf_object is linked in a list, which is
+ * hidden to caller. bpf_objects__<func> handlers deal with
+ * all objects.
+ */
+ struct list_head list;
char path[];
};
#define obj_elf_valid(o) ((o)->efile.elf)
@@ -254,6 +263,9 @@ static struct bpf_object *bpf_object__new(const char *path,
obj->efile.obj_buf_sz = obj_buf_sz;
obj->loaded = false;
+
+ INIT_LIST_HEAD(&obj->list);
+ list_add(&obj->list, &bpf_objects_list);
return obj;
}
@@ -921,6 +933,7 @@ void bpf_object__close(struct bpf_object *obj)
}
zfree(&obj->programs);
+ list_del(&obj->list);
free(obj);
}
@@ -933,6 +946,25 @@ int bpf_object__get_prog_cnt(struct bpf_object *obj, size_t *pcnt)
return 0;
}
+struct bpf_object *
+bpf_object__next(struct bpf_object *prev)
+{
+ struct bpf_object *next;
+
+ if (!prev)
+ next = list_first_entry(&bpf_objects_list,
+ struct bpf_object,
+ list);
+ else
+ next = list_next_entry(prev, list);
+
+ /* Empty list is noticed here so don't need checking on entry. */
+ if (&next->list == &bpf_objects_list)
+ return NULL;
+
+ return next;
+}
+
struct bpf_program *
bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
{
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index d11664b..61ea96d 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -38,6 +38,13 @@ int bpf_object__unload(struct bpf_object *obj);
/* Accessors of bpf_object */
int bpf_object__get_prog_cnt(struct bpf_object *obj, size_t *pcnt);
+struct bpf_object *bpf_object__next(struct bpf_object *prev);
+#define bpf_object__for_each(pos, tmp) \
+ for ((pos) = bpf_object__next(NULL), \
+ (tmp) = bpf_object__next(pos); \
+ (pos) != NULL; \
+ (pos) = (tmp), (tmp) = bpf_object__next(tmp))
+
/* Accessors of bpf_program. */
struct bpf_program;
struct bpf_program *bpf_program__next(struct bpf_program *prog,
--
1.8.3.4
By adding libbpf into perf's Makefile, this patch enables perf to
build libbpf during building if libelf is found and neither NO_LIBELF
nor NO_LIBBPF is set. The newly introduced code is similar to libapi
and libtraceevent building in Makefile.perf.
MANIFEST is also updated for 'make perf-*-src-pkg'.
Append make_no_libbpf to tools/perf/tests/make.
Error messages are also updated to notify users about the disable of
BPF support of 'perf record' if libelf is missed or BPF API check
failed.
Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/MANIFEST | 3 +++
tools/perf/Makefile.perf | 17 ++++++++++++++++-
tools/perf/config/Makefile | 19 ++++++++++++++++++-
tools/perf/tests/make | 4 +++-
4 files changed, 40 insertions(+), 3 deletions(-)
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST
index a0bdd61..1ad2016 100644
--- a/tools/perf/MANIFEST
+++ b/tools/perf/MANIFEST
@@ -17,6 +17,7 @@ tools/build
tools/arch/x86/include/asm/atomic.h
tools/arch/x86/include/asm/rmwcc.h
tools/lib/traceevent
+tools/lib/bpf
tools/lib/api
tools/lib/symbol/kallsyms.c
tools/lib/symbol/kallsyms.h
@@ -71,6 +72,8 @@ include/linux/magic.h
include/linux/hw_breakpoint.h
include/linux/rbtree_augmented.h
include/uapi/linux/perf_event.h
+include/uapi/linux/bpf.h
+include/uapi/linux/bpf_common.h
include/uapi/linux/const.h
include/uapi/linux/swab.h
include/uapi/linux/hw_breakpoint.h
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 5816a3b..8fe92d6 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -125,6 +125,7 @@ STRIP = strip
LIB_DIR = $(srctree)/tools/lib/api/
TRACE_EVENT_DIR = $(srctree)/tools/lib/traceevent/
+BPF_DIR = $(srctree)/tools/lib/bpf/
# include config/Makefile by default and rule out
# non-config cases
@@ -160,6 +161,7 @@ strip-libs = $(filter-out -l%,$(1))
ifneq ($(OUTPUT),)
TE_PATH=$(OUTPUT)
+ BPF_PATH=$(OUTPUT)
ifneq ($(subdir),)
LIB_PATH=$(OUTPUT)/../lib/api/
else
@@ -168,6 +170,7 @@ endif
else
TE_PATH=$(TRACE_EVENT_DIR)
LIB_PATH=$(LIB_DIR)
+ BPF_PATH=$(BPF_DIR)
endif
LIBTRACEEVENT = $(TE_PATH)libtraceevent.a
@@ -176,6 +179,8 @@ export LIBTRACEEVENT
LIBAPI = $(LIB_PATH)libapi.a
export LIBAPI
+LIBBPF = $(BPF_PATH)libbpf.a
+
# python extension build directories
PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/
PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/
@@ -227,6 +232,9 @@ export PERL_PATH
LIB_FILE=$(OUTPUT)libperf.a
PERFLIBS = $(LIB_FILE) $(LIBAPI) $(LIBTRACEEVENT)
+ifndef NO_LIBELF
+ PERFLIBS += $(LIBBPF)
+endif
# We choose to avoid "if .. else if .. else .. endif endif"
# because maintaining the nesting to match is a pain. If
@@ -389,6 +397,13 @@ $(LIBAPI)-clean:
$(call QUIET_CLEAN, libapi)
$(Q)$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) clean >/dev/null
+$(LIBBPF): FORCE
+ $(Q)$(MAKE) -C $(BPF_DIR) O=$(OUTPUT) $(OUTPUT)libbpf.a
+
+$(LIBBPF)-clean:
+ $(call QUIET_CLEAN, libbpf)
+ $(Q)$(MAKE) -C $(BPF_DIR) O=$(OUTPUT) clean >/dev/null
+
help:
@echo 'Perf make targets:'
@echo ' doc - make *all* documentation (see below)'
@@ -531,7 +546,7 @@ config-clean:
$(call QUIET_CLEAN, config)
$(Q)$(MAKE) -C $(srctree)/tools/build/feature/ clean >/dev/null
-clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean config-clean
+clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean config-clean
$(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS)
$(Q)find . -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
$(Q)$(RM) .config-detected
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 317001c..1b387ec 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -106,6 +106,7 @@ ifdef LIBBABELTRACE
FEATURE_CHECK_LDFLAGS-libbabeltrace := $(LIBBABELTRACE_LDFLAGS) -lbabeltrace-ctf
endif
+FEATURE_CHECK_CFLAGS-bpf = -I. -I$(srctree)/tools/include -I$(srctree)/arch/$(ARCH)/include/uapi -I$(srctree)/include/uapi
# include ARCH specific config
-include $(src-perf)/arch/$(ARCH)/Makefile
@@ -233,6 +234,7 @@ ifdef NO_LIBELF
NO_DEMANGLE := 1
NO_LIBUNWIND := 1
NO_LIBDW_DWARF_UNWIND := 1
+ NO_LIBBPF := 1
else
ifeq ($(feature-libelf), 0)
ifeq ($(feature-glibc), 1)
@@ -242,13 +244,14 @@ else
LIBC_SUPPORT := 1
endif
ifeq ($(LIBC_SUPPORT),1)
- msg := $(warning No libelf found, disables 'probe' tool, please install elfutils-libelf-devel/libelf-dev);
+ msg := $(warning No libelf found, disables 'probe' tool and BPF support in 'perf record', please install elfutils-libelf-devel/libelf-dev);
NO_LIBELF := 1
NO_DWARF := 1
NO_DEMANGLE := 1
NO_LIBUNWIND := 1
NO_LIBDW_DWARF_UNWIND := 1
+ NO_LIBBPF := 1
else
ifneq ($(filter s% -static%,$(LDFLAGS),),)
msg := $(error No static glibc found, please install glibc-static);
@@ -301,6 +304,13 @@ ifndef NO_LIBELF
$(call detected,CONFIG_DWARF)
endif # PERF_HAVE_DWARF_REGS
endif # NO_DWARF
+
+ ifndef NO_LIBBPF
+ ifeq ($(feature-bpf), 1)
+ CFLAGS += -DHAVE_LIBBPF_SUPPORT
+ $(call detected,CONFIG_LIBBPF)
+ endif
+ endif # NO_LIBBPF
endif # NO_LIBELF
ifeq ($(ARCH),powerpc)
@@ -316,6 +326,13 @@ ifndef NO_LIBUNWIND
endif
endif
+ifndef NO_LIBBPF
+ ifneq ($(feature-bpf), 1)
+ msg := $(warning BPF API too old. Please install recent kernel headers. BPF support in 'perf record' is disabled.)
+ NO_LIBBPF := 1
+ endif
+endif
+
dwarf-post-unwind := 1
dwarf-post-unwind-text := BUG
diff --git a/tools/perf/tests/make b/tools/perf/tests/make
index 65280d2..acf4634 100644
--- a/tools/perf/tests/make
+++ b/tools/perf/tests/make
@@ -33,6 +33,7 @@ make_no_libnuma := NO_LIBNUMA=1
make_no_libaudit := NO_LIBAUDIT=1
make_no_libbionic := NO_LIBBIONIC=1
make_no_auxtrace := NO_AUXTRACE=1
+make_no_libbpf := NO_LIBBPF=1
make_tags := tags
make_cscope := cscope
make_help := help
@@ -53,7 +54,7 @@ make_static := LDFLAGS=-static
make_minimal := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_NEWT=1 NO_GTK2=1
make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1
make_minimal += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1
-make_minimal += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1
+make_minimal += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1 NO_LIBBPF=1
# $(run) contains all available tests
run := make_pure
@@ -76,6 +77,7 @@ run += make_no_libnuma
run += make_no_libaudit
run += make_no_libbionic
run += make_no_auxtrace
+run += make_no_libbpf
run += make_help
run += make_doc
run += make_perf_o
--
1.8.3.4
By introducing new rules in tools/perf/util/parse-events.[ly], this
patch enables 'perf record --event bpf_file.o' to select events by
an eBPF object file. It calls parse_events_load_bpf() to load that
file, which uses bpf__prepare_load() and finally calls
bpf_object__open() for the object files.
Instead of introducing evsel to evlist during parsing, events
selected by eBPF object files are appended separately. The reason
is:
1. During parsing, the probing points have not been initialized.
2. Currently we are unable to call add_perf_probe_events() twice,
therefore we have to wait until all such events are collected,
then probe all points by one call.
The real probing and selecting is reside in following patches.
'bpf-loader.[ch]' are introduced in this patch. Which will be the
interface between perf and libbpf. bpf__prepare_load() resides in
bpf-loader.c. Dummy functions should be used because bpf-loader.c is
available only when CONFIG_LIBBPF is on.
Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/Build | 1 +
tools/perf/util/bpf-loader.c | 60 ++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/bpf-loader.h | 24 +++++++++++++++++
tools/perf/util/debug.c | 5 ++++
tools/perf/util/debug.h | 1 +
tools/perf/util/parse-events.c | 16 +++++++++++
tools/perf/util/parse-events.h | 2 ++
tools/perf/util/parse-events.l | 3 +++
tools/perf/util/parse-events.y | 18 ++++++++++++-
9 files changed, 129 insertions(+), 1 deletion(-)
create mode 100644 tools/perf/util/bpf-loader.c
create mode 100644 tools/perf/util/bpf-loader.h
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index e4b676d..1e1025f 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -77,6 +77,7 @@ libperf-y += thread-stack.o
libperf-$(CONFIG_AUXTRACE) += auxtrace.o
libperf-y += parse-branch-options.o
+libperf-$(CONFIG_LIBBPF) += bpf-loader.o
libperf-$(CONFIG_LIBELF) += symbol-elf.o
libperf-$(CONFIG_LIBELF) += probe-event.o
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
new file mode 100644
index 0000000..e950baa
--- /dev/null
+++ b/tools/perf/util/bpf-loader.c
@@ -0,0 +1,60 @@
+/*
+ * bpf-loader.c
+ *
+ * Copyright (C) 2015 Wang Nan <[email protected]>
+ * Copyright (C) 2015 Huawei Inc.
+ */
+
+#include <bpf/libbpf.h>
+#include "perf.h"
+#include "debug.h"
+#include "bpf-loader.h"
+
+#define DEFINE_PRINT_FN(name, level) \
+static int libbpf_##name(const char *fmt, ...) \
+{ \
+ va_list args; \
+ int ret; \
+ \
+ va_start(args, fmt); \
+ ret = veprintf(level, verbose, pr_fmt(fmt), args);\
+ va_end(args); \
+ return ret; \
+}
+
+DEFINE_PRINT_FN(warning, 0)
+DEFINE_PRINT_FN(info, 0)
+DEFINE_PRINT_FN(debug, 1)
+
+static bool libbpf_initialized = false;
+
+int bpf__prepare_load(const char *filename)
+{
+ struct bpf_object *obj;
+
+ if (!libbpf_initialized)
+ libbpf_set_print(libbpf_warning,
+ libbpf_info,
+ libbpf_debug);
+
+ obj = bpf_object__open(filename);
+ if (!obj) {
+ pr_err("bpf: failed to load %s\n", filename);
+ return -EINVAL;
+ }
+
+ /*
+ * Throw object pointer away: it will be retrived using
+ * bpf_objects iterater.
+ */
+
+ return 0;
+}
+
+void bpf__clear(void)
+{
+ struct bpf_object *obj, *tmp;
+
+ bpf_object__for_each(obj, tmp)
+ bpf_object__close(obj);
+}
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
new file mode 100644
index 0000000..39d8d1a
--- /dev/null
+++ b/tools/perf/util/bpf-loader.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015, Wang Nan <[email protected]>
+ * Copyright (C) 2015, Huawei Inc.
+ */
+#ifndef __BPF_LOADER_H
+#define __BPF_LOADER_H
+
+#include <linux/compiler.h>
+#include "debug.h"
+
+#ifdef HAVE_LIBBPF_SUPPORT
+int bpf__prepare_load(const char *filename);
+
+void bpf__clear(void);
+#else
+static inline int bpf__prepare_load(const char *filename __maybe_unused)
+{
+ pr_err("ERROR: eBPF object loading is disabled during compiling.\n");
+ return -1;
+}
+
+static inline void bpf__clear(void) { }
+#endif
+#endif
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index 2da5581..86d9c73 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -36,6 +36,11 @@ static int _eprintf(int level, int var, const char *fmt, va_list args)
return ret;
}
+int veprintf(int level, int var, const char *fmt, va_list args)
+{
+ return _eprintf(level, var, fmt, args);
+}
+
int eprintf(int level, int var, const char *fmt, ...)
{
va_list args;
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
index caac2fd..8b9a088 100644
--- a/tools/perf/util/debug.h
+++ b/tools/perf/util/debug.h
@@ -50,6 +50,7 @@ void pr_stat(const char *fmt, ...);
int eprintf(int level, int var, const char *fmt, ...) __attribute__((format(printf, 3, 4)));
int eprintf_time(int level, int var, u64 t, const char *fmt, ...) __attribute__((format(printf, 4, 5)));
+int veprintf(int level, int var, const char *fmt, va_list args);
int perf_debug_option(const char *str);
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 2a4d1ec..6f7a7b1 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -18,6 +18,7 @@
#include "pmu.h"
#include "thread_map.h"
#include "asm/bug.h"
+#include "bpf-loader.h"
#define MAX_NAME_LEN 100
@@ -472,6 +473,21 @@ int parse_events_add_tracepoint(struct list_head *list, int *idx,
return add_tracepoint_event(list, idx, sys, event);
}
+int parse_events_load_bpf(struct list_head *list __maybe_unused,
+ int *idx __maybe_unused,
+ char *bpf_file_name)
+{
+ /*
+ * Currently don't link any event to list. BPF object files
+ * should be saved to a seprated list and processed together.
+ *
+ * Things could be changed if we solve perf probe reentering
+ * problem. After that probe events file by file is possible.
+ * However, probing cost is still need to be considered.
+ */
+ return bpf__prepare_load(bpf_file_name);
+}
+
static int
parse_breakpoint_type(const char *type, struct perf_event_attr *attr)
{
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 131f29b..41b962a 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -114,6 +114,8 @@ int parse_events__modifier_group(struct list_head *list, char *event_mod);
int parse_events_name(struct list_head *list, char *name);
int parse_events_add_tracepoint(struct list_head *list, int *idx,
char *sys, char *event);
+int parse_events_load_bpf(struct list_head *list, int *idx,
+ char *bpf_file_name);
int parse_events_add_numeric(struct parse_events_evlist *data,
struct list_head *list,
u32 type, u64 config,
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index 09e738f..4acea38 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -115,6 +115,7 @@ do { \
group [^,{}/]*[{][^}]*[}][^,{}/]*
event_pmu [^,{}/]+[/][^/]*[/][^,{}/]*
event [^,{}/]+
+bpf_object .*\.(o|bpf)
num_dec [0-9]+
num_hex 0x[a-fA-F0-9]+
@@ -159,6 +160,7 @@ modifier_bp [rwx]{1,3}
}
{event_pmu} |
+{bpf_object} |
{event} {
BEGIN(INITIAL);
REWIND(1);
@@ -262,6 +264,7 @@ r{num_raw_hex} { return raw(yyscanner); }
{modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); }
{name} { return pmu_str_check(yyscanner); }
+{bpf_object} { return str(yyscanner, PE_BPF_OBJECT); }
"/" { BEGIN(config); return '/'; }
- { return '-'; }
, { BEGIN(event); return ','; }
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 591905a..481f3cd 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -42,6 +42,7 @@ static inc_group_count(struct list_head *list,
%token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM
%token PE_EVENT_NAME
%token PE_NAME
+%token PE_BPF_OBJECT
%token PE_MODIFIER_EVENT PE_MODIFIER_BP
%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
%token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP
@@ -53,6 +54,7 @@ static inc_group_count(struct list_head *list,
%type <num> PE_RAW
%type <num> PE_TERM
%type <str> PE_NAME
+%type <str> PE_BPF_OBJECT
%type <str> PE_NAME_CACHE_TYPE
%type <str> PE_NAME_CACHE_OP_RESULT
%type <str> PE_MODIFIER_EVENT
@@ -69,6 +71,7 @@ static inc_group_count(struct list_head *list,
%type <head> event_legacy_tracepoint
%type <head> event_legacy_numeric
%type <head> event_legacy_raw
+%type <head> event_bpf_file
%type <head> event_def
%type <head> event_mod
%type <head> event_name
@@ -198,7 +201,8 @@ event_def: event_pmu |
event_legacy_mem |
event_legacy_tracepoint sep_dc |
event_legacy_numeric sep_dc |
- event_legacy_raw sep_dc
+ event_legacy_raw sep_dc |
+ event_bpf_file
event_pmu:
PE_NAME '/' event_config '/'
@@ -420,6 +424,18 @@ PE_RAW
$$ = list;
}
+event_bpf_file:
+PE_BPF_OBJECT
+{
+ struct parse_events_evlist *data = _data;
+ struct list_head *list;
+
+ ALLOC_LIST(list);
+ ABORT_ON(parse_events_load_bpf(list, &data->idx, $1));
+ $$ = list;
+}
+
+
start_terms: event_config
{
struct parse_events_terms *data = _data;
--
1.8.3.4
This patch enables passing source files to --event directly using:
# perf record --event bpf-file.c command
This patch does following works:
1) 'struct bpf_param' is introduced to store compiler related options;
2) bpf__compile() is introduced for compiling source code by calling
clang and llc.
3) Allow passing '.c' file to '--event'. parse_events_load_bpf() is
expanded to allow caller tell it whether the passed file is source
file or object.
Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/bpf-loader.c | 177 ++++++++++++++++++++++++++++++++++++++++-
tools/perf/util/bpf-loader.h | 10 ++-
tools/perf/util/parse-events.c | 4 +-
tools/perf/util/parse-events.h | 2 +-
tools/perf/util/parse-events.l | 3 +
tools/perf/util/parse-events.y | 15 +++-
6 files changed, 203 insertions(+), 8 deletions(-)
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index e950baa..e5c03e9 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -28,7 +28,168 @@ DEFINE_PRINT_FN(debug, 1)
static bool libbpf_initialized = false;
-int bpf__prepare_load(const char *filename)
+/*
+ * Group all bpf related programs together. Futher we can config them
+ * using perf_default_config().
+ */
+struct bpf_param bpf_param = {
+ .clang_path = "clang",
+ .llc_path = "llc",
+ .clang_opt = "",
+ .llc_opt = "",
+};
+
+static int search_program(const char *def, const char *name,
+ char *output)
+{
+ char *env, *path, *tmp;
+ char buf[PATH_MAX];
+ int ret;
+
+ if (def && def[0] == '/')
+ if (access(def, F_OK) == 0) {
+ strncpy(output, def, PATH_MAX);
+ return 0;
+ }
+
+ env = getenv("PATH");
+ if (!env)
+ return -1;
+
+ env = strdup(env);
+ if (!env)
+ return -1;
+
+ ret = -1;
+ path = strtok_r(env, ":", &tmp);
+ while (path) {
+ scnprintf(buf, sizeof(buf), "%s/%s", path, name);
+ if (access(buf, F_OK) == 0) {
+ strncpy(output, buf, PATH_MAX);
+ ret = 0;
+ goto out;
+ }
+ path = strtok_r(NULL, ":", &tmp);
+ }
+
+out:
+ free(env);
+ return ret;
+}
+
+#define READ_SIZE 4096
+/*
+ * A tricky way to ensure pclose() always get failure exit status.
+ * If simple passing 'clang | llc' to popen(), and clang fails, its
+ * exit status is !0 but stdout is empty, which is valid for llc, so
+ * pclose() gets 0 as final exit status.
+ *
+ * In CMD_FMT, use 'if !clang ; then echo ERROR ; fi | llc' to ensure
+ * llc get bad input and return !0 exit status.
+ */
+#define CMD_FMT "if ! %s %s -Wno-unused-value -Wno-pointer-sign " \
+ "-emit-llvm -c \"%s\" -O2 -o - ; then echo ERROR ; fi " \
+ " | %s %s -march=bpf -filetype=obj -o %s"
+
+static int bpf__compile(const char *filename, void **p_obj_buf,
+ size_t *p_obj_buf_sz)
+{
+ char clang_path[PATH_MAX], llc_path[PATH_MAX], *cmd = NULL;
+ int err, cmd_sz;
+ FILE *file;
+ void *buf = NULL;
+ size_t buf_sz = 0, read_sz = 0;
+
+ *p_obj_buf = NULL;
+ *p_obj_buf_sz = 0;
+
+ err = search_program(bpf_param.clang_path,
+ "clang", clang_path);
+ if (!err)
+ err = search_program(bpf_param.llc_path,
+ "llc", llc_path);
+ if (err) {
+ err = -ENOENT;
+ pr_err("Error:\tunable to find clang or llc in $PATH\n");
+ pr_err("Hint:\tTry install LLVM with BPF backend.\n");
+ goto out;
+ }
+
+ cmd_sz = snprintf(NULL, 0, CMD_FMT,
+ clang_path, bpf_param.clang_opt, filename,
+ llc_path, bpf_param.llc_opt, "-");
+
+ if (cmd_sz <= 0) {
+ err = -EINVAL;
+ pr_err("Internal error: sprintf returns %d\n", cmd_sz);
+ goto out;
+ }
+
+ cmd = malloc(cmd_sz + 1);
+ if (!cmd) {
+ err = -ENOMEM;
+ pr_err("Unable to alloc compiling command\n");
+ goto out;
+ }
+
+ err = snprintf(cmd, cmd_sz + 1, CMD_FMT,
+ clang_path, bpf_param.clang_opt, filename,
+ llc_path, bpf_param.llc_opt, "-");
+ if (err != cmd_sz) {
+ err = -EINVAL;
+ pr_err("Internal error: snprintf returns %d\n", err);
+ goto out;
+ }
+ cmd[cmd_sz] = '\0';
+
+ file = popen(cmd, "r");
+ if (!file) {
+ err = -EINVAL;
+ pr_err("Error:\tfailed to run command:\n%s\nReason:\t%s\n",
+ cmd, strerror(errno));
+ goto out;
+ }
+
+ while (!feof(file) && !ferror(file)) {
+ void *new_buf;
+
+ if (buf_sz - read_sz < READ_SIZE) {
+ buf_sz += READ_SIZE;
+ new_buf = realloc(buf, buf_sz);
+ if (!new_buf) {
+ err = -ENOMEM;
+ pr_err("readlloc() failed\n");
+ goto out;
+ }
+ buf = new_buf;
+ }
+
+ read_sz += fread(buf + read_sz, 1, READ_SIZE, file);
+ }
+
+ if (ferror(file))
+ read_sz = -EINVAL;
+
+ err = WEXITSTATUS(pclose(file));
+ if (err || read_sz <= 0) {
+ err = -EINVAL;
+ pr_err("Error:\tsomething went wrong when compiling %s\n",
+ filename);
+ pr_err("Hint:\tTry manually run following command and check:\n");
+ pr_err(" # " CMD_FMT "\n\n",
+ clang_path, bpf_param.clang_opt, filename,
+ llc_path, bpf_param.llc_opt, "/dev/null");
+ goto out;
+ }
+
+ *p_obj_buf_sz = read_sz;
+ *p_obj_buf = buf;
+out:
+ free(cmd);
+ return err;
+}
+
+int bpf__prepare_load(const char *filename, bool source)
{
struct bpf_object *obj;
@@ -37,7 +198,19 @@ int bpf__prepare_load(const char *filename)
libbpf_info,
libbpf_debug);
- obj = bpf_object__open(filename);
+ if (source) {
+ void *obj_buf;
+ size_t obj_buf_sz;
+ int err;
+
+ err = bpf__compile(filename, &obj_buf, &obj_buf_sz);
+ if (err)
+ return err;
+ obj = bpf_object__open_buffer(obj_buf, obj_buf_sz);
+ free(obj_buf);
+ } else
+ obj = bpf_object__open(filename);
+
if (!obj) {
pr_err("bpf: failed to load %s\n", filename);
return -EINVAL;
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index 39d8d1a..5fd015c 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -8,8 +8,16 @@
#include <linux/compiler.h>
#include "debug.h"
+struct bpf_param {
+ const char *clang_path;
+ const char *llc_path;
+ const char *clang_opt;
+ const char *llc_opt;
+};
+extern struct bpf_param bpf_param;
+
#ifdef HAVE_LIBBPF_SUPPORT
-int bpf__prepare_load(const char *filename);
+int bpf__prepare_load(const char *filename, bool source);
void bpf__clear(void);
#else
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 6f7a7b1..aaee24c 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -475,7 +475,7 @@ int parse_events_add_tracepoint(struct list_head *list, int *idx,
int parse_events_load_bpf(struct list_head *list __maybe_unused,
int *idx __maybe_unused,
- char *bpf_file_name)
+ char *bpf_file_name, bool source)
{
/*
* Currently don't link any event to list. BPF object files
@@ -485,7 +485,7 @@ int parse_events_load_bpf(struct list_head *list __maybe_unused,
* problem. After that probe events file by file is possible.
* However, probing cost is still need to be considered.
*/
- return bpf__prepare_load(bpf_file_name);
+ return bpf__prepare_load(bpf_file_name, source);
}
static int
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 41b962a..5841d4f 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -115,7 +115,7 @@ int parse_events_name(struct list_head *list, char *name);
int parse_events_add_tracepoint(struct list_head *list, int *idx,
char *sys, char *event);
int parse_events_load_bpf(struct list_head *list, int *idx,
- char *bpf_file_name);
+ char *bpf_file_name, bool source);
int parse_events_add_numeric(struct parse_events_evlist *data,
struct list_head *list,
u32 type, u64 config,
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index 4acea38..eb821d4 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -116,6 +116,7 @@ group [^,{}/]*[{][^}]*[}][^,{}/]*
event_pmu [^,{}/]+[/][^/]*[/][^,{}/]*
event [^,{}/]+
bpf_object .*\.(o|bpf)
+bpf_source .*\.c
num_dec [0-9]+
num_hex 0x[a-fA-F0-9]+
@@ -161,6 +162,7 @@ modifier_bp [rwx]{1,3}
{event_pmu} |
{bpf_object} |
+{bpf_source} |
{event} {
BEGIN(INITIAL);
REWIND(1);
@@ -265,6 +267,7 @@ r{num_raw_hex} { return raw(yyscanner); }
{modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); }
{name} { return pmu_str_check(yyscanner); }
{bpf_object} { return str(yyscanner, PE_BPF_OBJECT); }
+{bpf_source} { return str(yyscanner, PE_BPF_SOURCE); }
"/" { BEGIN(config); return '/'; }
- { return '-'; }
, { BEGIN(event); return ','; }
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 481f3cd..eeb9768 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -42,7 +42,7 @@ static inc_group_count(struct list_head *list,
%token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM
%token PE_EVENT_NAME
%token PE_NAME
-%token PE_BPF_OBJECT
+%token PE_BPF_OBJECT PE_BPF_SOURCE
%token PE_MODIFIER_EVENT PE_MODIFIER_BP
%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
%token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP
@@ -55,6 +55,7 @@ static inc_group_count(struct list_head *list,
%type <num> PE_TERM
%type <str> PE_NAME
%type <str> PE_BPF_OBJECT
+%type <str> PE_BPF_SOURCE
%type <str> PE_NAME_CACHE_TYPE
%type <str> PE_NAME_CACHE_OP_RESULT
%type <str> PE_MODIFIER_EVENT
@@ -431,7 +432,17 @@ PE_BPF_OBJECT
struct list_head *list;
ALLOC_LIST(list);
- ABORT_ON(parse_events_load_bpf(list, &data->idx, $1));
+ ABORT_ON(parse_events_load_bpf(list, &data->idx, $1, false));
+ $$ = list;
+}
+|
+PE_BPF_SOURCE
+{
+ struct parse_events_evlist *data = _data;
+ struct list_head *list;
+
+ ALLOC_LIST(list);
+ ABORT_ON(parse_events_load_bpf(list, &data->idx, $1, true));
$$ = list;
}
--
1.8.3.4
perf_bpf_config() is added to parse 'bpf' section in perf config file.
Following is an example:
[bpf]
clang-path = /llvm/bin/x86_64-linux-clang"
llc-path = /llvm/bin/x86_64-linux-llc"
clang-opt = "-nostdinc -isystem /llvm/lib/clang/include -I/kernel/arch/x86/include ..."
llc-opt = ""
Error messages are updated to hint user about these options.
Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/bpf-loader.c | 24 +++++++++++++++++++++++-
tools/perf/util/bpf-loader.h | 1 +
tools/perf/util/config.c | 3 +++
3 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index e5c03e9..f33fd47 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -8,6 +8,7 @@
#include <bpf/libbpf.h>
#include "perf.h"
#include "debug.h"
+#include "util.h"
#include "bpf-loader.h"
#define DEFINE_PRINT_FN(name, level) \
@@ -39,6 +40,25 @@ struct bpf_param bpf_param = {
.llc_opt = "",
};
+int perf_bpf_config(const char *var, const char *value)
+{
+ if (prefixcmp(var, "bpf."))
+ return 0;
+ var += sizeof("bpf.") - 1;
+
+ if (!strcmp(var, "clang-path"))
+ bpf_param.clang_path = strdup(value);
+ else if (!strcmp(var, "llc-path"))
+ bpf_param.llc_path = strdup(value);
+ else if (!strcmp(var, "clang-opt"))
+ bpf_param.clang_opt = strdup(value);
+ else if (!strcmp(var, "llc-opt"))
+ bpf_param.llc_opt = strdup(value);
+ else
+ return -1;
+ return 0;
+}
+
static int search_program(const char *def, const char *name,
char *output)
{
@@ -111,7 +131,8 @@ static int bpf__compile(const char *filename, void **p_obj_buf,
if (err) {
err = -ENOENT;
pr_err("Error:\tunable to find clang or llc in $PATH\n");
- pr_err("Hint:\tTry install LLVM with BPF backend.\n");
+ pr_err("Hint:\tTry install LLVM with BPF backend,\n");
+ pr_err(" \tthen setup [bpf.clang-path] and [bpf.llc-path] in ~/.perfconfig\n");
goto out;
}
@@ -175,6 +196,7 @@ static int bpf__compile(const char *filename, void **p_obj_buf,
err = -EINVAL;
pr_err("Error:\tsomething went wrong when compiling %s\n",
filename);
+ pr_err("Hint:\tTry to set correct [bpf.clang-opt] and [bpf.llc-opt] in ~/.perfconfig\n");
pr_err("Hint:\tTry manually run following command and check:\n");
pr_err(" # " CMD_FMT "\n\n",
clang_path, bpf_param.clang_opt, filename,
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index 5fd015c..edc211e 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -15,6 +15,7 @@ struct bpf_param {
const char *llc_opt;
};
extern struct bpf_param bpf_param;
+extern int perf_bpf_config(const char *var, const char *value);
#ifdef HAVE_LIBBPF_SUPPORT
int bpf__prepare_load(const char *filename, bool source);
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index e18f653..def2b59 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -12,6 +12,7 @@
#include "cache.h"
#include "exec_cmd.h"
#include "util/hist.h" /* perf_hist_config */
+#include "util/bpf-loader.h" /* perf_bpf_config */
#define MAXNAME (256)
@@ -408,6 +409,8 @@ int perf_default_config(const char *var, const char *value,
if (!prefixcmp(var, "call-graph."))
return perf_callchain_config(var, value);
+ if (!prefixcmp(var, "bpf."))
+ return perf_bpf_config(var, value);
/* Add other config variables here. */
return 0;
}
--
1.8.3.4
This patch parses section name of each program, and creates
corresponding 'struct perf_probe_event' structure.
parse_perf_probe_command() is used to do the main parsing works.
Parsing result is stored into a global array. This is because
add_perf_probe_events() is non-reentrantable. In following patch,
add_perf_probe_events will be introduced to insert kprobes. It accepts
an array of 'struct perf_probe_event' and do all works in one call.
Define PERF_BPF_PROBE_GROUP as "perf_bpf_probe", which will be used
as group name of all eBPF probing points.
This patch utilizes bpf_program__set_private(), bind perf_probe_event
with bpf program by private field.
Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/bpf-loader.c | 124 ++++++++++++++++++++++++++++++++++++++++++-
tools/perf/util/bpf-loader.h | 2 +
2 files changed, 124 insertions(+), 2 deletions(-)
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index f33fd47..d786efc 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -10,6 +10,8 @@
#include "debug.h"
#include "util.h"
#include "bpf-loader.h"
+#include "probe-event.h"
+#include "probe-finder.h"
#define DEFINE_PRINT_FN(name, level) \
static int libbpf_##name(const char *fmt, ...) \
@@ -29,6 +31,9 @@ DEFINE_PRINT_FN(debug, 1)
static bool libbpf_initialized = false;
+static struct perf_probe_event probe_event_array[MAX_PROBES];
+static size_t nr_probe_events;
+
/*
* Group all bpf related programs together. Futher we can config them
* using perf_default_config().
@@ -211,9 +216,117 @@ out:
return err;
}
+static struct perf_probe_event *
+alloc_perf_probe_event(void)
+{
+ struct perf_probe_event *pev;
+ int n = nr_probe_events;
+ if (n >= MAX_PROBES) {
+ pr_err("bpf: too many events, increase MAX_PROBES\n");
+ return NULL;
+ }
+
+ nr_probe_events = n + 1;
+ pev = &probe_event_array[n];
+ bzero(pev, sizeof(*pev));
+ return pev;
+}
+
+struct bpf_prog_priv {
+ struct perf_probe_event *pev;
+};
+
+static void
+bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,
+ void *_priv)
+{
+ struct bpf_prog_priv *priv = _priv;
+ if (priv->pev)
+ clear_perf_probe_event(priv->pev);
+ free(priv);
+}
+
+static int
+config_bpf_program(struct bpf_program *prog)
+{
+ struct perf_probe_event *pev = alloc_perf_probe_event();
+ struct bpf_prog_priv *priv = NULL;
+ const char *config_str;
+ int err;
+
+ /* pr_err has been done by alloc_perf_probe_event */
+ if (!pev)
+ return -ENOMEM;
+
+ err = bpf_program__get_title(prog, &config_str, false);
+ if (err || !config_str) {
+ pr_err("bpf: unable to get title for program\n");
+ return -EINVAL;
+ }
+
+ pr_debug("bpf: config program '%s'\n", config_str);
+ err = parse_perf_probe_command(config_str, pev);
+ if (err < 0) {
+ pr_err("bpf: '%s' is not a valid config string\n",
+ config_str);
+ /* parse failed, don't need clear pev. */
+ return -EINVAL;
+ }
+
+ if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) {
+ pr_err("bpf: '%s': group for event is set and not '%s'.\n",
+ config_str, PERF_BPF_PROBE_GROUP);
+ err = -EINVAL;
+ goto errout;
+ } else if (!pev->group)
+ pev->group = strdup(PERF_BPF_PROBE_GROUP);
+
+ if (!pev->group) {
+ pr_err("bpf: strdup failed\n");
+ err = -ENOMEM;
+ goto errout;
+ }
+
+ if (!pev->event) {
+ pr_err("bpf: '%s': event name is missing\n",
+ config_str);
+ err = -EINVAL;
+ goto errout;
+ }
+
+ pr_debug("bpf: config '%s' is ok\n", config_str);
+
+ priv = calloc(1, sizeof(*priv));
+ if (!priv) {
+ pr_err("bpf: failed to alloc memory\n");
+ err = -ENOMEM;
+ goto errout;
+ }
+
+ priv->pev = pev;
+
+ err = bpf_program__set_private(prog, priv,
+ bpf_prog_priv__clear);
+ if (err) {
+ pr_err("bpf: set program private failed\n");
+ err = -ENOMEM;
+ goto errout;
+ }
+ return 0;
+
+errout:
+ if (pev)
+ clear_perf_probe_event(pev);
+ if (priv)
+ free(priv);
+ return err;
+}
+
int bpf__prepare_load(const char *filename, bool source)
{
struct bpf_object *obj;
+ struct bpf_program *prog;
+ int err = 0;
if (!libbpf_initialized)
libbpf_set_print(libbpf_warning,
@@ -223,7 +336,6 @@ int bpf__prepare_load(const char *filename, bool source)
if (source) {
void *obj_buf;
size_t obj_buf_sz;
- int err;
err = bpf__compile(filename, &obj_buf, &obj_buf_sz);
if (err)
@@ -238,12 +350,20 @@ int bpf__prepare_load(const char *filename, bool source)
return -EINVAL;
}
+ bpf_object__for_each_program(prog, obj) {
+ err = config_bpf_program(prog);
+ if (err)
+ goto errout;
+ }
+
/*
* Throw object pointer away: it will be retrived using
* bpf_objects iterater.
*/
-
return 0;
+errout:
+ bpf_object__close(obj);
+ return err;
}
void bpf__clear(void)
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index edc211e..2ed7a16 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -17,6 +17,8 @@ struct bpf_param {
extern struct bpf_param bpf_param;
extern int perf_bpf_config(const char *var, const char *value);
+#define PERF_BPF_PROBE_GROUP "perf_bpf_probe"
+
#ifdef HAVE_LIBBPF_SUPPORT
int bpf__prepare_load(const char *filename, bool source);
--
1.8.3.4
This patch drops struct __event_package structure. Instead, it adds
trace_probe_event into 'struct perf_probe_event'.
trace_probe_event information give further patches a chance to access
actual probe points and actual arguments. Using them, bpf_loader will
be able to attach one bpf program to different probing points of a
inline functions (which has multiple probing points) and glob
functions. Moreover, by reading arguments information, bpf code for
reading those arguments can be generated.
Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/builtin-probe.c | 2 +-
tools/perf/util/probe-event.c | 57 +++++++++++++++++++++----------------------
tools/perf/util/probe-event.h | 5 +++-
3 files changed, 33 insertions(+), 31 deletions(-)
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 1272559..0218725 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -497,7 +497,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
usage_with_options(probe_usage, options);
}
- ret = add_perf_probe_events(params.events, params.nevents);
+ ret = add_perf_probe_events(params.events, params.nevents, true);
if (ret < 0) {
pr_err_with_code(" Error: Failed to add events.", ret);
return ret;
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index e6f215b..b395097 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -1878,6 +1878,9 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
struct perf_probe_arg_field *field, *next;
int i;
+ if (pev->ntevs)
+ cleanup_perf_probe_event(pev);
+
free(pev->event);
free(pev->group);
free(pev->target);
@@ -2685,56 +2688,52 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
return find_probe_trace_events_from_map(pev, tevs);
}
-struct __event_package {
- struct perf_probe_event *pev;
- struct probe_trace_event *tevs;
- int ntevs;
-};
-
-int add_perf_probe_events(struct perf_probe_event *pevs, int npevs)
+int cleanup_perf_probe_event(struct perf_probe_event *pev)
{
- int i, j, ret;
- struct __event_package *pkgs;
+ int i;
- ret = 0;
- pkgs = zalloc(sizeof(struct __event_package) * npevs);
+ if (!pev || !pev->ntevs)
+ return 0;
- if (pkgs == NULL)
- return -ENOMEM;
+ for (i = 0; i < pev->ntevs; i++)
+ clear_probe_trace_event(&pev->tevs[i]);
+
+ zfree(&pev->tevs);
+ pev->ntevs = 0;
+ return 0;
+}
+
+int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, bool cleanup)
+{
+ int i, ret;
ret = init_symbol_maps(pevs->uprobes);
- if (ret < 0) {
- free(pkgs);
+ if (ret < 0)
return ret;
- }
/* Loop 1: convert all events */
for (i = 0; i < npevs; i++) {
- pkgs[i].pev = &pevs[i];
/* Convert with or without debuginfo */
- ret = convert_to_probe_trace_events(pkgs[i].pev,
- &pkgs[i].tevs);
- if (ret < 0)
+ ret = convert_to_probe_trace_events(&pevs[i], &pevs[i].tevs);
+ if (ret < 0) {
+ cleanup = true;
goto end;
- pkgs[i].ntevs = ret;
+ }
+ pevs[i].ntevs = ret;
}
/* Loop 2: add all events */
for (i = 0; i < npevs; i++) {
- ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs,
- pkgs[i].ntevs,
+ ret = __add_probe_trace_events(&pevs[i], pevs[i].tevs,
+ pevs[i].ntevs,
probe_conf.force_add);
if (ret < 0)
break;
}
end:
/* Loop 3: cleanup and free trace events */
- for (i = 0; i < npevs; i++) {
- for (j = 0; j < pkgs[i].ntevs; j++)
- clear_probe_trace_event(&pkgs[i].tevs[j]);
- zfree(&pkgs[i].tevs);
- }
- free(pkgs);
+ for (i = 0; cleanup && (i < npevs); i++)
+ cleanup_perf_probe_event(&pevs[i]);
exit_symbol_maps();
return ret;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 31db6ee..40dd22b 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -86,6 +86,8 @@ struct perf_probe_event {
bool uprobes; /* Uprobe event flag */
char *target; /* Target binary */
struct perf_probe_arg *args; /* Arguments */
+ struct probe_trace_event *tevs;
+ int ntevs;
};
/* Line range */
@@ -131,8 +133,9 @@ extern void line_range__clear(struct line_range *lr);
/* Initialize line range */
extern int line_range__init(struct line_range *lr);
-extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs);
+extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, bool cleanup);
extern int del_perf_probe_events(struct strfilter *filter);
+extern int cleanup_perf_probe_event(struct perf_probe_event *pev);
extern int show_perf_probe_events(struct strfilter *filter);
extern int show_line_range(struct line_range *lr, const char *module,
bool user);
--
1.8.3.4
In this patch, kprobe points are created using add_perf_probe_events.
Since all events are already grouped together in an array, calling
add_perf_probe_events() once creates all of them.
probe_conf.max_probes is set to MAX_PROBES to support glob matching.
Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/builtin-record.c | 14 ++++++++++++-
tools/perf/util/bpf-loader.c | 47 ++++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/bpf-loader.h | 4 ++++
3 files changed, 64 insertions(+), 1 deletion(-)
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 91aa2a3..a7c178e 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -29,6 +29,7 @@
#include "util/data.h"
#include "util/auxtrace.h"
#include "util/parse-branch-options.h"
+#include "util/bpf-loader.h"
#include <unistd.h>
#include <sched.h>
@@ -1108,7 +1109,17 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
if (err)
return err;
- err = -ENOMEM;
+ /*
+ * bpf__probe must be called before symbol__init() because we
+ * need init_symbol_maps. If called after symbol__init,
+ * symbol_conf.sort_by_name won't take effect.
+ */
+ err = bpf__probe();
+ if (err) {
+ pr_err("Probing at events in BPF object failed.\n");
+ pr_err("Try perf probe -d '*' to remove existing probe events.\n");
+ return err;
+ }
symbol__init(NULL);
@@ -1169,6 +1180,7 @@ out_symbol_exit:
perf_evlist__delete(rec->evlist);
symbol__exit();
auxtrace_record__free(rec->itr);
+ bpf__unprobe();
return err;
}
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index d786efc..8c0bf46 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -373,3 +373,50 @@ void bpf__clear(void)
bpf_object__for_each(obj, tmp)
bpf_object__close(obj);
}
+
+static bool is_probing = false;
+
+int bpf__unprobe(void)
+{
+ struct strfilter *delfilter;
+ int ret;
+
+ if (!is_probing)
+ return 0;
+
+ delfilter = strfilter__new(PERF_BPF_PROBE_GROUP ":*", NULL);
+ if (!delfilter) {
+ pr_err("Failed to create delfilter when unprobing\n");
+ return -ENOMEM;
+ }
+
+ ret = del_perf_probe_events(delfilter);
+ strfilter__delete(delfilter);
+ if (ret < 0 && is_probing)
+ pr_err("Error: failed to delete events: %s\n",
+ strerror(-ret));
+ else
+ is_probing = false;
+ return ret < 0 ? ret : 0;
+}
+
+int bpf__probe(void)
+{
+ int err;
+
+ if (nr_probe_events <= 0)
+ return 0;
+
+ probe_conf.max_probes = MAX_PROBES;
+ /* Let add_perf_probe_events keeps probe_trace_event */
+ err = add_perf_probe_events(probe_event_array,
+ nr_probe_events,
+ false);
+ /* add_perf_probe_events return negative when fail */
+ if (err < 0)
+ pr_err("bpf probe: failed to probe events\n");
+ else
+ is_probing = true;
+
+ return err < 0 ? err : 0;
+}
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index 2ed7a16..7387fb6 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -21,6 +21,8 @@ extern int perf_bpf_config(const char *var, const char *value);
#ifdef HAVE_LIBBPF_SUPPORT
int bpf__prepare_load(const char *filename, bool source);
+int bpf__probe(void);
+int bpf__unprobe(void);
void bpf__clear(void);
#else
@@ -30,6 +32,8 @@ static inline int bpf__prepare_load(const char *filename __maybe_unused)
return -1;
}
+static inline int bpf__probe(void) { return 0; }
+static inline int bpf__unprobe(void) { return 0; }
static inline void bpf__clear(void) { }
#endif
#endif
--
1.8.3.4
This patch utilizes bpf_load_object() provided by libbpf to load all
objects into kernel.
Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/builtin-record.c | 12 ++++++++++++
tools/perf/util/bpf-loader.c | 19 +++++++++++++++++++
tools/perf/util/bpf-loader.h | 2 ++
3 files changed, 33 insertions(+)
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index a7c178e..5ce9d4a 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -1121,6 +1121,18 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
return err;
}
+ /*
+ * bpf__probe() also calls symbol__init() if there are probe
+ * events in bpf objects, so calling symbol_exit when failuer
+ * is safe. If there is no probe event, bpf__load() always
+ * success.
+ */
+ err = bpf__load();
+ if (err) {
+ pr_err("Loading BPF programs failed\n");
+ goto out_symbol_exit;
+ }
+
symbol__init(NULL);
if (symbol_conf.kptr_restrict)
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 8c0bf46..45282e9 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -420,3 +420,22 @@ int bpf__probe(void)
return err < 0 ? err : 0;
}
+
+int bpf__load(void)
+{
+ struct bpf_object *obj, *tmp;
+ int err = 0;
+
+ bpf_object__for_each(obj, tmp) {
+ err = bpf_object__load(obj);
+ if (err) {
+ pr_err("bpf: load objects failed\n");
+ goto errout;
+ }
+ }
+ return 0;
+errout:
+ bpf_object__for_each(obj, tmp)
+ bpf_object__unload(obj);
+ return err;
+}
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index 7387fb6..e9c5f61 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -23,6 +23,7 @@ extern int perf_bpf_config(const char *var, const char *value);
int bpf__prepare_load(const char *filename, bool source);
int bpf__probe(void);
int bpf__unprobe(void);
+int bpf__load(void);
void bpf__clear(void);
#else
@@ -34,6 +35,7 @@ static inline int bpf__prepare_load(const char *filename __maybe_unused)
static inline int bpf__probe(void) { return 0; }
static inline int bpf__unprobe(void) { return 0; }
+static inline int bpf__load(void) { return 0; }
static inline void bpf__clear(void) { }
#endif
#endif
--
1.8.3.4
This patch adds a bpf_fd field to 'struct evsel' then introduces method
to config it. In bpf-loader, a bpf__for_each_program() function is added.
Which calls the callback function for each eBPF programs with their event
structure and file descriptors. In evlist.c, perf_evlist__add_bpf()
is added to add all bpf events into evlist. The event names are found
from probe_trace_event structure. 'perf record' calls
perf_evlist__add_bpf().
Since bpf-loader.c will not be built if libbpf is turned off, an empty
bpf__for_each_program() is defined in bpf-loader.h to avoid compiling
error.
Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/builtin-record.c | 6 ++++++
tools/perf/util/bpf-loader.c | 36 +++++++++++++++++++++++++++++++
tools/perf/util/bpf-loader.h | 13 +++++++++++
tools/perf/util/evlist.c | 51 ++++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/evlist.h | 1 +
tools/perf/util/evsel.c | 1 +
tools/perf/util/evsel.h | 1 +
7 files changed, 109 insertions(+)
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 5ce9d4a..235c3d9 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -1133,6 +1133,12 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
goto out_symbol_exit;
}
+ err = perf_evlist__add_bpf(rec->evlist);
+ if (err < 0) {
+ pr_err("Failed to add events from BPF object(s)\n");
+ goto out_symbol_exit;
+ }
+
symbol__init(NULL);
if (symbol_conf.kptr_restrict)
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 45282e9..343562b 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -439,3 +439,39 @@ errout:
bpf_object__unload(obj);
return err;
}
+
+int
+bpf__for_each_program(bpf_prog_iter_callback_t func,
+ void *arg)
+{
+ struct bpf_object *obj, *tmp;
+ struct bpf_program *prog;
+ int err;
+
+ bpf_object__for_each(obj, tmp) {
+ bpf_object__for_each_program(prog, obj) {
+ struct bpf_prog_priv *priv;
+ int fd;
+
+ err = bpf_program__get_private(prog,
+ (void **)&priv);
+ if (err || !priv) {
+ pr_err("bpf: failed to get private field\n");
+ return -EINVAL;
+ }
+ err = bpf_program__get_fd(prog, &fd);
+ if (err || fd < 0) {
+ pr_err("bpf: failed to get file descriptor\n");
+ return -EINVAL;
+ }
+
+ err = func(priv->pev, fd, arg);
+ if (err) {
+ pr_err("bpf: call back failed, stop iterate\n");
+ return err;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index e9c5f61..692f7b3 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -6,6 +6,7 @@
#define __BPF_LOADER_H
#include <linux/compiler.h>
+#include "probe-event.h"
#include "debug.h"
struct bpf_param {
@@ -19,6 +20,9 @@ extern int perf_bpf_config(const char *var, const char *value);
#define PERF_BPF_PROBE_GROUP "perf_bpf_probe"
+typedef int (*bpf_prog_iter_callback_t)(struct perf_probe_event *pev,
+ int fd, void *arg);
+
#ifdef HAVE_LIBBPF_SUPPORT
int bpf__prepare_load(const char *filename, bool source);
int bpf__probe(void);
@@ -26,6 +30,8 @@ int bpf__unprobe(void);
int bpf__load(void);
void bpf__clear(void);
+
+int bpf__for_each_program(bpf_prog_iter_callback_t func, void *arg);
#else
static inline int bpf__prepare_load(const char *filename __maybe_unused)
{
@@ -37,5 +43,12 @@ static inline int bpf__probe(void) { return 0; }
static inline int bpf__unprobe(void) { return 0; }
static inline int bpf__load(void) { return 0; }
static inline void bpf__clear(void) { }
+
+static inline int
+bpf__for_each_program(bpf_prog_iter_callback_t func __maybe_unused,
+ void *arg __maybe_unused)
+{
+ return 0;
+}
#endif
#endif
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index dc1dc2c..08863b6 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -14,6 +14,7 @@
#include "target.h"
#include "evlist.h"
#include "evsel.h"
+#include "bpf-loader.h"
#include "debug.h"
#include <unistd.h>
@@ -194,6 +195,56 @@ error:
return -ENOMEM;
}
+static int add_bpf_event(struct perf_probe_event *pev, int fd,
+ void *arg)
+{
+ struct perf_evlist *evlist = arg;
+ struct perf_evsel *pos;
+ struct list_head list;
+ int err, idx, entries, i;
+
+ if (!pev || !pev->ntevs) {
+ pr_err("Internal error: add bpf event before probing\n");
+ return -EINVAL;
+ }
+
+ pr_debug("add bpf event %s:%s and attach bpf program %d\n",
+ pev->group, pev->event, fd);
+ INIT_LIST_HEAD(&list);
+ idx = evlist->nr_entries;
+
+ for (i = 0; i < pev->ntevs; i++) {
+ struct probe_trace_event *tev = &pev->tevs[i];
+
+ pr_debug("adding %s:%s\n", tev->group, tev->event);
+ err = parse_events_add_tracepoint(&list, &idx,
+ tev->group,
+ tev->event);
+ if (err) {
+ struct perf_evsel *evsel, *tmp;
+
+ pr_err("Failed to add BPF event %s:%s\n",
+ tev->group, tev->event);
+ list_for_each_entry_safe(evsel, tmp, &list, node) {
+ list_del(&evsel->node);
+ perf_evsel__delete(evsel);
+ }
+ return -EINVAL;
+ }
+ }
+
+ list_for_each_entry(pos, &list, node)
+ pos->bpf_fd = fd;
+ entries = idx - evlist->nr_entries;
+ perf_evlist__splice_list_tail(evlist, &list, entries);
+ return 0;
+}
+
+int perf_evlist__add_bpf(struct perf_evlist *evlist)
+{
+ return bpf__for_each_program(add_bpf_event, evlist);
+}
+
static int perf_evlist__add_attrs(struct perf_evlist *evlist,
struct perf_event_attr *attrs, size_t nr_attrs)
{
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index 955bf31..773e8e9 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -71,6 +71,7 @@ void perf_evlist__delete(struct perf_evlist *evlist);
void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry);
int perf_evlist__add_default(struct perf_evlist *evlist);
+int perf_evlist__add_bpf(struct perf_evlist *evlist);
int __perf_evlist__add_default_attrs(struct perf_evlist *evlist,
struct perf_event_attr *attrs, size_t nr_attrs);
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index a3e36fc..e5cbfcd 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -205,6 +205,7 @@ void perf_evsel__init(struct perf_evsel *evsel,
evsel->leader = evsel;
evsel->unit = "";
evsel->scale = 1.0;
+ evsel->bpf_fd = -1;
INIT_LIST_HEAD(&evsel->node);
perf_evsel__object.init(evsel);
evsel->sample_size = __perf_evsel__sample_size(attr->sample_type);
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 21ec082..261de76 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -100,6 +100,7 @@ struct perf_evsel {
unsigned long *per_pkg_mask;
struct perf_evsel *leader;
char *group_name;
+ int bpf_fd;
};
union u64_swap {
--
1.8.3.4
In this patch PERF_EVENT_IOC_SET_BPF ioctl is used to attach eBPF
program to a newly created perf event. The file descriptor of the
eBPF program is passed to perf record using previous patches, and
stored into evsel->bpf_fd.
It is possible that different perf event are created for one kprobe
events for different CPUs. In this case, when trying to call the
ioctl, EEXIST will be return. This patch doesn't treat it as an error.
Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/evsel.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index e5cbfcd..4a84630 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -1202,6 +1202,22 @@ retry_open:
err);
goto try_fallback;
}
+
+ if (evsel->bpf_fd >= 0) {
+ int evt_fd = FD(evsel, cpu, thread);
+ int bpf_fd = evsel->bpf_fd;
+
+ err = ioctl(evt_fd,
+ PERF_EVENT_IOC_SET_BPF,
+ bpf_fd);
+ if (err && errno != EEXIST) {
+ pr_err("failed to attach bpf fd %d: %s\n",
+ bpf_fd, strerror(errno));
+ err = -EINVAL;
+ goto out_close;
+ }
+ }
+
set_rlimit = NO_CHANGE;
/*
--
1.8.3.4
Although previous patch allows setting BPF compiler related options in
perfconfig, on some ad-hoc situation it still requires passing options
through cmdline. This patch introduces 4 options to 'perf record' for
this propose: --clang-path, --clang-opt, --llc-path and --llc-opt.
Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/builtin-record.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 235c3d9..ad5892e 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -1070,6 +1070,14 @@ struct option __record_options[] = {
parse_clockid),
OPT_STRING_OPTARG('S', "snapshot", &record.opts.auxtrace_snapshot_opts,
"opts", "AUX area tracing Snapshot Mode", ""),
+ OPT_STRING(0, "clang-path", &bpf_param.clang_path, "clang patch",
+ "clang binary to use for compiling BPF scriptlets"),
+ OPT_STRING(0, "clang-opt", &bpf_param.clang_opt, "clang options",
+ "options passed to clang when compiling BPF scriptlets"),
+ OPT_STRING(0, "llc-path", &bpf_param.llc_path, "llc path",
+ "llc binary to use for compiling BPF scriptlets"),
+ OPT_STRING(0, "llc-opt", &bpf_param.llc_opt, "llc opt",
+ "options passed to llc when compiling BPF scriptlets"),
OPT_END()
};
--
1.8.3.4
On 2015/6/9 13:50, Wang Nan wrote:
[SNIP]
> +LIBBPF = $(BPF_PATH)libbpf.a
> +
> # python extension build directories
> PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/
> PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/
> @@ -227,6 +232,9 @@ export PERL_PATH
> LIB_FILE=$(OUTPUT)libperf.a
>
> PERFLIBS = $(LIB_FILE) $(LIBAPI) $(LIBTRACEEVENT)
> +ifndef NO_LIBELF
> + PERFLIBS += $(LIBBPF)
> +endif
>
Here should be
ifndef NO_LIBBPF
PERFLIBS += $(LIBBPF)
endif
On 2015/6/9 13:50, Wang Nan wrote:
> Although previous patch allows setting BPF compiler related options in
> perfconfig, on some ad-hoc situation it still requires passing options
> through cmdline. This patch introduces 4 options to 'perf record' for
> this propose: --clang-path, --clang-opt, --llc-path and --llc-opt.
>
> Signed-off-by: Wang Nan <[email protected]>
> ---
> tools/perf/builtin-record.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
> index 235c3d9..ad5892e 100644
> --- a/tools/perf/builtin-record.c
> +++ b/tools/perf/builtin-record.c
> @@ -1070,6 +1070,14 @@ struct option __record_options[] = {
> parse_clockid),
> OPT_STRING_OPTARG('S', "snapshot", &record.opts.auxtrace_snapshot_opts,
> "opts", "AUX area tracing Snapshot Mode", ""),
> + OPT_STRING(0, "clang-path", &bpf_param.clang_path, "clang patch",
> + "clang binary to use for compiling BPF scriptlets"),
> + OPT_STRING(0, "clang-opt", &bpf_param.clang_opt, "clang options",
> + "options passed to clang when compiling BPF scriptlets"),
> + OPT_STRING(0, "llc-path", &bpf_param.llc_path, "llc path",
> + "llc binary to use for compiling BPF scriptlets"),
> + OPT_STRING(0, "llc-opt", &bpf_param.llc_opt, "llc opt",
> + "options passed to llc when compiling BPF scriptlets"),
> OPT_END()
> };
>
These 4 options should be wrapped inside "#ifdef HAVE_LIBBPF_SUPPORT"
because
it requires bpf_param which won't be compiled if HAVE_LIBBPF_SUPPORT is set.
Another choice should be introducing something like
tools/perf/utils/llvm.c and
putting all llvm related things into it. If other part of perf also use
llvm,
making a new file to hold those options should be a better way.
Em Tue, Jun 09, 2015 at 05:50:04AM +0000, Wang Nan escreveu:
> This is the 6th version which tries to introduce eBPF programs to perf.
> It enables 'perf record' to filter events using eBPF programs like:
>
> # perf record --event bpf-file.c sleep 1
Thanks for doing this, comments below for this 00/32 file:
> and
>
> # perf record --event bpf-file.o sleep 1
>
> This patch series is based on tip/perf/core (a3d8654).
>
> Compared with v5 patch, this series allows user to pass BPF
> scriptlet source code file directly, instead of requiring user to
> pre-compile it into an ELF object. Which is done mainly by patch 24/32.
>
> In addition, to help users setup LLVM, [bpf] perf config section is
> appended. 4 command line options are also appended to 'perf record'
> for setting pathes and options of clang and llc. Which is done by patch
> 25/32 and 32/32.
>
> Other improvements:
>
> 1. Adjust context.
>
> 2. Get rid of 'trailing whitespace' and 'space before tab in indent'
> problems.
>
> 3. Error messages are improved when BPF API checking fail.
>
> In this series:
>
> Patch 1/32 adds a feature check to check version of eBPF API.
>
> Patch 2/32 - 21/32 introduce libbpf, which first parse eBPF object
> files then load maps and programs into kernel. These patches are
> already received 'Acked-by' from Alexei Starovoitov except patch
> 5/32, which enables opening a memory-based object file image using
> bpf_object__open_buffer().
>
> Patch 22/32 - 32/32 are perf related. Patch 24, 25 and 32 are new to
> support dynamical compiling.
>
> Here is an example shows the using of dynamic compiling:
>
> # perf record --event lock_page.c ls /
> Added new event:
> perf_bpf_probe:lock_page (on __lock_page)
>
> You can now use it in all perf tools, such as:
Can we suppress these messages? They are misleading, because we will not
be able to " use it in all perf tools", since they will be removed right
after this ' perf record' session ends.
They may have value as debugging aid, so that we see what probes are
being put in place, etc, but for the default case they are just annoying
scrolling.
> perf record -e perf_bpf_probe:lock_page -aR sleep 1
>
> Added new event:
> perf_bpf_probe:lock_page_ret (on __lock_page%return)
>
> You can now use it in all perf tools, such as:
>
> perf record -e perf_bpf_probe:lock_page_ret -aR sleep 1
>
> Added new event:
> perf_bpf_probe:unlock_page (on unlock_page)
>
> You can now use it in all perf tools, such as:
>
> perf record -e perf_bpf_probe:unlock_page -aR sleep 1
>
> bin boot dev etc home init lib lib64 linuxrc media mnt proc root run sbin sys tmp usr var
> [ perf record: Woken up 1 times to write data ]
> [ perf record: Captured and wrote 0.002 MB perf.data ]
> Removed event: perf_bpf_probe:lock_page
> Removed event: perf_bpf_probe:lock_page_ret
> Removed event: perf_bpf_probe:unlock_page
Ditto for this.
> Where lock_page.c can be found from:
>
> http://lkml.kernel.org/r/[email protected]
I'll try applying the patches and running this script, perhaps it would
be a good idea to have some /examples/ directory under som of the
tools/ directory, so that everything that is needed to test this is in
the source tree.
We need to at some point, best if right now, to make 'perf test' have an
entry for this, is this in this patchkit?
I.e. plain running 'perf test' will tell us if all this works fine, now
and everytime somebody with all the needed bits runs 'perf test'.
Thanks for the hard work!
- Arnaldo
> Wang Nan (32):
> tools build: Add feature check for eBPF API
> bpf tools: Introduce 'bpf' library to tools
> bpf tools: Allow caller to set printing function
> bpf tools: Open eBPF object file and do basic validation
> bpf tools: Read eBPF object from buffer
> bpf tools: Check endianess and make libbpf fail early
> bpf tools: Iterate over ELF sections to collect information
> bpf tools: Collect version and license from ELF sections
> bpf tools: Collect map definitions from 'maps' section
> bpf tools: Collect symbol table from SHT_SYMTAB section
> bpf tools: Collect eBPF programs from their own sections
> bpf tools: Collect relocation sections from SHT_REL sections
> bpf tools: Record map accessing instructions for each program
> bpf tools: Add bpf.c/h for common bpf operations
> bpf tools: Create eBPF maps defined in an object file
> bpf tools: Relocate eBPF programs
> bpf tools: Introduce bpf_load_program() to bpf.c
> bpf tools: Load eBPF programs in object files into kernel
> bpf tools: Introduce accessors for struct bpf_program
> bpf tools: Introduce accessors for struct bpf_object
> bpf tools: Link all bpf objects onto a list
> perf tools: Make perf depend on libbpf
> perf record: Enable passing bpf object file to --event
> perf record: Compile scriptlets if pass '.c' to --event
> perf tools: Add 'bpf.' config section to perf default config
> perf tools: Parse probe points of eBPF programs during preparation
> perf probe: Attach trace_probe_event with perf_probe_event
> perf record: Probe at kprobe points
> perf record: Load all eBPF object into kernel
> perf tools: Add bpf_fd field to evsel and config it
> perf tools: Attach eBPF program to perf event
> perf record: Add LLVM options for compiling BPF scripts
>
> tools/build/Makefile.feature | 6 +-
> tools/build/feature/Makefile | 6 +-
> tools/build/feature/test-bpf.c | 18 +
> tools/lib/bpf/.gitignore | 2 +
> tools/lib/bpf/Build | 1 +
> tools/lib/bpf/Makefile | 195 ++++++++
> tools/lib/bpf/bpf.c | 84 ++++
> tools/lib/bpf/bpf.h | 23 +
> tools/lib/bpf/libbpf.c | 1037 ++++++++++++++++++++++++++++++++++++++++
> tools/lib/bpf/libbpf.h | 85 ++++
> tools/perf/MANIFEST | 3 +
> tools/perf/Makefile.perf | 17 +-
> tools/perf/builtin-probe.c | 2 +-
> tools/perf/builtin-record.c | 40 +-
> tools/perf/config/Makefile | 19 +-
> tools/perf/tests/make | 4 +-
> tools/perf/util/Build | 1 +
> tools/perf/util/bpf-loader.c | 477 ++++++++++++++++++
> tools/perf/util/bpf-loader.h | 54 +++
> tools/perf/util/config.c | 3 +
> tools/perf/util/debug.c | 5 +
> tools/perf/util/debug.h | 1 +
> tools/perf/util/evlist.c | 51 ++
> tools/perf/util/evlist.h | 1 +
> tools/perf/util/evsel.c | 17 +
> tools/perf/util/evsel.h | 1 +
> tools/perf/util/parse-events.c | 16 +
> tools/perf/util/parse-events.h | 2 +
> tools/perf/util/parse-events.l | 6 +
> tools/perf/util/parse-events.y | 29 +-
> tools/perf/util/probe-event.c | 57 ++-
> tools/perf/util/probe-event.h | 5 +-
> 32 files changed, 2229 insertions(+), 39 deletions(-)
> create mode 100644 tools/build/feature/test-bpf.c
> create mode 100644 tools/lib/bpf/.gitignore
> create mode 100644 tools/lib/bpf/Build
> create mode 100644 tools/lib/bpf/Makefile
> create mode 100644 tools/lib/bpf/bpf.c
> create mode 100644 tools/lib/bpf/bpf.h
> create mode 100644 tools/lib/bpf/libbpf.c
> create mode 100644 tools/lib/bpf/libbpf.h
> create mode 100644 tools/perf/util/bpf-loader.c
> create mode 100644 tools/perf/util/bpf-loader.h
>
> --
> 1.8.3.4
On 6/8/15 10:50 PM, Wang Nan wrote:
> # perf record --event lock_page.c ls /
> Added new event:
> perf_bpf_probe:lock_page (on __lock_page)
>
> You can now use it in all perf tools, such as:
agree with Arnaldo. The output is misleading.
All these events will disappear when 'perf record' is over.
Please trim it.
btw we've been thinking how to make truly global programs
and maps, so that they can be used in 'perf probe' interface.
Right now in 'tc' we're using bpf_agent. It's a user space
demon that keeps prog_fd and map_fds and passes them to other
apps via scm_rights.
We've started experimenting with fuse for this purpose as well,
but that's similar user space demon and not very convenient
for perf to talk to such fuse.
The work in that area is still very rough, just fyi.
Em Tue, Jun 09, 2015 at 02:30:36PM -0700, Alexei Starovoitov escreveu:
> On 6/8/15 10:50 PM, Wang Nan wrote:
> > # perf record --event lock_page.c ls /
> > Added new event:
> > perf_bpf_probe:lock_page (on __lock_page)
> >
> > You can now use it in all perf tools, such as:
> agree with Arnaldo. The output is misleading.
> All these events will disappear when 'perf record' is over.
> Please trim it.
> btw we've been thinking how to make truly global programs
> and maps, so that they can be used in 'perf probe' interface.
> Right now in 'tc' we're using bpf_agent. It's a user space
> demon that keeps prog_fd and map_fds and passes them to other
> apps via scm_rights.
> We've started experimenting with fuse for this purpose as well,
> but that's similar user space demon and not very convenient
> for perf to talk to such fuse.
> The work in that area is still very rough, just fyi.
Cool stuff, but please leave us without daemons, we've exorcised from
day one ;-)
- Arnaldo
On 6/8/15 10:50 PM, Wang Nan wrote:
> +struct bpf_param bpf_param = {
> + .clang_path = "clang",
> + .llc_path = "llc",
> + .clang_opt = "",
> + .llc_opt = "",
> +};
the defaults are ok-ish, but llc is never in PATH.
So most likely it won't work out of the box.
I think the cleanest option is too wait a little bit
until we finish clang integration.
(hopefully few more days)
we've quite been busy on llvm side
and I'm excited to announce that BPF backend finally
graduated from experimental.
So now anyone can just pull the latest llvm and
bpf will be present by default.
No more cmake -DLLVM_EXPERIMENTAL...
Once clang integration is complete. One can pull
upsteam llvm and clang and just use 'clang -O2 -c -march=bpf file.c'
> + err = WEXITSTATUS(pclose(file));
> + if (err || read_sz <= 0) {
> + err = -EINVAL;
> + pr_err("Error:\tsomething went wrong when compiling %s\n",
> + filename);
> + pr_err("Hint:\tTry manually run following command and check:\n");
> + pr_err(" # " CMD_FMT "\n\n",
> + clang_path, bpf_param.clang_opt, filename,
> + llc_path, bpf_param.llc_opt, "/dev/null");
not terribly informative hint.
I think would be good to point to some readme or at least mention
that only the latest clang/llvm support bpf.
Either svn/git trunk or upcoming 3.7
May be a link to llvm.org?
On 6/9/15 2:44 PM, Arnaldo Carvalho de Melo wrote:
>
>> btw we've been thinking how to make truly global programs
>> and maps, so that they can be used in 'perf probe' interface.
>> Right now in 'tc' we're using bpf_agent. It's a user space
>> demon that keeps prog_fd and map_fds and passes them to other
>> apps via scm_rights.
>> We've started experimenting with fuse for this purpose as well,
>> but that's similar user space demon and not very convenient
>> for perf to talk to such fuse.
>> The work in that area is still very rough, just fyi.
>
> Cool stuff, but please leave us without daemons, we've exorcised from
> day one ;-)
agreed. I've been looking at hacking debugfs, tracefs, sysfs, but
none of them fit well, since such infra needs to be generic and support
both tracing and networking needs. Looking at procfs at the moment.
Ideally I would like programs/maps to 'attach to a file', so that
they don't disappear when user app exits. Then opening such file
in the future would get user app a new FD that can be used to access
this global map or program. When the file is removed, the prog/map
would be unloaded.
On 6/8/15 10:50 PM, Wang Nan wrote:
> perf_bpf_config() is added to parse 'bpf' section in perf config file.
> Following is an example:
>
> [bpf]
> clang-path = /llvm/bin/x86_64-linux-clang"
> llc-path = /llvm/bin/x86_64-linux-llc"
> clang-opt = "-nostdinc -isystem /llvm/lib/clang/include -I/kernel/arch/x86/include ..."
> llc-opt = ""
a section to specify -I flags to compile prog.c is useful,
but users shouldn't be populating it manually for kernel headers.
How about adding a script that can figure out $(LINUXINCLUDE)
automatically ?
You can even invoke such flag detector from perf via something like:
f = open /tmpdir/Makefile
fprintf(f, "obj-y := dummy.o\n");
fprintf(f, "$(obj)/%%.o: $(src)/%%.c\n");
fprintf(f, "\t@echo -n \"$(NOSTDINC_FLAGS) $(LINUXINCLUDE)
$(EXTRA_CFLAGS)\" > %s\n", cachefile);
snprintf(cmd, "make -s -C /lib/modules/%s/build M=%s dummy.o",
uts_release, tmpdir);
system(cmd);
read flags from cachefile and cache it for the future.
...
or as independent script that populates .perfconfig
On 2015/6/9 21:59, Arnaldo Carvalho de Melo wrote:
> Em Tue, Jun 09, 2015 at 05:50:04AM +0000, Wang Nan escreveu:
>> This is the 6th version which tries to introduce eBPF programs to perf.
>> It enables 'perf record' to filter events using eBPF programs like:
>>
>> # perf record --event bpf-file.c sleep 1
> Thanks for doing this, comments below for this 00/32 file:
>
>> and
>>
>> # perf record --event bpf-file.o sleep 1
>>
>> This patch series is based on tip/perf/core (a3d8654).
>>
>> Compared with v5 patch, this series allows user to pass BPF
>> scriptlet source code file directly, instead of requiring user to
>> pre-compile it into an ELF object. Which is done mainly by patch 24/32.
>>
>> In addition, to help users setup LLVM, [bpf] perf config section is
>> appended. 4 command line options are also appended to 'perf record'
>> for setting pathes and options of clang and llc. Which is done by patch
>> 25/32 and 32/32.
>>
>> Other improvements:
>>
>> 1. Adjust context.
>>
>> 2. Get rid of 'trailing whitespace' and 'space before tab in indent'
>> problems.
>>
>> 3. Error messages are improved when BPF API checking fail.
>>
>> In this series:
>>
>> Patch 1/32 adds a feature check to check version of eBPF API.
>>
>> Patch 2/32 - 21/32 introduce libbpf, which first parse eBPF object
>> files then load maps and programs into kernel. These patches are
>> already received 'Acked-by' from Alexei Starovoitov except patch
>> 5/32, which enables opening a memory-based object file image using
>> bpf_object__open_buffer().
>>
>> Patch 22/32 - 32/32 are perf related. Patch 24, 25 and 32 are new to
>> support dynamical compiling.
>>
>> Here is an example shows the using of dynamic compiling:
>>
>> # perf record --event lock_page.c ls /
>> Added new event:
>> perf_bpf_probe:lock_page (on __lock_page)
>>
>> You can now use it in all perf tools, such as:
> Can we suppress these messages? They are misleading, because we will not
> be able to " use it in all perf tools", since they will be removed right
> after this ' perf record' session ends.
>
> They may have value as debugging aid, so that we see what probes are
> being put in place, etc, but for the default case they are just annoying
> scrolling.
Sure. I don't want to alert API of add_perf_probe_events() and
__add_probe_trace_events(),
so in next version I'd like to introduce a new field in probe_conf to
control these output.
>> perf record -e perf_bpf_probe:lock_page -aR sleep 1
>>
>> Added new event:
>> perf_bpf_probe:lock_page_ret (on __lock_page%return)
>>
>> You can now use it in all perf tools, such as:
>>
>> perf record -e perf_bpf_probe:lock_page_ret -aR sleep 1
>>
>> Added new event:
>> perf_bpf_probe:unlock_page (on unlock_page)
>>
>> You can now use it in all perf tools, such as:
>>
>> perf record -e perf_bpf_probe:unlock_page -aR sleep 1
>>
>> bin boot dev etc home init lib lib64 linuxrc media mnt proc root run sbin sys tmp usr var
>> [ perf record: Woken up 1 times to write data ]
>> [ perf record: Captured and wrote 0.002 MB perf.data ]
>> Removed event: perf_bpf_probe:lock_page
>> Removed event: perf_bpf_probe:lock_page_ret
>> Removed event: perf_bpf_probe:unlock_page
> Ditto for this.
>
>> Where lock_page.c can be found from:
>>
>> http://lkml.kernel.org/r/[email protected]
> I'll try applying the patches and running this script, perhaps it would
> be a good idea to have some /examples/ directory under som of the
> tools/ directory, so that everything that is needed to test this is in
> the source tree.
>
> We need to at some point, best if right now, to make 'perf test' have an
> entry for this, is this in this patchkit?
Will append. Tow parts: 1. testing llvm compiling BPF source code; 2.
loading it into kernel.
> I.e. plain running 'perf test' will tell us if all this works fine, now
> and everytime somebody with all the needed bits runs 'perf test'.
>
> Thanks for the hard work!
>
> - Arnaldo
>
>> Wang Nan (32):
>> tools build: Add feature check for eBPF API
>> bpf tools: Introduce 'bpf' library to tools
>> bpf tools: Allow caller to set printing function
>> bpf tools: Open eBPF object file and do basic validation
>> bpf tools: Read eBPF object from buffer
>> bpf tools: Check endianess and make libbpf fail early
>> bpf tools: Iterate over ELF sections to collect information
>> bpf tools: Collect version and license from ELF sections
>> bpf tools: Collect map definitions from 'maps' section
>> bpf tools: Collect symbol table from SHT_SYMTAB section
>> bpf tools: Collect eBPF programs from their own sections
>> bpf tools: Collect relocation sections from SHT_REL sections
>> bpf tools: Record map accessing instructions for each program
>> bpf tools: Add bpf.c/h for common bpf operations
>> bpf tools: Create eBPF maps defined in an object file
>> bpf tools: Relocate eBPF programs
>> bpf tools: Introduce bpf_load_program() to bpf.c
>> bpf tools: Load eBPF programs in object files into kernel
>> bpf tools: Introduce accessors for struct bpf_program
>> bpf tools: Introduce accessors for struct bpf_object
>> bpf tools: Link all bpf objects onto a list
>> perf tools: Make perf depend on libbpf
>> perf record: Enable passing bpf object file to --event
>> perf record: Compile scriptlets if pass '.c' to --event
>> perf tools: Add 'bpf.' config section to perf default config
>> perf tools: Parse probe points of eBPF programs during preparation
>> perf probe: Attach trace_probe_event with perf_probe_event
>> perf record: Probe at kprobe points
>> perf record: Load all eBPF object into kernel
>> perf tools: Add bpf_fd field to evsel and config it
>> perf tools: Attach eBPF program to perf event
>> perf record: Add LLVM options for compiling BPF scripts
>>
>> tools/build/Makefile.feature | 6 +-
>> tools/build/feature/Makefile | 6 +-
>> tools/build/feature/test-bpf.c | 18 +
>> tools/lib/bpf/.gitignore | 2 +
>> tools/lib/bpf/Build | 1 +
>> tools/lib/bpf/Makefile | 195 ++++++++
>> tools/lib/bpf/bpf.c | 84 ++++
>> tools/lib/bpf/bpf.h | 23 +
>> tools/lib/bpf/libbpf.c | 1037 ++++++++++++++++++++++++++++++++++++++++
>> tools/lib/bpf/libbpf.h | 85 ++++
>> tools/perf/MANIFEST | 3 +
>> tools/perf/Makefile.perf | 17 +-
>> tools/perf/builtin-probe.c | 2 +-
>> tools/perf/builtin-record.c | 40 +-
>> tools/perf/config/Makefile | 19 +-
>> tools/perf/tests/make | 4 +-
>> tools/perf/util/Build | 1 +
>> tools/perf/util/bpf-loader.c | 477 ++++++++++++++++++
>> tools/perf/util/bpf-loader.h | 54 +++
>> tools/perf/util/config.c | 3 +
>> tools/perf/util/debug.c | 5 +
>> tools/perf/util/debug.h | 1 +
>> tools/perf/util/evlist.c | 51 ++
>> tools/perf/util/evlist.h | 1 +
>> tools/perf/util/evsel.c | 17 +
>> tools/perf/util/evsel.h | 1 +
>> tools/perf/util/parse-events.c | 16 +
>> tools/perf/util/parse-events.h | 2 +
>> tools/perf/util/parse-events.l | 6 +
>> tools/perf/util/parse-events.y | 29 +-
>> tools/perf/util/probe-event.c | 57 ++-
>> tools/perf/util/probe-event.h | 5 +-
>> 32 files changed, 2229 insertions(+), 39 deletions(-)
>> create mode 100644 tools/build/feature/test-bpf.c
>> create mode 100644 tools/lib/bpf/.gitignore
>> create mode 100644 tools/lib/bpf/Build
>> create mode 100644 tools/lib/bpf/Makefile
>> create mode 100644 tools/lib/bpf/bpf.c
>> create mode 100644 tools/lib/bpf/bpf.h
>> create mode 100644 tools/lib/bpf/libbpf.c
>> create mode 100644 tools/lib/bpf/libbpf.h
>> create mode 100644 tools/perf/util/bpf-loader.c
>> create mode 100644 tools/perf/util/bpf-loader.h
>>
>> --
>> 1.8.3.4
On 6/8/15 10:50 PM, Wang Nan wrote:
> Although previous patch allows setting BPF compiler related options in
> perfconfig, on some ad-hoc situation it still requires passing options
> through cmdline. This patch introduces 4 options to 'perf record' for
> this propose: --clang-path, --clang-opt, --llc-path and --llc-opt.
looks good. I hope only --clang will be used from time to time.
Today, indeed, --llc* are needed too.
btw, our experimental libbpfprog.so (which is clang and llvm libs
compiled into single .so with C api for compiling .c into bpf):
$ ls -lh libbpfprog.so
-rw-r--r-- 1 ast ast 31M Jun 9 16:49 libbpfprog.so
not tiny, but I think we should be able to include it into perf,
then perf will only have build dependencies on llvm instead of
run-time dependencies.
We'll release this lib as soon as it's a bit more stable.
On 2015/6/10 5:48, Alexei Starovoitov wrote:
> On 6/8/15 10:50 PM, Wang Nan wrote:
>> +struct bpf_param bpf_param = {
>> + .clang_path = "clang",
>> + .llc_path = "llc",
>> + .clang_opt = "",
>> + .llc_opt = "",
>> +};
>
> the defaults are ok-ish, but llc is never in PATH.
> So most likely it won't work out of the box.
> I think the cleanest option is too wait a little bit
> until we finish clang integration.
> (hopefully few more days)
>
> we've quite been busy on llvm side
> and I'm excited to announce that BPF backend finally
> graduated from experimental.
> So now anyone can just pull the latest llvm and
> bpf will be present by default.
> No more cmake -DLLVM_EXPERIMENTAL...
>
> Once clang integration is complete. One can pull
> upsteam llvm and clang and just use 'clang -O2 -c -march=bpf file.c'
>
Good news, so llc part should be removed.
Next version I'd like to use following config options:
[llvm]
clang-bpf-cmd-template = "$CLANG_EXEC $CLANG_OPTIONS -c
$CLANG_SOURCE -emit-llvm -O2 -o - | /path/to/llc -march=bpf
-filetype=obj -o -"
clang-path = "/path/to/clang"
clang-opt = ""
And the default template should be:
$CLANG_EXEC $CLANG_OPTIONS -c "$CLANG_SOURCE" -emit-llvm -O2 -o -
Then with environment variable tricks we make it work with popen.
By this way we can get rid of llc in perf side, and make it work even
before clang
integration is complete.
Thank you.
>> + err = WEXITSTATUS(pclose(file));
>> + if (err || read_sz <= 0) {
>> + err = -EINVAL;
>> + pr_err("Error:\tsomething went wrong when compiling %s\n",
>> + filename);
>> + pr_err("Hint:\tTry manually run following command and
>> check:\n");
>> + pr_err(" # " CMD_FMT "\n\n",
>> + clang_path, bpf_param.clang_opt, filename,
>> + llc_path, bpf_param.llc_opt, "/dev/null");
>
> not terribly informative hint.
> I think would be good to point to some readme or at least mention
> that only the latest clang/llvm support bpf.
> Either svn/git trunk or upcoming 3.7
> May be a link to llvm.org?
>
On 2015/6/10 8:02, Alexei Starovoitov wrote:
> On 6/8/15 10:50 PM, Wang Nan wrote:
>> Although previous patch allows setting BPF compiler related options in
>> perfconfig, on some ad-hoc situation it still requires passing options
>> through cmdline. This patch introduces 4 options to 'perf record' for
>> this propose: --clang-path, --clang-opt, --llc-path and --llc-opt.
>
> looks good. I hope only --clang will be used from time to time.
Right. Please see another mail I posted. I'd like to generate command using
command template instead of printf() in next version.
> Today, indeed, --llc* are needed too.
>
With template, --llc can be removed now.
> btw, our experimental libbpfprog.so (which is clang and llvm libs
> compiled into single .so with C api for compiling .c into bpf):
> $ ls -lh libbpfprog.so
> -rw-r--r-- 1 ast ast 31M Jun 9 16:49 libbpfprog.so
>
> not tiny, but I think we should be able to include it into perf,
> then perf will only have build dependencies on llvm instead of
> run-time dependencies.
> We'll release this lib as soon as it's a bit more stable.
>
Could you please give me some URL to LLVM git repositories so I can
track your work on it?
Thank you.
On 6/9/15 5:06 PM, Wangnan (F) wrote:
>
>
> On 2015/6/10 5:48, Alexei Starovoitov wrote:
>> On 6/8/15 10:50 PM, Wang Nan wrote:
>>> +struct bpf_param bpf_param = {
>>> + .clang_path = "clang",
>>> + .llc_path = "llc",
>>> + .clang_opt = "",
>>> + .llc_opt = "",
>>> +};
>>
>> the defaults are ok-ish, but llc is never in PATH.
>> So most likely it won't work out of the box.
>> I think the cleanest option is too wait a little bit
>> until we finish clang integration.
>> (hopefully few more days)
>>
>> we've quite been busy on llvm side
>> and I'm excited to announce that BPF backend finally
>> graduated from experimental.
>> So now anyone can just pull the latest llvm and
>> bpf will be present by default.
>> No more cmake -DLLVM_EXPERIMENTAL...
>>
>> Once clang integration is complete. One can pull
>> upsteam llvm and clang and just use 'clang -O2 -c -march=bpf file.c'
>>
>
> Good news, so llc part should be removed.
>
> Next version I'd like to use following config options:
>
> [llvm]
> clang-bpf-cmd-template = "$CLANG_EXEC $CLANG_OPTIONS -c
> $CLANG_SOURCE -emit-llvm -O2 -o - | /path/to/llc -march=bpf
> -filetype=obj -o -"
> clang-path = "/path/to/clang"
> clang-opt = ""
yes. that's better.
On 2015/6/10 7:43, Alexei Starovoitov wrote:
> On 6/8/15 10:50 PM, Wang Nan wrote:
>> perf_bpf_config() is added to parse 'bpf' section in perf config file.
>> Following is an example:
>>
>> [bpf]
>> clang-path = /llvm/bin/x86_64-linux-clang"
>> llc-path = /llvm/bin/x86_64-linux-llc"
>> clang-opt = "-nostdinc -isystem /llvm/lib/clang/include
>> -I/kernel/arch/x86/include ..."
>> llc-opt = ""
>
> a section to specify -I flags to compile prog.c is useful,
> but users shouldn't be populating it manually for kernel headers.
> How about adding a script that can figure out $(LINUXINCLUDE)
> automatically ?
> You can even invoke such flag detector from perf via something like:
> f = open /tmpdir/Makefile
> fprintf(f, "obj-y := dummy.o\n");
> fprintf(f, "$(obj)/%%.o: $(src)/%%.c\n");
> fprintf(f, "\t@echo -n \"$(NOSTDINC_FLAGS) $(LINUXINCLUDE)
> $(EXTRA_CFLAGS)\" > %s\n", cachefile);
> snprintf(cmd, "make -s -C /lib/modules/%s/build M=%s dummy.o",
> uts_release, tmpdir);
> system(cmd);
> read flags from cachefile and cache it for the future.
> ...
> or as independent script that populates .perfconfig
>
That's cool, but could I implement it later? Introducing such stuffs
also introduces
a lot of trouble tasks:
1. Dependency on make and kernel build. We should search make and kbuild
dir dynamically
and also gives users the right to specify them by theirselves. A lot
of configuration
options should be appended: --make-path=/path/to/make
--kbuild-dir=kernel/build/dir
in cmdline and
[kbuild]
make_path = /path/to/make
kbuild_dir = /path/to/kbuild
in .perfconfig.
2. Selection of architectures. Although currently we want it to work
only when we dynamically
compile a script, I think finally we should consider cross compiling
bpf objects. Then cmdline
generation becomes complex. Also, --arch and [kbuild.arch] should
also be introduced.
3. Kernel dependency. Consider if kernel decides to change its interface...
I think currently we can pop some messages to let user know how to get
include dirs manually,
let further patches to do it for them automatically.
P.S.
Have you tested your Makefile? It doesn't work for me:
# cat ./Makefile
obj-y := dummy.o
$(obj)/%.o: $(src)/%.c
@echo -n "$(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS)" > xxxx
# make -s -C kernel/build/dir M=`pwd` dummy.o
make[2]: *** No rule to make target `dummy.o'. Stop.
make[1]: *** [sub-make] Error 2
make: *** [__sub-make] Error 2
Thank you.
On 6/9/15 5:47 PM, Wangnan (F) wrote:
>
>
> On 2015/6/10 7:43, Alexei Starovoitov wrote:
>> On 6/8/15 10:50 PM, Wang Nan wrote:
>>> perf_bpf_config() is added to parse 'bpf' section in perf config file.
>>> Following is an example:
>>>
>>> [bpf]
>>> clang-path = /llvm/bin/x86_64-linux-clang"
>>> llc-path = /llvm/bin/x86_64-linux-llc"
>>> clang-opt = "-nostdinc -isystem /llvm/lib/clang/include
>>> -I/kernel/arch/x86/include ..."
>>> llc-opt = ""
>>
>> a section to specify -I flags to compile prog.c is useful,
>> but users shouldn't be populating it manually for kernel headers.
>> How about adding a script that can figure out $(LINUXINCLUDE)
>> automatically ?
>> You can even invoke such flag detector from perf via something like:
>> f = open /tmpdir/Makefile
>> fprintf(f, "obj-y := dummy.o\n");
>> fprintf(f, "$(obj)/%%.o: $(src)/%%.c\n");
>> fprintf(f, "\t@echo -n \"$(NOSTDINC_FLAGS) $(LINUXINCLUDE)
>> $(EXTRA_CFLAGS)\" > %s\n", cachefile);
>> snprintf(cmd, "make -s -C /lib/modules/%s/build M=%s dummy.o",
>> uts_release, tmpdir);
>> system(cmd);
>> read flags from cachefile and cache it for the future.
>> ...
>> or as independent script that populates .perfconfig
>>
>
> That's cool, but could I implement it later? Introducing such stuffs
I think --clang-opts shouldn't be introduced without
automatic flag finder. Ease of use is important.
> also introduces
> a lot of trouble tasks:
>
> 1. Dependency on make and kernel build. We should search make and kbuild
> dir dynamically
> and also gives users the right to specify them by theirselves. A lot
> of configuration
> options should be appended: --make-path=/path/to/make
> --kbuild-dir=kernel/build/dir
> in cmdline and
> [kbuild]
> make_path = /path/to/make
> kbuild_dir = /path/to/kbuild
> in .perfconfig.
they're not mandatory. If 'make' is not in a PATH, it's dead end.
kernel build is very likely installed in /lib/modules/
> 2. Selection of architectures. Although currently we want it to work
> only when we dynamically
> compile a script, I think finally we should consider cross compiling
> bpf objects. Then cmdline
> generation becomes complex. Also, --arch and [kbuild.arch] should
> also be introduced.
that's a rare use case. This one can be added later.
> 3. Kernel dependency. Consider if kernel decides to change its interface...
change what interface? 'make M=' ? sure. then lots of scripts will be
broken.
> I think currently we can pop some messages to let user know how to get
> include dirs manually,
> let further patches to do it for them automatically.
>
> P.S.
>
> Have you tested your Makefile? It doesn't work for me:
works as a charm:
$ cat /home/ast/ff/Makefile
obj-y := dummy.o
$(obj)/%.o: $(src)/%.c
@echo -n "$(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS)" > /tmp/xxx
$ make -s -C /w/net-next/bld_x64 M=/home/ast/ff dummy.o
$ cat /tmp/xxx
-nostdinc -isystem /usr/lib/gcc/x86_64-linux-gnu/4.7/include
-I../arch/x86/include -Iarch/x86/include/generated/uapi
-Iarch/x86/include/generated -I../include -Iinclude
-I../arch/x86/include/uapi -Iarch/x86/include/generated/uapi
-I../include/uapi -Iinclude/generated/uapi -include
../include/linux/kconfig.h
On 6/9/15 5:17 PM, Wangnan (F) wrote:
> Could you please give me some URL to LLVM git repositories so I can
> track your work on it?
traffic on llvm/clang is very heavy. probably as much as lkml.
you can subscribe to llvmweekly instead.
In the future I'll cc you on new things in that area.
On 2015/6/10 9:09, Alexei Starovoitov wrote:
> On 6/9/15 5:47 PM, Wangnan (F) wrote:
>>
>>
>> On 2015/6/10 7:43, Alexei Starovoitov wrote:
>>> On 6/8/15 10:50 PM, Wang Nan wrote:
>>>> perf_bpf_config() is added to parse 'bpf' section in perf config file.
>>>> Following is an example:
>>>>
>>>> [bpf]
>>>> clang-path = /llvm/bin/x86_64-linux-clang"
>>>> llc-path = /llvm/bin/x86_64-linux-llc"
>>>> clang-opt = "-nostdinc -isystem /llvm/lib/clang/include
>>>> -I/kernel/arch/x86/include ..."
>>>> llc-opt = ""
>>>
>>> a section to specify -I flags to compile prog.c is useful,
>>> but users shouldn't be populating it manually for kernel headers.
>>> How about adding a script that can figure out $(LINUXINCLUDE)
>>> automatically ?
>>> You can even invoke such flag detector from perf via something like:
>>> f = open /tmpdir/Makefile
>>> fprintf(f, "obj-y := dummy.o\n");
>>> fprintf(f, "$(obj)/%%.o: $(src)/%%.c\n");
>>> fprintf(f, "\t@echo -n \"$(NOSTDINC_FLAGS) $(LINUXINCLUDE)
>>> $(EXTRA_CFLAGS)\" > %s\n", cachefile);
>>> snprintf(cmd, "make -s -C /lib/modules/%s/build M=%s dummy.o",
>>> uts_release, tmpdir);
>>> system(cmd);
>>> read flags from cachefile and cache it for the future.
>>> ...
>>> or as independent script that populates .perfconfig
>>>
>>
>> That's cool, but could I implement it later? Introducing such stuffs
>
> I think --clang-opts shouldn't be introduced without
> automatic flag finder. Ease of use is important.
>
What about automatic finder failed? It is possible that user environment
doesn't have
kbuild directory installed, should we make it another deadend? Then
these 'ease of use'
design is useless for me because in embedded area usecases are always rare.
I think we can hide --clang-opt from cmdline, make it reside in
.perfconfig only, and
pop some messages on it when failure. Most of the time users need to
config only once.
Still not very hard, right?
>> also introduces
>> a lot of trouble tasks:
>>
>> 1. Dependency on make and kernel build. We should search make and kbuild
>> dir dynamically
>> and also gives users the right to specify them by theirselves. A lot
>> of configuration
>> options should be appended: --make-path=/path/to/make
>> --kbuild-dir=kernel/build/dir
>> in cmdline and
>> [kbuild]
>> make_path = /path/to/make
>> kbuild_dir = /path/to/kbuild
>> in .perfconfig.
>
> they're not mandatory. If 'make' is not in a PATH, it's dead end.
> kernel build is very likely installed in /lib/modules/
>
>> 2. Selection of architectures. Although currently we want it to work
>> only when we dynamically
>> compile a script, I think finally we should consider cross compiling
>> bpf objects. Then cmdline
>> generation becomes complex. Also, --arch and [kbuild.arch] should
>> also be introduced.
>
> that's a rare use case. This one can be added later.
>
>> 3. Kernel dependency. Consider if kernel decides to change its
>> interface...
>
> change what interface? 'make M=' ? sure. then lots of scripts will be
> broken.
>
'make M=' is unlikely to be changed. However it is possible for kernel
to append
more variable for specifying include, or devide $(NOSTDINC_FLAGS)
$(LINUXINCLUDE)
and $(EXTRA_CFLAGS) into smaller pieces. I'm not quite sure whether
those make
variables are part of kbuild interface.
>> I think currently we can pop some messages to let user know how to get
>> include dirs manually,
>> let further patches to do it for them automatically.
>>
>> P.S.
>>
>> Have you tested your Makefile? It doesn't work for me:
>
> works as a charm:
> $ cat /home/ast/ff/Makefile
> obj-y := dummy.o
>
> $(obj)/%.o: $(src)/%.c
> @echo -n "$(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS)" >
> /tmp/xxx
> $ make -s -C /w/net-next/bld_x64 M=/home/ast/ff dummy.o
> $ cat /tmp/xxx
> -nostdinc -isystem /usr/lib/gcc/x86_64-linux-gnu/4.7/include
> -I../arch/x86/include -Iarch/x86/include/generated/uapi
> -Iarch/x86/include/generated -I../include -Iinclude
> -I../arch/x86/include/uapi -Iarch/x86/include/generated/uapi
> -I../include/uapi -Iinclude/generated/uapi -include
> ../include/linux/kconfig.h
>
Finally I made it work. dummy.c must exist on current directory.
What we are talking about seems not very suit to be implemented using C.
It should be implemented
using a shell script:
#!/usr/bin/env sh
TMPDIR=`mktemp -d`
cat << EOF > $TMPDIR/Makefile
obj-y := dummy.o
\$(obj)/%.o: \$(src)/%.c
@echo -n "\$(NOSTDINC_FLAGS) \$(LINUXINCLUDE) \$(EXTRA_CFLAGS)"
> $TMPDIR/cflags
EOF
touch $TMPDIR/dummy.c
DEFAULT_KBUILD_DIR=/lib/modules/`uname -r`/build
if ! test -d "$KBUILD_DIR"
then
KBUILD_DIR=$DEFAULT_KBUILD_DIR
fi
make -s -C $KBUILD_DIR M=$TMPDIR $OTHER_OPTS dummy.o 2>/dev/null
cat $TMPDIR/cflags 2>/dev/null
RET=$?
rm -rf $TMPDIR
exit $RET
In this script, we can inject KBUILD_DIR and OTHER_OPTS to select another
kbuild directory and "ARCH=" option.
I'll embed this script in my next version.
Thaknk you.
On 6/9/15 7:23 PM, Wangnan (F) wrote:
>
> I'll embed this script in my next version.
fine, let's use the script for now and inform the user
that they would need to manually copy the flags into .perfconfig
Hi Wang,
On Wed, Jun 10, 2015 at 08:06:17AM +0800, Wangnan (F) wrote:
> On 2015/6/10 5:48, Alexei Starovoitov wrote:
> >Once clang integration is complete. One can pull
> >upsteam llvm and clang and just use 'clang -O2 -c -march=bpf file.c'
> >
>
> Good news, so llc part should be removed.
>
> Next version I'd like to use following config options:
>
> [llvm]
> clang-bpf-cmd-template = "$CLANG_EXEC $CLANG_OPTIONS -c $CLANG_SOURCE
> -emit-llvm -O2 -o - | /path/to/llc -march=bpf -filetype=obj -o -"
> clang-path = "/path/to/clang"
> clang-opt = ""
>
> And the default template should be:
>
> $CLANG_EXEC $CLANG_OPTIONS -c "$CLANG_SOURCE" -emit-llvm -O2 -o -
Did you mean this?
$CLANG_EXEC $CLANG_OPTIONS -O2 -c "$BPF_SOURCE" -march=bpf -o -
Thanks,
Namhyung
>
> Then with environment variable tricks we make it work with popen.
>
> By this way we can get rid of llc in perf side, and make it work even before
> clang
> integration is complete.
>
> Thank you.
On 2015/6/11 15:19, Namhyung Kim wrote:
> Hi Wang,
>
> On Wed, Jun 10, 2015 at 08:06:17AM +0800, Wangnan (F) wrote:
>> On 2015/6/10 5:48, Alexei Starovoitov wrote:
>>> Once clang integration is complete. One can pull
>>> upsteam llvm and clang and just use 'clang -O2 -c -march=bpf file.c'
>>>
>> Good news, so llc part should be removed.
>>
>> Next version I'd like to use following config options:
>>
>> [llvm]
>> clang-bpf-cmd-template = "$CLANG_EXEC $CLANG_OPTIONS -c $CLANG_SOURCE
>> -emit-llvm -O2 -o - | /path/to/llc -march=bpf -filetype=obj -o -"
>> clang-path = "/path/to/clang"
>> clang-opt = ""
>>
>> And the default template should be:
>>
>> $CLANG_EXEC $CLANG_OPTIONS -c "$CLANG_SOURCE" -emit-llvm -O2 -o -
> Did you mean this?
>
> $CLANG_EXEC $CLANG_OPTIONS -O2 -c "$BPF_SOURCE" -march=bpf -o -
Thank you for this notice.
Now I'm trying this:
"$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS "
"-Wno-unused-value -Wno-pointer-sign "
"-working-directory $WORKING_DIR "
" -c \"$CLANG_SOURCE\" -march=bpf -O2 -o -"
WORKING_DIR is appended because we will get relative include directory
using the
Makefile trick.
> Thanks,
> Namhyung
>
>
>> Then with environment variable tricks we make it work with popen.
>>
>> By this way we can get rid of llc in perf side, and make it work even before
>> clang
>> integration is complete.
>>
>> Thank you.
On Tue, Jun 09, 2015 at 04:43:19PM -0700, Alexei Starovoitov wrote:
> On 6/8/15 10:50 PM, Wang Nan wrote:
> >perf_bpf_config() is added to parse 'bpf' section in perf config file.
> >Following is an example:
> >
> > [bpf]
> > clang-path = /llvm/bin/x86_64-linux-clang"
> > llc-path = /llvm/bin/x86_64-linux-llc"
> > clang-opt = "-nostdinc -isystem /llvm/lib/clang/include -I/kernel/arch/x86/include ..."
> > llc-opt = ""
>
> a section to specify -I flags to compile prog.c is useful,
> but users shouldn't be populating it manually for kernel headers.
> How about adding a script that can figure out $(LINUXINCLUDE)
> automatically ?
Maybe a dummy question. For what reason it needs such headers? Is it
possible to have a (part of?) copy of needed bits in the perf source?
Thanks,
Namhyung
> You can even invoke such flag detector from perf via something like:
> f = open /tmpdir/Makefile
> fprintf(f, "obj-y := dummy.o\n");
> fprintf(f, "$(obj)/%%.o: $(src)/%%.c\n");
> fprintf(f, "\t@echo -n \"$(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS)\"
> > %s\n", cachefile);
> snprintf(cmd, "make -s -C /lib/modules/%s/build M=%s dummy.o",
> uts_release, tmpdir);
> system(cmd);
> read flags from cachefile and cache it for the future.
> ...
> or as independent script that populates .perfconfig
>
On 2015/6/11 15:45, Namhyung Kim wrote:
> On Tue, Jun 09, 2015 at 04:43:19PM -0700, Alexei Starovoitov wrote:
>> On 6/8/15 10:50 PM, Wang Nan wrote:
>>> perf_bpf_config() is added to parse 'bpf' section in perf config file.
>>> Following is an example:
>>>
>>> [bpf]
>>> clang-path = /llvm/bin/x86_64-linux-clang"
>>> llc-path = /llvm/bin/x86_64-linux-llc"
>>> clang-opt = "-nostdinc -isystem /llvm/lib/clang/include -I/kernel/arch/x86/include ..."
>>> llc-opt = ""
>> a section to specify -I flags to compile prog.c is useful,
>> but users shouldn't be populating it manually for kernel headers.
>> How about adding a script that can figure out $(LINUXINCLUDE)
>> automatically ?
> Maybe a dummy question. For what reason it needs such headers? Is it
> possible to have a (part of?) copy of needed bits in the perf source?
>
> Thanks,
> Namhyung
It is improtant for eBPF script that it should be able to extract
information
from kernel pointers. For example: to extract registers from 'struct
pt_regs' and
to extract fields from 'struct page *'. I believe this is an improtant
reason
why we decide to use C-like language instead of designing a DLS.
Therefore, when
compiling .c to .o, kernel source is mandatory. Since we don't want to
limit the
ability of data accessing of eBPF scripts, we have to give it ability to
access all
kernel headers.
Thank you for reviewing.
>> You can even invoke such flag detector from perf via something like:
>> f = open /tmpdir/Makefile
>> fprintf(f, "obj-y := dummy.o\n");
>> fprintf(f, "$(obj)/%%.o: $(src)/%%.c\n");
>> fprintf(f, "\t@echo -n \"$(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS)\"
>>> %s\n", cachefile);
>> snprintf(cmd, "make -s -C /lib/modules/%s/build M=%s dummy.o",
>> uts_release, tmpdir);
>> system(cmd);
>> read flags from cachefile and cache it for the future.
>> ...
>> or as independent script that populates .perfconfig
>>
On Tue, Jun 09, 2015 at 05:50:32AM +0000, Wang Nan wrote:
> In this patch, kprobe points are created using add_perf_probe_events.
> Since all events are already grouped together in an array, calling
> add_perf_probe_events() once creates all of them.
>
> probe_conf.max_probes is set to MAX_PROBES to support glob matching.
>
> Signed-off-by: Wang Nan <[email protected]>
> ---
> tools/perf/builtin-record.c | 14 ++++++++++++-
> tools/perf/util/bpf-loader.c | 47 ++++++++++++++++++++++++++++++++++++++++++++
> tools/perf/util/bpf-loader.h | 4 ++++
> 3 files changed, 64 insertions(+), 1 deletion(-)
>
> diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
> index 91aa2a3..a7c178e 100644
> --- a/tools/perf/builtin-record.c
> +++ b/tools/perf/builtin-record.c
> @@ -29,6 +29,7 @@
> #include "util/data.h"
> #include "util/auxtrace.h"
> #include "util/parse-branch-options.h"
> +#include "util/bpf-loader.h"
>
> #include <unistd.h>
> #include <sched.h>
> @@ -1108,7 +1109,17 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
> if (err)
> return err;
>
> - err = -ENOMEM;
> + /*
> + * bpf__probe must be called before symbol__init() because we
> + * need init_symbol_maps. If called after symbol__init,
> + * symbol_conf.sort_by_name won't take effect.
> + */
> + err = bpf__probe();
> + if (err) {
> + pr_err("Probing at events in BPF object failed.\n");
> + pr_err("Try perf probe -d '*' to remove existing probe events.\n");
Wouldn't it be better to call bpf__unprobe() here or goto
out_symbol_exit?
Thanks,
Namhyung
> + return err;
> + }
>
> symbol__init(NULL);
>
> @@ -1169,6 +1180,7 @@ out_symbol_exit:
> perf_evlist__delete(rec->evlist);
> symbol__exit();
> auxtrace_record__free(rec->itr);
> + bpf__unprobe();
> return err;
> }
>
> diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
> index d786efc..8c0bf46 100644
> --- a/tools/perf/util/bpf-loader.c
> +++ b/tools/perf/util/bpf-loader.c
> @@ -373,3 +373,50 @@ void bpf__clear(void)
> bpf_object__for_each(obj, tmp)
> bpf_object__close(obj);
> }
> +
> +static bool is_probing = false;
> +
> +int bpf__unprobe(void)
> +{
> + struct strfilter *delfilter;
> + int ret;
> +
> + if (!is_probing)
> + return 0;
> +
> + delfilter = strfilter__new(PERF_BPF_PROBE_GROUP ":*", NULL);
> + if (!delfilter) {
> + pr_err("Failed to create delfilter when unprobing\n");
> + return -ENOMEM;
> + }
> +
> + ret = del_perf_probe_events(delfilter);
> + strfilter__delete(delfilter);
> + if (ret < 0 && is_probing)
> + pr_err("Error: failed to delete events: %s\n",
> + strerror(-ret));
> + else
> + is_probing = false;
> + return ret < 0 ? ret : 0;
> +}
> +
> +int bpf__probe(void)
> +{
> + int err;
> +
> + if (nr_probe_events <= 0)
> + return 0;
> +
> + probe_conf.max_probes = MAX_PROBES;
> + /* Let add_perf_probe_events keeps probe_trace_event */
> + err = add_perf_probe_events(probe_event_array,
> + nr_probe_events,
> + false);
> + /* add_perf_probe_events return negative when fail */
> + if (err < 0)
> + pr_err("bpf probe: failed to probe events\n");
> + else
> + is_probing = true;
> +
> + return err < 0 ? err : 0;
> +}
> diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
> index 2ed7a16..7387fb6 100644
> --- a/tools/perf/util/bpf-loader.h
> +++ b/tools/perf/util/bpf-loader.h
> @@ -21,6 +21,8 @@ extern int perf_bpf_config(const char *var, const char *value);
>
> #ifdef HAVE_LIBBPF_SUPPORT
> int bpf__prepare_load(const char *filename, bool source);
> +int bpf__probe(void);
> +int bpf__unprobe(void);
>
> void bpf__clear(void);
> #else
> @@ -30,6 +32,8 @@ static inline int bpf__prepare_load(const char *filename __maybe_unused)
> return -1;
> }
>
> +static inline int bpf__probe(void) { return 0; }
> +static inline int bpf__unprobe(void) { return 0; }
> static inline void bpf__clear(void) { }
> #endif
> #endif
> --
> 1.8.3.4
>
On Thu, Jun 11, 2015 at 04:09:36PM +0800, Wangnan (F) wrote:
> On 2015/6/11 15:45, Namhyung Kim wrote:
> >On Tue, Jun 09, 2015 at 04:43:19PM -0700, Alexei Starovoitov wrote:
> >>On 6/8/15 10:50 PM, Wang Nan wrote:
> >>>perf_bpf_config() is added to parse 'bpf' section in perf config file.
> >>>Following is an example:
> >>>
> >>> [bpf]
> >>> clang-path = /llvm/bin/x86_64-linux-clang"
> >>> llc-path = /llvm/bin/x86_64-linux-llc"
> >>> clang-opt = "-nostdinc -isystem /llvm/lib/clang/include -I/kernel/arch/x86/include ..."
> >>> llc-opt = ""
> >>a section to specify -I flags to compile prog.c is useful,
> >>but users shouldn't be populating it manually for kernel headers.
> >>How about adding a script that can figure out $(LINUXINCLUDE)
> >>automatically ?
> >Maybe a dummy question. For what reason it needs such headers? Is it
> >possible to have a (part of?) copy of needed bits in the perf source?
> >
> >Thanks,
> >Namhyung
>
> It is improtant for eBPF script that it should be able to extract
> information
> from kernel pointers. For example: to extract registers from 'struct
> pt_regs' and
> to extract fields from 'struct page *'. I believe this is an improtant
> reason
> why we decide to use C-like language instead of designing a DLS. Therefore,
> when
> compiling .c to .o, kernel source is mandatory. Since we don't want to limit
> the
> ability of data accessing of eBPF scripts, we have to give it ability to
> access all
> kernel headers.
Understood, thank you for the explanation!
Namhyung
On 6/11/15 12:35 AM, Wangnan (F) wrote:
> Now I'm trying this:
>
> "$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS "
> "-Wno-unused-value -Wno-pointer-sign "
> "-working-directory $WORKING_DIR "
> " -c \"$CLANG_SOURCE\" -march=bpf -O2 -o -"
>
> WORKING_DIR is appended because we will get relative include directory
> using the
> Makefile trick.
clang bpf support is in.
so please pull the latest llvm and clang, rebuild and you should
be able to use:
clang -O2 -target bpf -c file.c
Just like any other arch use -S to generate .s and so on.
Old style 'clang -emit-llvm -o - | llc -march=bpf' is fine too.
To build llvm/clang do:
git clone https://github.com/llvm-mirror/llvm.git
git clone https://github.com/llvm-mirror/clang.git llvm/tools/clang
mkdir llvm/build/
cd llvm/build/
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_TARGETS_TO_BUILD="X86;BPF" \
-DCMAKE_INSTALL_PREFIX=/opt/local/llvm
make -j
sudo make install