mirror of https://gitee.com/openkylin/linux.git
perf/ebpf basic integration
Please see the changeset comments, but this is the very basic integration of perf with libbpf that, given a .o file built for the 'bpf' target with clang, will get it validated and loaded into the kernel via the sys_bpf syscall, which can be seen using 'perf trace' to trace the whole thing looking just for the bpf and perf_event_open syscalls: # perf trace -e bpf,perf_event_open perf record -g --event /tmp/foo.o -a 362.779 ( 0.129 ms): perf/22408 bpf(cmd: 5, uattr: 0x7ffd4edb6db0, size: 48 ) = 3 384.192 ( 0.016 ms): perf/22408 perf_event_open(attr_uptr: 0x7ffd4edbace0, pid: -1, cpu: 3, group_fd: -1, flags: FD_CLOEXEC) = 5 384.247 ( 0.038 ms): perf/22408 perf_event_open(attr_uptr: 0x37aedd8, pid: -1, group_fd: -1, flags: FD_CLOEXEC) = 5 384.261 ( 0.007 ms): perf/22408 perf_event_open(attr_uptr: 0x37aedd8, pid: -1, group_fd: -1, flags: FD_CLOEXEC) = 5 387.680 ( 3.413 ms): perf/22408 perf_event_open(attr_uptr: 0x3222f08, pid: -1, group_fd: -1, flags: FD_CLOEXEC) = 5 387.688 ( 0.005 ms): perf/22408 perf_event_open(attr_uptr: 0x3222f08, pid: -1, cpu: 1, group_fd: -1, flags: FD_CLOEXEC) = 6 387.693 ( 0.004 ms): perf/22408 perf_event_open(attr_uptr: 0x3222f08, pid: -1, cpu: 2, group_fd: -1, flags: FD_CLOEXEC) = 7 387.698 ( 0.003 ms): perf/22408 perf_event_open(attr_uptr: 0x3222f08, pid: -1, cpu: 3, group_fd: -1, flags: FD_CLOEXEC) = 8 ^C[ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.221 MB perf.data (2 samples) ] # perf script bash 18389 [002] 83446.412607: perf_bpf_probe:fork: (ffffffff8109be30) 29be31 _do_fork (/lib/modules/4.3.0-rc6+/build/vmlinux) 96d662 tracesys_phase2 (/lib/modules/4.3.0-rc6+/build/vmlinux) bd56c __libc_fork (/usr/lib64/libc-2.17.so) 413b2 make_child (/usr/bin/bash) bash 18389 [002] 83447.227255: perf_bpf_probe:fork: (ffffffff8109be30) 29be31 _do_fork (/lib/modules/4.3.0-rc6+/build/vmlinux) 96d662 tracesys_phase2 (/lib/modules/4.3.0-rc6+/build/vmlinux) bd56c __libc_fork (/usr/lib64/libc-2.17.so) 413b2 make_child (/usr/bin/bash) # perf evlist -v perf_bpf_probe:fork: type: 2, size: 112, config: 0x6cf, { sample_period, sample_freq }: 1, sample_type: IP|TID|TIME|CALLCHAIN|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 # More work is about to be reviewed, tested and merged that will allow the whole process of going from a .c file to an .o file via clang, etc to be done automagically. (Wang Nan) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWMPzEAAoJENZQFvNTUqpAib4P/Ronv5SdIHxT1OfVZ6GiK9oG b+K72dAFcOZVg0aSRIe0s9jczKEomJBkT8H7JG5JOPGD94E/asWnItjuS9EwTaQG E2m/+OMwb0LhL4OeF7KH4YBAhNFMpBzlIO+IgHky8FNwuk2sa/dgPK8Xav3NLHzn yNunz+282RcSt9XE06Pm/tMCkcMQIvPbjRcXV5McA0zJYkCZDCYDKl3i11ypJY3K 7t5sdz2Rau0wwG5XEsr5ZxQB4jphlzeYA+5YxiYxFigHlg/nSskzrwwSwxzQBSJb tHVBV9GQYVj2KEqS60kO4lNNTUPdhF92GNh3GSKa/laxtUYu+fBm224oz3cES86B oh8B8B05eSyj9WM4u23TSHir7Z8ppbtzTVfBXJNDO63dMZ/EhlL8r6uzfKNB3zM2 aXMyUfcrF9wK8wnds4MA9VGwmkkXx3ailOH5zIskTNSzU5FK6WWAPptNFI1ykxJb 56poai9g5F1VqE5X90yHgzRFC/Vc6GC4KuOjtw9ixzPZ6zA8TLYrDEtIeP5eZoC6 ZaI8jF6+8+nOVoDwsYIoEYE+IFi/zhmpOOQ0eNoAv2+o6FaPJ7tSNmDMMcgE1CQN x9jVvAb3eVFuh5dLhLiCPxkE7qv8kAARt2qNncscMBTEVRJ3mNGSvjQcYB5ScOnp /W6no1caIEPGr/AZIXch =xGPO -----END PGP SIGNATURE----- Merge tag 'perf-ebpf-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull basic perf/ebpf integration: "Please see the changeset comments, but this is the very basic integration of perf with libbpf that, given a .o file built for the 'bpf' target with clang, will get it validated and loaded into the kernel via the sys_bpf syscall, which can be seen using 'perf trace' to trace the whole thing looking just for the bpf and perf_event_open syscalls: # perf trace -e bpf,perf_event_open perf record -g --event /tmp/foo.o -a 362.779 ( 0.129 ms): perf/22408 bpf(cmd: 5, uattr: 0x7ffd4edb6db0, size: 48 ) = 3 384.192 ( 0.016 ms): perf/22408 perf_event_open(attr_uptr: 0x7ffd4edbace0, pid: -1, cpu: 3, group_fd: -1, flags: FD_CLOEXEC) = 5 384.247 ( 0.038 ms): perf/22408 perf_event_open(attr_uptr: 0x37aedd8, pid: -1, group_fd: -1, flags: FD_CLOEXEC) = 5 384.261 ( 0.007 ms): perf/22408 perf_event_open(attr_uptr: 0x37aedd8, pid: -1, group_fd: -1, flags: FD_CLOEXEC) = 5 387.680 ( 3.413 ms): perf/22408 perf_event_open(attr_uptr: 0x3222f08, pid: -1, group_fd: -1, flags: FD_CLOEXEC) = 5 387.688 ( 0.005 ms): perf/22408 perf_event_open(attr_uptr: 0x3222f08, pid: -1, cpu: 1, group_fd: -1, flags: FD_CLOEXEC) = 6 387.693 ( 0.004 ms): perf/22408 perf_event_open(attr_uptr: 0x3222f08, pid: -1, cpu: 2, group_fd: -1, flags: FD_CLOEXEC) = 7 387.698 ( 0.003 ms): perf/22408 perf_event_open(attr_uptr: 0x3222f08, pid: -1, cpu: 3, group_fd: -1, flags: FD_CLOEXEC) = 8 ^C[ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.221 MB perf.data (2 samples) ] # perf script bash 18389 [002] 83446.412607: perf_bpf_probe:fork: (ffffffff8109be30) 29be31 _do_fork (/lib/modules/4.3.0-rc6+/build/vmlinux) 96d662 tracesys_phase2 (/lib/modules/4.3.0-rc6+/build/vmlinux) bd56c __libc_fork (/usr/lib64/libc-2.17.so) 413b2 make_child (/usr/bin/bash) bash 18389 [002] 83447.227255: perf_bpf_probe:fork: (ffffffff8109be30) 29be31 _do_fork (/lib/modules/4.3.0-rc6+/build/vmlinux) 96d662 tracesys_phase2 (/lib/modules/4.3.0-rc6+/build/vmlinux) bd56c __libc_fork (/usr/lib64/libc-2.17.so) 413b2 make_child (/usr/bin/bash) # perf evlist -v perf_bpf_probe:fork: type: 2, size: 112, config: 0x6cf, { sample_period, sample_freq }: 1, sample_type: IP|TID|TIME|CALLCHAIN|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 # More work is about to be reviewed, tested and merged that will allow the whole process of going from a .c file to an .o file via clang, etc to be done automagically. (Wang Nan)" Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
66a565c203
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 <api/fs/tracing_path.h>
|
||||
#include <pthread.h>
|
||||
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,339 @@
|
|||
/*
|
||||
* bpf-loader.c
|
||||
*
|
||||
* Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
|
||||
* Copyright (C) 2015 Huawei Inc.
|
||||
*/
|
||||
|
||||
#include <bpf/libbpf.h>
|
||||
#include <linux/err.h>
|
||||
#include "perf.h"
|
||||
#include "debug.h"
|
||||
#include "bpf-loader.h"
|
||||
#include "probe-event.h"
|
||||
#include "probe-finder.h" // for MAX_PROBES
|
||||
|
||||
#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_prog_priv {
|
||||
struct perf_probe_event pev;
|
||||
};
|
||||
|
||||
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__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;
|
||||
}
|
||||
|
||||
int bpf__load(struct bpf_object *obj)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bpf_object__load(obj);
|
||||
if (err) {
|
||||
pr_debug("bpf: load objects failed\n");
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bpf__foreach_tev(struct bpf_object *obj,
|
||||
bpf_prog_iter_callback_t func,
|
||||
void *arg)
|
||||
{
|
||||
struct bpf_program *prog;
|
||||
int err;
|
||||
|
||||
bpf_object__for_each_program(prog, obj) {
|
||||
struct probe_trace_event *tev;
|
||||
struct perf_probe_event *pev;
|
||||
struct bpf_prog_priv *priv;
|
||||
int i, fd;
|
||||
|
||||
err = bpf_program__get_private(prog,
|
||||
(void **)&priv);
|
||||
if (err || !priv) {
|
||||
pr_debug("bpf: failed to get private field\n");
|
||||
return -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)\
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (C) 2015, Wang Nan <wangnan0@huawei.com>
|
||||
* Copyright (C) 2015, Huawei Inc.
|
||||
*/
|
||||
#ifndef __BPF_LOADER_H
|
||||
#define __BPF_LOADER_H
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/err.h>
|
||||
#include <string.h>
|
||||
#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);
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
{
|
||||
pr_debug("ERROR: eBPF object loading is disabled during compiling.\n");
|
||||
return ERR_PTR(-ENOTSUP);
|
||||
}
|
||||
|
||||
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__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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
|
@ -11,6 +11,7 @@
|
|||
#include "symbol.h"
|
||||
#include "cache.h"
|
||||
#include "header.h"
|
||||
#include "bpf-loader.h"
|
||||
#include "debug.h"
|
||||
#include <api/fs/tracing_path.h>
|
||||
#include "parse-events-bison.h"
|
||||
|
@ -529,6 +530,123 @@ 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) {
|
||||
snprintf(errbuf, sizeof(errbuf),
|
||||
"Internal error: load bpf obj with NULL");
|
||||
err = -EINVAL;
|
||||
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;
|
||||
}
|
||||
|
||||
err = bpf__load(obj);
|
||||
if (err) {
|
||||
bpf__strerror_load(obj, err, errbuf, sizeof(errbuf));
|
||||
goto errout;
|
||||
}
|
||||
|
||||
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);
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 '-'; }
|
||||
|
|
|
@ -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
|
||||
|
@ -70,6 +72,7 @@ static inc_group_count(struct list_head *list,
|
|||
%type <tracepoint_name> tracepoint_name
|
||||
%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
|
||||
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue