From ed63f34c026e9a60d17fa750ecdfe3f600d49393 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Wed, 14 Oct 2015 12:41:12 +0000 Subject: [PATCH 1/6] perf tools: Make perf depend on libbpf By adding libbpf into perf's Makefile, this patch enables perf to build libbpf if libelf is found and neither NO_LIBELF nor NO_LIBBPF is set. The newly introduced code is similar to how libapi and libtraceevent are wired into Makefile.perf. MANIFEST is also updated for 'make perf-*-src-pkg'. Append make_no_libbpf to tools/perf/tests/make. The 'bpf' feature check is appended into default FEATURE_TESTS and FEATURE_DISPLAY, so perf will check the API version of bpf in /path/to/kernel/include/uapi/linux/bpf.h. Which should not fail except when we are trying to port this code to an old kernel. Error messages are also updated to notify users about the lack of BPF support in 'perf record' if libelf is missing or the BPF API check failed. tools/lib/bpf is added to TAG_FOLDERS to allow us to navigate libbpf files when working on perf using tools/perf/tags. Signed-off-by: Wang Nan Acked-by: Alexei Starovoitov Cc: Brendan Gregg Cc: Daniel Borkmann Cc: David Ahern Cc: He Kuang Cc: Jiri Olsa Cc: Kaixu Xia Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1444826502-49291-2-git-send-email-wangnan0@huawei.com [ Document NO_LIBBPF in Makefile.perf, noted by Jiri Olsa ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/Makefile.feature | 6 ++++-- tools/perf/MANIFEST | 3 +++ tools/perf/Makefile.perf | 21 +++++++++++++++++++-- tools/perf/config/Makefile | 19 ++++++++++++++++++- tools/perf/tests/make | 4 +++- 5 files changed, 47 insertions(+), 6 deletions(-) diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index 72817e4d5e70..37ff4c9f92f1 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -53,7 +53,8 @@ FEATURE_TESTS ?= \ libdw-dwarf-unwind \ zlib \ lzma \ - get_cpuid + get_cpuid \ + bpf FEATURE_DISPLAY ?= \ dwarf \ @@ -71,7 +72,8 @@ FEATURE_DISPLAY ?= \ libdw-dwarf-unwind \ zlib \ lzma \ - get_cpuid + get_cpuid \ + 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/perf/MANIFEST b/tools/perf/MANIFEST index 9e6bdf5b2df6..39c38cb45b00 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/bpf tools/lib/hweight.c @@ -69,6 +70,8 @@ arch/*/lib/memset*.S include/linux/poison.h include/linux/hw_breakpoint.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 56517d304772..1e2e2d1d26b7 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -75,6 +75,8 @@ include config/utilities.mak # Define NO_LZMA if you do not want to support compressed (xz) kernel modules # # Define NO_AUXTRACE if you do not want AUX area tracing support +# +# Define NO_LIBBPF if you do not want BPF support # As per kernel Makefile, avoid funny character set dependencies unexport LC_ALL @@ -145,6 +147,7 @@ AWK = awk 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 @@ -180,6 +183,7 @@ strip-libs = $(filter-out -l%,$(1)) ifneq ($(OUTPUT),) TE_PATH=$(OUTPUT) + BPF_PATH=$(OUTPUT) ifneq ($(subdir),) LIB_PATH=$(OUTPUT)/../lib/api/ else @@ -188,6 +192,7 @@ endif else TE_PATH=$(TRACE_EVENT_DIR) LIB_PATH=$(LIB_DIR) + BPF_PATH=$(BPF_DIR) endif LIBTRACEEVENT = $(TE_PATH)libtraceevent.a @@ -199,6 +204,8 @@ LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS = -Xlinker --dynamic-list=$(LIBTRACEEVENT_DYN 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/ @@ -251,6 +258,9 @@ export PERL_PATH LIB_FILE=$(OUTPUT)libperf.a PERFLIBS = $(LIB_FILE) $(LIBAPI) $(LIBTRACEEVENT) +ifndef NO_LIBBPF + PERFLIBS += $(LIBBPF) +endif # We choose to avoid "if .. else if .. else .. endif endif" # because maintaining the nesting to match is a pain. If @@ -420,6 +430,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)' @@ -459,7 +476,7 @@ INSTALL_DOC_TARGETS += quick-install-doc quick-install-man quick-install-html $(DOC_TARGETS): $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:doc=all) -TAG_FOLDERS= . ../lib/traceevent ../lib/api ../lib/symbol ../include +TAG_FOLDERS= . ../lib/traceevent ../lib/api ../lib/symbol ../include ../lib/bpf TAG_FILES= ../../include/uapi/linux/perf_event.h TAGS: @@ -567,7 +584,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) $(OUTPUT).config-detected diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index ab09adaabc9c..de89ec574361 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 @@ -237,6 +238,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) @@ -246,13 +248,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); @@ -309,6 +312,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) @@ -324,6 +334,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 ba31c4bd441d..2cbd0c6901e3 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make @@ -44,6 +44,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 @@ -66,7 +67,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 @@ -94,6 +95,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 From 69d262a93a25cf475012ea2e00aeb29f4932c028 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Wed, 14 Oct 2015 12:41:13 +0000 Subject: [PATCH 2/6] perf ebpf: Add the libbpf glue The 'bpf-loader.[ch]' files are introduced in this patch. Which will be the interface between perf and libbpf. bpf__prepare_load() resides in bpf-loader.c. Following patches will enrich these two files. Signed-off-by: Wang Nan Acked-by: Alexei Starovoitov Cc: Brendan Gregg Cc: Daniel Borkmann Cc: David Ahern Cc: He Kuang Cc: Jiri Olsa Cc: Kaixu Xia Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1444826502-49291-3-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-loader.c | 57 ++++++++++++++++++++++++++++++++++++ tools/perf/util/bpf-loader.h | 29 ++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 tools/perf/util/bpf-loader.c create mode 100644 tools/perf/util/bpf-loader.h diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c new file mode 100644 index 000000000000..ab56073c5d6e --- /dev/null +++ b/tools/perf/util/bpf-loader.c @@ -0,0 +1,57 @@ +/* + * bpf-loader.c + * + * Copyright (C) 2015 Wang Nan + * Copyright (C) 2015 Huawei Inc. + */ + +#include +#include +#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) + +struct bpf_object *bpf__prepare_load(const char *filename) +{ + struct bpf_object *obj; + static bool libbpf_initialized; + + if (!libbpf_initialized) { + libbpf_set_print(libbpf_warning, + libbpf_info, + libbpf_debug); + libbpf_initialized = true; + } + + obj = bpf_object__open(filename); + if (!obj) { + pr_debug("bpf: failed to load %s\n", filename); + return ERR_PTR(-EINVAL); + } + + return obj; +} + +void bpf__clear(void) +{ + struct bpf_object *obj, *tmp; + + bpf_object__for_each_safe(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 000000000000..f402d7c8c288 --- /dev/null +++ b/tools/perf/util/bpf-loader.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015, Wang Nan + * Copyright (C) 2015, Huawei Inc. + */ +#ifndef __BPF_LOADER_H +#define __BPF_LOADER_H + +#include +#include +#include +#include "debug.h" + +struct bpf_object; + +#ifdef HAVE_LIBBPF_SUPPORT +struct bpf_object *bpf__prepare_load(const char *filename); + +void bpf__clear(void); +#else +static inline struct bpf_object * +bpf__prepare_load(const char *filename __maybe_unused) +{ + pr_debug("ERROR: eBPF object loading is disabled during compiling.\n"); + return ERR_PTR(-ENOTSUP); +} + +static inline void bpf__clear(void) { } +#endif +#endif From 84c86ca12b2189df751eed7b2d67cb63bc8feda5 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Wed, 14 Oct 2015 12:41:14 +0000 Subject: [PATCH 3/6] perf tools: Enable passing bpf object file to --event 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. After applying this patch, commands like: # perf record --event foo.o sleep become possible. However, at this point it is unable to link any useful things onto the evsel list because the creating of probe points and BPF program attaching have not been implemented. Before real events are possible to be extracted, to avoid perf report error because of empty evsel list, this patch link a dummy evsel. The dummy event related code will be removed when probing and extracting code is ready. Commiter notes: Using it: $ ls -la foo.o ls: cannot access foo.o: No such file or directory $ perf record --event foo.o sleep libbpf: failed to open foo.o: No such file or directory event syntax error: 'foo.o' \___ BPF object file 'foo.o' is invalid (add -v to see detail) Run 'perf list' for a list of valid events Usage: perf record [] [] or: perf record [] -- [] -e, --event event selector. use 'perf list' to list available events $ $ file /tmp/build/perf/perf.o /tmp/build/perf/perf.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped $ perf record --event /tmp/build/perf/perf.o sleep libbpf: /tmp/build/perf/perf.o is not an eBPF object file event syntax error: '/tmp/build/perf/perf.o' \___ BPF object file '/tmp/build/perf/perf.o' is invalid (add -v to see detail) Run 'perf list' for a list of valid events Usage: perf record [] [] or: perf record [] -- [] -e, --event event selector. use 'perf list' to list available events $ $ file /tmp/foo.o /tmp/foo.o: ELF 64-bit LSB relocatable, no machine, version 1 (SYSV), not stripped $ perf record --event /tmp/foo.o sleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.013 MB perf.data ] $ perf evlist /tmp/foo.o $ perf evlist -v /tmp/foo.o: type: 1, size: 112, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|PERIOD, disabled: 1, inherit: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1 $ So, type 1 is PERF_TYPE_SOFTWARE, config 0x9 is PERF_COUNT_SW_DUMMY, ok. $ perf report --stdio Error: The perf.data file has no samples! # To display the perf.data header info, please use --header/--header-only options. # $ Signed-off-by: Wang Nan Tested-by: Arnaldo Carvalho de Melo Cc: Alexei Starovoitov Cc: Brendan Gregg Cc: Daniel Borkmann Cc: David Ahern Cc: He Kuang Cc: Jiri Olsa Cc: Kaixu Xia Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1444826502-49291-4-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/perf.c | 2 ++ tools/perf/util/Build | 1 + tools/perf/util/parse-events.c | 57 ++++++++++++++++++++++++++++++++++ tools/perf/util/parse-events.h | 8 +++++ tools/perf/util/parse-events.l | 3 ++ tools/perf/util/parse-events.y | 18 ++++++++++- 6 files changed, 88 insertions(+), 1 deletion(-) diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 543713422d14..3d4c7c09adea 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -15,6 +15,7 @@ #include "util/run-command.h" #include "util/parse-events.h" #include "util/parse-options.h" +#include "util/bpf-loader.h" #include "util/debug.h" #include #include @@ -385,6 +386,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) status = p->fn(argc, argv, prefix); exit_browser(status); perf_env__exit(&perf_env); + bpf__clear(); if (status) return status & 0xff; diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 9217119c4108..591b3fe3ed49 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -87,6 +87,7 @@ libperf-$(CONFIG_AUXTRACE) += intel-bts.o libperf-y += parse-branch-options.o libperf-y += parse-regs-options.o +libperf-$(CONFIG_LIBBPF) += bpf-loader.o libperf-$(CONFIG_LIBELF) += symbol-elf.o libperf-$(CONFIG_LIBELF) += probe-file.o libperf-$(CONFIG_LIBELF) += probe-event.o diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 72abcf254ccb..a9e1d79d17d7 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -11,6 +11,7 @@ #include "symbol.h" #include "cache.h" #include "header.h" +#include "bpf-loader.h" #include "debug.h" #include #include "parse-events-bison.h" @@ -529,6 +530,62 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx, return ret; } +int parse_events_load_bpf_obj(struct parse_events_evlist *data, + struct list_head *list, + struct bpf_object *obj) +{ + int err; + char errbuf[BUFSIZ]; + + if (IS_ERR(obj) || !obj) { + snprintf(errbuf, sizeof(errbuf), + "Internal error: load bpf obj with NULL"); + err = -EINVAL; + goto errout; + } + + /* + * Temporary add a dummy event here so we can check whether + * basic bpf loader works. Following patches will replace + * dummy event by useful evsels. + */ + return parse_events_add_numeric(data, list, PERF_TYPE_SOFTWARE, + PERF_COUNT_SW_DUMMY, NULL); +errout: + data->error->help = strdup("(add -v to see detail)"); + data->error->str = strdup(errbuf); + return err; +} + +int parse_events_load_bpf(struct parse_events_evlist *data, + struct list_head *list, + char *bpf_file_name) +{ + struct bpf_object *obj; + + obj = bpf__prepare_load(bpf_file_name); + if (IS_ERR(obj) || !obj) { + char errbuf[BUFSIZ]; + int err; + + err = obj ? PTR_ERR(obj) : -EINVAL; + + if (err == -ENOTSUP) + snprintf(errbuf, sizeof(errbuf), + "BPF support is not compiled"); + else + snprintf(errbuf, sizeof(errbuf), + "BPF object file '%s' is invalid", + bpf_file_name); + + data->error->help = strdup("(add -v to see detail)"); + data->error->str = strdup(errbuf); + return err; + } + + return parse_events_load_bpf_obj(data, list, obj); +} + 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 13c9063513eb..765018a17448 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -123,6 +123,14 @@ int parse_events_add_tracepoint(struct list_head *list, int *idx, char *sys, char *event, struct parse_events_error *error, struct list_head *head_config); +int parse_events_load_bpf(struct parse_events_evlist *data, + struct list_head *list, + char *bpf_file_name); +/* Provide this function for perf test */ +struct bpf_object; +int parse_events_load_bpf_obj(struct parse_events_evlist *data, + struct list_head *list, + struct bpf_object *obj); 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 8d0de5b2991d..cf330ebf812c 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); @@ -266,6 +268,7 @@ r{num_raw_hex} { return raw(yyscanner); } {num_hex} { return value(yyscanner, 16); } {modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); } +{bpf_object} { return str(yyscanner, PE_BPF_OBJECT); } {name} { return pmu_str_check(yyscanner); } "/" { BEGIN(config); return '/'; } - { return '-'; } diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index ae6af269f9c9..497f19b20f0b 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 PE_RAW %type PE_TERM %type PE_NAME +%type PE_BPF_OBJECT %type PE_NAME_CACHE_TYPE %type PE_NAME_CACHE_OP_RESULT %type PE_MODIFIER_EVENT @@ -70,6 +72,7 @@ static inc_group_count(struct list_head *list, %type tracepoint_name %type event_legacy_numeric %type event_legacy_raw +%type event_bpf_file %type event_def %type event_mod %type event_name @@ -203,7 +206,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 '/' @@ -449,6 +453,18 @@ PE_RAW $$ = list; } +event_bpf_file: +PE_BPF_OBJECT +{ + struct parse_events_evlist *data = _data; + struct parse_events_error *error = data->error; + struct list_head *list; + + ALLOC_LIST(list); + ABORT_ON(parse_events_load_bpf(data, list, $1)); + $$ = list; +} + start_terms: event_config { struct parse_events_terms *data = _data; From aa3abf30bb28addcf593578d37447d42e3f65fc3 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Wed, 14 Oct 2015 12:41:15 +0000 Subject: [PATCH 4/6] perf tools: Create probe points for BPF programs This patch introduces bpf__{un,}probe() functions to enable callers to create kprobe points based on section names a BPF program. It parses the section names in the program and creates corresponding 'struct perf_probe_event' structures. The parse_perf_probe_command() function is used to do the main parsing work. The resuling 'struct perf_probe_event' is stored into program private data for further using. By utilizing the new probing API, this patch creates probe points during event parsing. To ensure probe points be removed correctly, register an atexit hook so even perf quit through exit() bpf__clear() is still called, so probing points are cleared. Note that bpf_clear() should be registered before bpf__probe() is called, so failure of bpf__probe() can still trigger bpf__clear() to remove probe points which are already probed. strerror style error reporting scaffold is created by this patch. bpf__strerror_probe() is the first error reporting function in bpf-loader.c. Committer note: Trying it: To build a test eBPF object file: I am testing using a script I built from the 'perf test -v LLVM' output: $ cat ~/bin/hello-ebpf export KERNEL_INC_OPTIONS="-nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/4.8.3/include -I/home/acme/git/linux/arch/x86/include -Iarch/x86/include/generated/uapi -Iarch/x86/include/generated -I/home/acme/git/linux/include -Iinclude -I/home/acme/git/linux/arch/x86/include/uapi -Iarch/x86/include/generated/uapi -I/home/acme/git/linux/include/uapi -Iinclude/generated/uapi -include /home/acme/git/linux/include/linux/kconfig.h" export WORKING_DIR=/lib/modules/4.2.0/build export CLANG_SOURCE=- export CLANG_OPTIONS=-xc OBJ=/tmp/foo.o rm -f $OBJ echo '__attribute__((section("fork=do_fork"), used)) int fork(void *ctx) {return 0;} char _license[] __attribute__((section("license"), used)) = "GPL";int _version __attribute__((section("version"), used)) = 0x40100;' | \ clang -D__KERNEL__ $CLANG_OPTIONS $KERNEL_INC_OPTIONS -Wno-unused-value -Wno-pointer-sign -working-directory $WORKING_DIR -c "$CLANG_SOURCE" -target bpf -O2 -o /tmp/foo.o && file $OBJ --- First asking to put a probe in a function not present in the kernel (misses the initial _): $ perf record --event /tmp/foo.o sleep 1 Probe point 'do_fork' not found. event syntax error: '/tmp/foo.o' \___ You need to check probing points in BPF file (add -v to see detail) Run 'perf list' for a list of valid events Usage: perf record [] [] or: perf record [] -- [] -e, --event event selector. use 'perf list' to list available events $ --- Now, with "__attribute__((section("fork=_do_fork"), used)): $ grep _do_fork /proc/kallsyms ffffffff81099ab0 T _do_fork $ perf record --event /tmp/foo.o sleep 1 Failed to open kprobe_events: Permission denied event syntax error: '/tmp/foo.o' \___ Permission denied --- Cool, we need to provide some better hints, "kprobe_events" is too low level, one doesn't strictly need to know the precise details of how these things are put in place, so something that shows the command needed to fix the permissions would be more helpful. Lets try as root instead: # perf record --event /tmp/foo.o sleep 1 Lowering default frequency rate to 1000. Please consider tweaking /proc/sys/kernel/perf_event_max_sample_rate. [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.013 MB perf.data ] # perf evlist /tmp/foo.o [root@felicio ~]# perf evlist -v /tmp/foo.o: type: 1, size: 112, config: 0x9, { sample_period, sample_freq }: 1000, sample_type: IP|TID|TIME|PERIOD, disabled: 1, inherit: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1 --- Signed-off-by: Wang Nan Tested-by: Arnaldo Carvalho de Melo Cc: Alexei Starovoitov Cc: Brendan Gregg Cc: Daniel Borkmann Cc: David Ahern Cc: He Kuang Cc: Jiri Olsa Cc: Kaixu Xia Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1444826502-49291-5-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-loader.c | 222 ++++++++++++++++++++++++++++++++- tools/perf/util/bpf-loader.h | 30 +++++ tools/perf/util/parse-events.c | 17 +++ 3 files changed, 268 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index ab56073c5d6e..56f6fe8cf318 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -10,6 +10,8 @@ #include "perf.h" #include "debug.h" #include "bpf-loader.h" +#include "probe-event.h" +#include "probe-finder.h" // for MAX_PROBES #define DEFINE_PRINT_FN(name, level) \ static int libbpf_##name(const char *fmt, ...) \ @@ -27,6 +29,10 @@ DEFINE_PRINT_FN(warning, 0) DEFINE_PRINT_FN(info, 0) DEFINE_PRINT_FN(debug, 1) +struct bpf_prog_priv { + struct perf_probe_event pev; +}; + struct bpf_object *bpf__prepare_load(const char *filename) { struct bpf_object *obj; @@ -52,6 +58,220 @@ void bpf__clear(void) { struct bpf_object *obj, *tmp; - bpf_object__for_each_safe(obj, tmp) + bpf_object__for_each_safe(obj, tmp) { + bpf__unprobe(obj); bpf_object__close(obj); + } +} + +static void +bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused, + void *_priv) +{ + struct bpf_prog_priv *priv = _priv; + + cleanup_perf_probe_events(&priv->pev, 1); + free(priv); +} + +static int +config_bpf_program(struct bpf_program *prog) +{ + struct perf_probe_event *pev = NULL; + struct bpf_prog_priv *priv = NULL; + const char *config_str; + int err; + + config_str = bpf_program__title(prog, false); + if (!config_str) { + pr_debug("bpf: unable to get title for program\n"); + return -EINVAL; + } + + priv = calloc(sizeof(*priv), 1); + if (!priv) { + pr_debug("bpf: failed to alloc priv\n"); + return -ENOMEM; + } + pev = &priv->pev; + + pr_debug("bpf: config program '%s'\n", config_str); + err = parse_perf_probe_command(config_str, pev); + if (err < 0) { + pr_debug("bpf: '%s' is not a valid config string\n", + config_str); + err = -EINVAL; + goto errout; + } + + if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) { + pr_debug("bpf: '%s': group for event is set and not '%s'.\n", + config_str, PERF_BPF_PROBE_GROUP); + err = -EINVAL; + goto errout; + } else if (!pev->group) + pev->group = strdup(PERF_BPF_PROBE_GROUP); + + if (!pev->group) { + pr_debug("bpf: strdup failed\n"); + err = -ENOMEM; + goto errout; + } + + if (!pev->event) { + pr_debug("bpf: '%s': event name is missing\n", + config_str); + err = -EINVAL; + goto errout; + } + pr_debug("bpf: config '%s' is ok\n", config_str); + + err = bpf_program__set_private(prog, priv, bpf_prog_priv__clear); + if (err) { + pr_debug("Failed to set priv for program '%s'\n", config_str); + goto errout; + } + + return 0; + +errout: + if (pev) + clear_perf_probe_event(pev); + free(priv); + return err; +} + +static int bpf__prepare_probe(void) +{ + static int err = 0; + static bool initialized = false; + + /* + * Make err static, so if init failed the first, bpf__prepare_probe() + * fails each time without calling init_probe_symbol_maps multiple + * times. + */ + if (initialized) + return err; + + initialized = true; + err = init_probe_symbol_maps(false); + if (err < 0) + pr_debug("Failed to init_probe_symbol_maps\n"); + probe_conf.max_probes = MAX_PROBES; + return err; +} + +int bpf__probe(struct bpf_object *obj) +{ + int err = 0; + struct bpf_program *prog; + struct bpf_prog_priv *priv; + struct perf_probe_event *pev; + + err = bpf__prepare_probe(); + if (err) { + pr_debug("bpf__prepare_probe failed\n"); + return err; + } + + bpf_object__for_each_program(prog, obj) { + err = config_bpf_program(prog); + if (err) + goto out; + + err = bpf_program__get_private(prog, (void **)&priv); + if (err || !priv) + goto out; + pev = &priv->pev; + + err = convert_perf_probe_events(pev, 1); + if (err < 0) { + pr_debug("bpf_probe: failed to convert perf probe events"); + goto out; + } + + err = apply_perf_probe_events(pev, 1); + if (err < 0) { + pr_debug("bpf_probe: failed to apply perf probe events"); + goto out; + } + } +out: + return err < 0 ? err : 0; +} + +#define EVENTS_WRITE_BUFSIZE 4096 +int bpf__unprobe(struct bpf_object *obj) +{ + int err, ret = 0; + struct bpf_program *prog; + struct bpf_prog_priv *priv; + + bpf_object__for_each_program(prog, obj) { + int i; + + err = bpf_program__get_private(prog, (void **)&priv); + if (err || !priv) + continue; + + for (i = 0; i < priv->pev.ntevs; i++) { + struct probe_trace_event *tev = &priv->pev.tevs[i]; + char name_buf[EVENTS_WRITE_BUFSIZE]; + struct strfilter *delfilter; + + snprintf(name_buf, EVENTS_WRITE_BUFSIZE, + "%s:%s", tev->group, tev->event); + name_buf[EVENTS_WRITE_BUFSIZE - 1] = '\0'; + + delfilter = strfilter__new(name_buf, NULL); + if (!delfilter) { + pr_debug("Failed to create filter for unprobing\n"); + ret = -ENOMEM; + continue; + } + + err = del_perf_probe_events(delfilter); + strfilter__delete(delfilter); + if (err) { + pr_debug("Failed to delete %s\n", name_buf); + ret = err; + continue; + } + } + } + return ret; +} + +#define bpf__strerror_head(err, buf, size) \ + char sbuf[STRERR_BUFSIZE], *emsg;\ + if (!size)\ + return 0;\ + if (err < 0)\ + err = -err;\ + emsg = strerror_r(err, sbuf, sizeof(sbuf));\ + switch (err) {\ + default:\ + scnprintf(buf, size, "%s", emsg);\ + break; + +#define bpf__strerror_entry(val, fmt...)\ + case val: {\ + scnprintf(buf, size, fmt);\ + break;\ + } + +#define bpf__strerror_end(buf, size)\ + }\ + buf[size - 1] = '\0'; + +int bpf__strerror_probe(struct bpf_object *obj __maybe_unused, + int err, char *buf, size_t size) +{ + bpf__strerror_head(err, buf, size); + bpf__strerror_entry(EEXIST, "Probe point exist. Try use 'perf probe -d \"*\"'"); + bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0\n"); + bpf__strerror_entry(ENOENT, "You need to check probing points in BPF file\n"); + bpf__strerror_end(buf, size); + return 0; } diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h index f402d7c8c288..b819622dc7ce 100644 --- a/tools/perf/util/bpf-loader.h +++ b/tools/perf/util/bpf-loader.h @@ -11,11 +11,18 @@ #include "debug.h" struct bpf_object; +#define PERF_BPF_PROBE_GROUP "perf_bpf_probe" #ifdef HAVE_LIBBPF_SUPPORT struct bpf_object *bpf__prepare_load(const char *filename); void bpf__clear(void); + +int bpf__probe(struct bpf_object *obj); +int bpf__unprobe(struct bpf_object *obj); +int bpf__strerror_probe(struct bpf_object *obj, int err, + char *buf, size_t size); + #else static inline struct bpf_object * bpf__prepare_load(const char *filename __maybe_unused) @@ -25,5 +32,28 @@ bpf__prepare_load(const char *filename __maybe_unused) } static inline void bpf__clear(void) { } + +static inline int bpf__probe(struct bpf_object *obj __maybe_unused) { return 0;} +static inline int bpf__unprobe(struct bpf_object *obj __maybe_unused) { return 0;} + +static inline int +__bpf_strerror(char *buf, size_t size) +{ + if (!size) + return 0; + strncpy(buf, + "ERROR: eBPF object loading is disabled during compiling.\n", + size); + buf[size - 1] = '\0'; + return 0; +} + +static inline int +bpf__strerror_probe(struct bpf_object *obj __maybe_unused, + int err __maybe_unused, + char *buf, size_t size) +{ + return __bpf_strerror(buf, size); +} #endif #endif diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index a9e1d79d17d7..10a946779f46 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -536,6 +536,7 @@ int parse_events_load_bpf_obj(struct parse_events_evlist *data, { int err; char errbuf[BUFSIZ]; + static bool registered_unprobe_atexit = false; if (IS_ERR(obj) || !obj) { snprintf(errbuf, sizeof(errbuf), @@ -544,6 +545,22 @@ int parse_events_load_bpf_obj(struct parse_events_evlist *data, goto errout; } + /* + * Register atexit handler before calling bpf__probe() so + * bpf__probe() don't need to unprobe probe points its already + * created when failure. + */ + if (!registered_unprobe_atexit) { + atexit(bpf__clear); + registered_unprobe_atexit = true; + } + + err = bpf__probe(obj); + if (err) { + bpf__strerror_probe(obj, err, errbuf, sizeof(errbuf)); + goto errout; + } + /* * Temporary add a dummy event here so we can check whether * basic bpf loader works. Following patches will replace From 1e5e3ee8ff3877db6943032b54a6ac21c095affd Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Wed, 14 Oct 2015 12:41:16 +0000 Subject: [PATCH 5/6] perf tools: Load eBPF object into kernel This patch utilizes bpf_object__load() provided by libbpf to load all objects into kernel. Committer notes: Testing it: When using an incorrect kernel version number, i.e., having this in your eBPF proggie: int _version __attribute__((section("version"), used)) = 0x40100; For a 4.3.0-rc6+ kernel, say, this happens and needs checking at event parsing time, to provide a better error report to the user: # perf record --event /tmp/foo.o sleep 1 libbpf: load bpf program failed: Invalid argument libbpf: -- BEGIN DUMP LOG --- libbpf: libbpf: -- END LOG -- libbpf: failed to load program 'fork=_do_fork' libbpf: failed to load object '/tmp/foo.o' event syntax error: '/tmp/foo.o' \___ Invalid argument: Are you root and runing a CONFIG_BPF_SYSCALL kernel? (add -v to see detail) Run 'perf list' for a list of valid events Usage: perf record [] [] or: perf record [] -- [] -e, --event event selector. use 'perf list' to list available events If we instead make it match, i.e. use 0x40300 on this v4.3.0-rc6+ kernel, the whole process goes thru: # perf record --event /tmp/foo.o -a usleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.202 MB perf.data ] # perf evlist -v /tmp/foo.o: type: 1, size: 112, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|CPU|PERIOD, disabled: 1, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1 # Signed-off-by: Wang Nan Tested-by: Arnaldo Carvalho de Melo Cc: Alexei Starovoitov Cc: Brendan Gregg Cc: Daniel Borkmann Cc: David Ahern Cc: He Kuang Cc: Jiri Olsa Cc: Kaixu Xia Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1444826502-49291-6-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-loader.c | 22 ++++++++++++++++++++++ tools/perf/util/bpf-loader.h | 11 +++++++++++ tools/perf/util/parse-events.c | 6 ++++++ 3 files changed, 39 insertions(+) diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index 56f6fe8cf318..727955858d00 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -243,6 +243,18 @@ int bpf__unprobe(struct bpf_object *obj) return ret; } +int bpf__load(struct bpf_object *obj) +{ + int err; + + err = bpf_object__load(obj); + if (err) { + pr_debug("bpf: load objects failed\n"); + return err; + } + return 0; +} + #define bpf__strerror_head(err, buf, size) \ char sbuf[STRERR_BUFSIZE], *emsg;\ if (!size)\ @@ -275,3 +287,13 @@ int bpf__strerror_probe(struct bpf_object *obj __maybe_unused, bpf__strerror_end(buf, size); return 0; } + +int bpf__strerror_load(struct bpf_object *obj __maybe_unused, + int err, char *buf, size_t size) +{ + bpf__strerror_head(err, buf, size); + bpf__strerror_entry(EINVAL, "%s: Are you root and runing a CONFIG_BPF_SYSCALL kernel?", + emsg) + bpf__strerror_end(buf, size); + return 0; +} diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h index b819622dc7ce..b091ceb19c48 100644 --- a/tools/perf/util/bpf-loader.h +++ b/tools/perf/util/bpf-loader.h @@ -23,6 +23,9 @@ int bpf__unprobe(struct bpf_object *obj); int bpf__strerror_probe(struct bpf_object *obj, int err, char *buf, size_t size); +int bpf__load(struct bpf_object *obj); +int bpf__strerror_load(struct bpf_object *obj, int err, + char *buf, size_t size); #else static inline struct bpf_object * bpf__prepare_load(const char *filename __maybe_unused) @@ -35,6 +38,7 @@ static inline void bpf__clear(void) { } static inline int bpf__probe(struct bpf_object *obj __maybe_unused) { return 0;} static inline int bpf__unprobe(struct bpf_object *obj __maybe_unused) { return 0;} +static inline int bpf__load(struct bpf_object *obj __maybe_unused) { return 0; } static inline int __bpf_strerror(char *buf, size_t size) @@ -55,5 +59,12 @@ bpf__strerror_probe(struct bpf_object *obj __maybe_unused, { return __bpf_strerror(buf, size); } + +static inline int bpf__strerror_load(struct bpf_object *obj __maybe_unused, + int err __maybe_unused, + char *buf, size_t size) +{ + return __bpf_strerror(buf, size); +} #endif #endif diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 10a946779f46..c3aabeb63e88 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -561,6 +561,12 @@ int parse_events_load_bpf_obj(struct parse_events_evlist *data, goto errout; } + err = bpf__load(obj); + if (err) { + bpf__strerror_load(obj, err, errbuf, sizeof(errbuf)); + goto errout; + } + /* * Temporary add a dummy event here so we can check whether * basic bpf loader works. Following patches will replace From 4edf30e39e6cff32390eaff6a1508969b3cd967b Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Wed, 14 Oct 2015 12:41:17 +0000 Subject: [PATCH 6/6] perf bpf: Collect perf_evsel in BPF object files This patch creates a 'struct perf_evsel' for every probe in a BPF object file(s) and fills 'struct evlist' with them. The previously introduced dummy event is now removed. After this patch, the following command: # perf record --event filter.o ls Can trace on each of the probes defined in filter.o. The core of this patch is bpf__foreach_tev(), which calls a callback function for each 'struct probe_trace_event' event for a bpf program with each associated file descriptors. The add_bpf_event() callback creates evsels by calling parse_events_add_tracepoint(). Since bpf-loader.c will not be built if libbpf is turned off, an empty bpf__foreach_tev() is defined in bpf-loader.h to avoid build errors. Committer notes: Before: # /tmp/oldperf record --event /tmp/foo.o -a usleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.198 MB perf.data ] # perf evlist /tmp/foo.o # perf evlist -v /tmp/foo.o: type: 1, size: 112, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|CPU|PERIOD, disabled: 1, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1 I.e. we create just the PERF_TYPE_SOFTWARE (type: 1), PERF_COUNT_SW_DUMMY(config 0x9) event, now, with this patch: # perf record --event /tmp/foo.o -a usleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.210 MB perf.data ] # perf evlist -v perf_bpf_probe:fork: type: 2, size: 112, config: 0x6bd, { sample_period, sample_freq }: 1, sample_type: IP|TID|TIME|CPU|PERIOD|RAW, disabled: 1, inherit: 1, mmap: 1, comm: 1, task: 1, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1 # We now have a PERF_TYPE_SOFTWARE (type: 1), but the config states 0x6bd, which is how, after setting up the event via the kprobes interface, the 'perf_bpf_probe:fork' event is accessible via the perf_event_open syscall. This is all transient, as soon as the 'perf record' session ends, these probes will go away. To see how it looks like, lets try doing a neverending session, one that expects a control+C to end: # perf record --event /tmp/foo.o -a So, with that in place, we can use 'perf probe' to see what is in place: # perf probe -l perf_bpf_probe:fork (on _do_fork@acme/git/linux/kernel/fork.c) We also can use debugfs: [root@felicio ~]# cat /sys/kernel/debug/tracing/kprobe_events p:perf_bpf_probe/fork _text+638512 Ok, now lets stop and see if we got some forks: [root@felicio linux]# perf record --event /tmp/foo.o -a ^C[ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.325 MB perf.data (111 samples) ] [root@felicio linux]# perf script sshd 1271 [003] 81797.507678: perf_bpf_probe:fork: (ffffffff8109be30) sshd 18309 [000] 81797.524917: perf_bpf_probe:fork: (ffffffff8109be30) sshd 18309 [001] 81799.381603: perf_bpf_probe:fork: (ffffffff8109be30) sshd 18309 [001] 81799.408635: perf_bpf_probe:fork: (ffffffff8109be30) Sure enough, we have 111 forks :-) Callchains seems to work as well: # perf report --stdio --no-child # To display the perf.data header info, please use --header/--header-only options. # # Total Lost Samples: 0 # # Samples: 562 of event 'perf_bpf_probe:fork' # Event count (approx.): 562 # # Overhead Command Shared Object Symbol # ........ ........ ................ ............ # 44.66% sh [kernel.vmlinux] [k] _do_fork | ---_do_fork entry_SYSCALL_64_fastpath __libc_fork make_child 26.16% make [kernel.vmlinux] [k] _do_fork # Signed-off-by: Wang Nan Tested-by: Arnaldo Carvalho de Melo Cc: Alexei Starovoitov Cc: Brendan Gregg Cc: Daniel Borkmann Cc: David Ahern Cc: He Kuang Cc: Jiri Olsa Cc: Kaixu Xia Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1444826502-49291-7-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-loader.c | 40 ++++++++++++++++++++++++++ tools/perf/util/bpf-loader.h | 14 +++++++++ tools/perf/util/parse-events.c | 52 +++++++++++++++++++++++++++++----- 3 files changed, 99 insertions(+), 7 deletions(-) diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index 727955858d00..aa784a498c48 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -255,6 +255,46 @@ int bpf__load(struct bpf_object *obj) return 0; } +int bpf__foreach_tev(struct bpf_object *obj, + bpf_prog_iter_callback_t func, + void *arg) +{ + struct bpf_program *prog; + int err; + + bpf_object__for_each_program(prog, obj) { + struct probe_trace_event *tev; + struct perf_probe_event *pev; + struct bpf_prog_priv *priv; + int i, fd; + + err = bpf_program__get_private(prog, + (void **)&priv); + if (err || !priv) { + pr_debug("bpf: failed to get private field\n"); + return -EINVAL; + } + + pev = &priv->pev; + for (i = 0; i < pev->ntevs; i++) { + tev = &pev->tevs[i]; + + fd = bpf_program__fd(prog); + if (fd < 0) { + pr_debug("bpf: failed to get file descriptor\n"); + return fd; + } + + err = (*func)(tev, fd, arg); + if (err) { + pr_debug("bpf: call back failed, stop iterate\n"); + return err; + } + } + } + return 0; +} + #define bpf__strerror_head(err, buf, size) \ char sbuf[STRERR_BUFSIZE], *emsg;\ if (!size)\ diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h index b091ceb19c48..a8f25ee06fc5 100644 --- a/tools/perf/util/bpf-loader.h +++ b/tools/perf/util/bpf-loader.h @@ -8,11 +8,15 @@ #include #include #include +#include "probe-event.h" #include "debug.h" struct bpf_object; #define PERF_BPF_PROBE_GROUP "perf_bpf_probe" +typedef int (*bpf_prog_iter_callback_t)(struct probe_trace_event *tev, + int fd, void *arg); + #ifdef HAVE_LIBBPF_SUPPORT struct bpf_object *bpf__prepare_load(const char *filename); @@ -26,6 +30,8 @@ int bpf__strerror_probe(struct bpf_object *obj, int err, int bpf__load(struct bpf_object *obj); int bpf__strerror_load(struct bpf_object *obj, int err, char *buf, size_t size); +int bpf__foreach_tev(struct bpf_object *obj, + bpf_prog_iter_callback_t func, void *arg); #else static inline struct bpf_object * bpf__prepare_load(const char *filename __maybe_unused) @@ -40,6 +46,14 @@ static inline int bpf__probe(struct bpf_object *obj __maybe_unused) { return 0;} static inline int bpf__unprobe(struct bpf_object *obj __maybe_unused) { return 0;} static inline int bpf__load(struct bpf_object *obj __maybe_unused) { return 0; } +static inline int +bpf__foreach_tev(struct bpf_object *obj __maybe_unused, + bpf_prog_iter_callback_t func __maybe_unused, + void *arg __maybe_unused) +{ + return 0; +} + static inline int __bpf_strerror(char *buf, size_t size) { diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index c3aabeb63e88..d97b03710331 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -530,12 +530,49 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx, return ret; } +struct __add_bpf_event_param { + struct parse_events_evlist *data; + struct list_head *list; +}; + +static int add_bpf_event(struct probe_trace_event *tev, int fd, + void *_param) +{ + LIST_HEAD(new_evsels); + struct __add_bpf_event_param *param = _param; + struct parse_events_evlist *evlist = param->data; + struct list_head *list = param->list; + int err; + + pr_debug("add bpf event %s:%s and attach bpf program %d\n", + tev->group, tev->event, fd); + + err = parse_events_add_tracepoint(&new_evsels, &evlist->idx, tev->group, + tev->event, evlist->error, NULL); + if (err) { + struct perf_evsel *evsel, *tmp; + + pr_debug("Failed to add BPF event %s:%s\n", + tev->group, tev->event); + list_for_each_entry_safe(evsel, tmp, &new_evsels, node) { + list_del(&evsel->node); + perf_evsel__delete(evsel); + } + return err; + } + pr_debug("adding %s:%s\n", tev->group, tev->event); + + list_splice(&new_evsels, list); + return 0; +} + int parse_events_load_bpf_obj(struct parse_events_evlist *data, struct list_head *list, struct bpf_object *obj) { int err; char errbuf[BUFSIZ]; + struct __add_bpf_event_param param = {data, list}; static bool registered_unprobe_atexit = false; if (IS_ERR(obj) || !obj) { @@ -567,13 +604,14 @@ int parse_events_load_bpf_obj(struct parse_events_evlist *data, goto errout; } - /* - * Temporary add a dummy event here so we can check whether - * basic bpf loader works. Following patches will replace - * dummy event by useful evsels. - */ - return parse_events_add_numeric(data, list, PERF_TYPE_SOFTWARE, - PERF_COUNT_SW_DUMMY, NULL); + err = bpf__foreach_tev(obj, add_bpf_event, ¶m); + if (err) { + snprintf(errbuf, sizeof(errbuf), + "Attach events in BPF object failed"); + goto errout; + } + + return 0; errout: data->error->help = strdup("(add -v to see detail)"); data->error->str = strdup(errbuf);