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:
Ingo Molnar 2015-10-29 13:17:56 +01:00
commit 66a565c203
13 changed files with 619 additions and 7 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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, &param);
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)
{

View File

@ -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,

View File

@ -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 '-'; }

View File

@ -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;