mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-28 03:58:02 +07:00
perf/ebpf library + llvm/clang infrastructure
Infrastructure: - library for interfacing with the kernel eBPF infrastructure, with tools/perf/ targeted as a first user (Wang Nan) - llvm/clang infrastructure for building BPF object files from C source code (Wang Nan) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJVxMUTAAoJENZQFvNTUqpA0qIP/3gQOMyvc7JFEQV0xOBQSlZ2 qEwR0gXpHqrEhiQ8jYPAaY+Ei8moWohF6DOg3ein75kmsGJDP2rfr0d/6U0K5S7t 3tkxA7QWhEYOqAo/UDfc5WygQF794w8UQxApzox6DnrUjeeJ0VfdkZU7fpnwQS5Z K4hJjWdg9lxvJ+QJM3DgxqLNlyI8TfalQGnWmKS5m0184kBDaXz/MtEQKCRB5X5S khw/VW+F4Er1mDGmzwZrzHRuXJJcZsLCCz4jIERlKe7a7vUHaH5KPM2OSqlf0asK 62o1EVNNpCszfJKa69BjA/vvtiaWKdWkfBko6+C+ARYRTqOXoPR6X10QsneVrwXM DZ8dkgSdzgE9CbbAa+Fo46i+u3TPoDQfk/LB1ZIzy0BD+gwXZcdnVZLBLK+zMziI IsKidf/aHykgaKfjXUmeocO/LUnvsoEcYEqkLsoT45br38p7OQ/0DWfguSFNuVpR 1onyuYEa8vHD4vjgjlUKV8UEoL11lEKLcUPvK/z2pQwUQnlrdmVx1V7dGNYGCiTI uhvjHYaMCifKKn52LdnjkYZqyfIqBLBwlAu49i0ONCIfYiJ+IoIEdBIXbog3mThC JI60I1VsWp2PyUoQ8E93T8AQTMZs8umzKr1LX+/5pZBTXKL+26ZsC7qwcSExom3n erfehyibJDo/4Gqj1CEx =E+oR -----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 perf/ebpf library + llvm/clang infrastructure changes from Arnaldo Carvalho de Melo: Infrastructure changes: - Add library for interfacing with the kernel eBPF infrastructure, with tools/perf/ targeted as a first user. (Wang Nan) - Add llvm/clang infrastructure for building BPF object files from C source code. (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
f1d800bf61
@ -33,7 +33,8 @@ FILES= \
|
||||
test-compile-32.bin \
|
||||
test-compile-x32.bin \
|
||||
test-zlib.bin \
|
||||
test-lzma.bin
|
||||
test-lzma.bin \
|
||||
test-bpf.bin
|
||||
|
||||
CC := $(CROSS_COMPILE)gcc -MD
|
||||
PKG_CONFIG := $(CROSS_COMPILE)pkg-config
|
||||
@ -156,6 +157,9 @@ test-zlib.bin:
|
||||
test-lzma.bin:
|
||||
$(BUILD) -llzma
|
||||
|
||||
test-bpf.bin:
|
||||
$(BUILD)
|
||||
|
||||
-include *.d
|
||||
|
||||
###############################
|
||||
|
18
tools/build/feature/test-bpf.c
Normal file
18
tools/build/feature/test-bpf.c
Normal file
@ -0,0 +1,18 @@
|
||||
#include <linux/bpf.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
attr.prog_type = BPF_PROG_TYPE_KPROBE;
|
||||
attr.insn_cnt = 0;
|
||||
attr.insns = 0;
|
||||
attr.license = 0;
|
||||
attr.log_buf = 0;
|
||||
attr.log_size = 0;
|
||||
attr.log_level = 0;
|
||||
attr.kern_version = 0;
|
||||
|
||||
attr = attr;
|
||||
return 0;
|
||||
}
|
2
tools/lib/bpf/.gitignore
vendored
Normal file
2
tools/lib/bpf/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
libbpf_version.h
|
||||
FEATURE-DUMP
|
1
tools/lib/bpf/Build
Normal file
1
tools/lib/bpf/Build
Normal file
@ -0,0 +1 @@
|
||||
libbpf-y := libbpf.o bpf.o
|
195
tools/lib/bpf/Makefile
Normal file
195
tools/lib/bpf/Makefile
Normal file
@ -0,0 +1,195 @@
|
||||
# Most of this file is copied from tools/lib/traceevent/Makefile
|
||||
|
||||
BPF_VERSION = 0
|
||||
BPF_PATCHLEVEL = 0
|
||||
BPF_EXTRAVERSION = 1
|
||||
|
||||
MAKEFLAGS += --no-print-directory
|
||||
|
||||
|
||||
# Makefiles suck: This macro sets a default value of $(2) for the
|
||||
# variable named by $(1), unless the variable has been set by
|
||||
# environment or command line. This is necessary for CC and AR
|
||||
# because make sets default values, so the simpler ?= approach
|
||||
# won't work as expected.
|
||||
define allow-override
|
||||
$(if $(or $(findstring environment,$(origin $(1))),\
|
||||
$(findstring command line,$(origin $(1)))),,\
|
||||
$(eval $(1) = $(2)))
|
||||
endef
|
||||
|
||||
# Allow setting CC and AR, or setting CROSS_COMPILE as a prefix.
|
||||
$(call allow-override,CC,$(CROSS_COMPILE)gcc)
|
||||
$(call allow-override,AR,$(CROSS_COMPILE)ar)
|
||||
|
||||
INSTALL = install
|
||||
|
||||
# Use DESTDIR for installing into a different root directory.
|
||||
# This is useful for building a package. The program will be
|
||||
# installed in this directory as if it was the root directory.
|
||||
# Then the build tool can move it later.
|
||||
DESTDIR ?=
|
||||
DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))'
|
||||
|
||||
LP64 := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1)
|
||||
ifeq ($(LP64), 1)
|
||||
libdir_relative = lib64
|
||||
else
|
||||
libdir_relative = lib
|
||||
endif
|
||||
|
||||
prefix ?= /usr/local
|
||||
libdir = $(prefix)/$(libdir_relative)
|
||||
man_dir = $(prefix)/share/man
|
||||
man_dir_SQ = '$(subst ','\'',$(man_dir))'
|
||||
|
||||
export man_dir man_dir_SQ INSTALL
|
||||
export DESTDIR DESTDIR_SQ
|
||||
|
||||
include ../../scripts/Makefile.include
|
||||
|
||||
# copy a bit from Linux kbuild
|
||||
|
||||
ifeq ("$(origin V)", "command line")
|
||||
VERBOSE = $(V)
|
||||
endif
|
||||
ifndef VERBOSE
|
||||
VERBOSE = 0
|
||||
endif
|
||||
|
||||
ifeq ($(srctree),)
|
||||
srctree := $(patsubst %/,%,$(dir $(shell pwd)))
|
||||
srctree := $(patsubst %/,%,$(dir $(srctree)))
|
||||
srctree := $(patsubst %/,%,$(dir $(srctree)))
|
||||
#$(info Determined 'srctree' to be $(srctree))
|
||||
endif
|
||||
|
||||
FEATURE_DISPLAY = libelf libelf-getphdrnum libelf-mmap bpf
|
||||
FEATURE_TESTS = libelf bpf
|
||||
|
||||
INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/arch/$(ARCH)/include/uapi -I$(srctree)/include/uapi
|
||||
FEATURE_CHECK_CFLAGS-bpf = $(INCLUDES)
|
||||
|
||||
include $(srctree)/tools/build/Makefile.feature
|
||||
|
||||
export prefix libdir src obj
|
||||
|
||||
# Shell quotes
|
||||
libdir_SQ = $(subst ','\'',$(libdir))
|
||||
libdir_relative_SQ = $(subst ','\'',$(libdir_relative))
|
||||
plugin_dir_SQ = $(subst ','\'',$(plugin_dir))
|
||||
|
||||
LIB_FILE = libbpf.a libbpf.so
|
||||
|
||||
VERSION = $(BPF_VERSION)
|
||||
PATCHLEVEL = $(BPF_PATCHLEVEL)
|
||||
EXTRAVERSION = $(BPF_EXTRAVERSION)
|
||||
|
||||
OBJ = $@
|
||||
N =
|
||||
|
||||
LIBBPF_VERSION = $(BPF_VERSION).$(BPF_PATCHLEVEL).$(BPF_EXTRAVERSION)
|
||||
|
||||
# Set compile option CFLAGS
|
||||
ifdef EXTRA_CFLAGS
|
||||
CFLAGS := $(EXTRA_CFLAGS)
|
||||
else
|
||||
CFLAGS := -g -Wall
|
||||
endif
|
||||
|
||||
ifeq ($(feature-libelf-mmap), 1)
|
||||
override CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT
|
||||
endif
|
||||
|
||||
ifeq ($(feature-libelf-getphdrnum), 1)
|
||||
override CFLAGS += -DHAVE_ELF_GETPHDRNUM_SUPPORT
|
||||
endif
|
||||
|
||||
# Append required CFLAGS
|
||||
override CFLAGS += $(EXTRA_WARNINGS)
|
||||
override CFLAGS += -Werror -Wall
|
||||
override CFLAGS += -fPIC
|
||||
override CFLAGS += $(INCLUDES)
|
||||
|
||||
ifeq ($(VERBOSE),1)
|
||||
Q =
|
||||
else
|
||||
Q = @
|
||||
endif
|
||||
|
||||
# Disable command line variables (CFLAGS) overide from top
|
||||
# level Makefile (perf), otherwise build Makefile will get
|
||||
# the same command line setup.
|
||||
MAKEOVERRIDES=
|
||||
|
||||
export srctree OUTPUT CC LD CFLAGS V
|
||||
build := -f $(srctree)/tools/build/Makefile.build dir=. obj
|
||||
|
||||
BPF_IN := $(OUTPUT)libbpf-in.o
|
||||
LIB_FILE := $(addprefix $(OUTPUT),$(LIB_FILE))
|
||||
|
||||
CMD_TARGETS = $(LIB_FILE)
|
||||
|
||||
TARGETS = $(CMD_TARGETS)
|
||||
|
||||
all: $(VERSION_FILES) all_cmd
|
||||
|
||||
all_cmd: $(CMD_TARGETS)
|
||||
|
||||
$(BPF_IN): force elfdep bpfdep
|
||||
$(Q)$(MAKE) $(build)=libbpf
|
||||
|
||||
$(OUTPUT)libbpf.so: $(BPF_IN)
|
||||
$(QUIET_LINK)$(CC) --shared $^ -o $@
|
||||
|
||||
$(OUTPUT)libbpf.a: $(BPF_IN)
|
||||
$(QUIET_LINK)$(RM) $@; $(AR) rcs $@ $^
|
||||
|
||||
define update_dir
|
||||
(echo $1 > $@.tmp; \
|
||||
if [ -r $@ ] && cmp -s $@ $@.tmp; then \
|
||||
rm -f $@.tmp; \
|
||||
else \
|
||||
echo ' UPDATE $@'; \
|
||||
mv -f $@.tmp $@; \
|
||||
fi);
|
||||
endef
|
||||
|
||||
define do_install
|
||||
if [ ! -d '$(DESTDIR_SQ)$2' ]; then \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \
|
||||
fi; \
|
||||
$(INSTALL) $1 '$(DESTDIR_SQ)$2'
|
||||
endef
|
||||
|
||||
install_lib: all_cmd
|
||||
$(call QUIET_INSTALL, $(LIB_FILE)) \
|
||||
$(call do_install,$(LIB_FILE),$(libdir_SQ))
|
||||
|
||||
install: install_lib
|
||||
|
||||
### Cleaning rules
|
||||
|
||||
config-clean:
|
||||
$(call QUIET_CLEAN, config)
|
||||
$(Q)$(MAKE) -C $(srctree)/tools/build/feature/ clean >/dev/null
|
||||
|
||||
clean:
|
||||
$(call QUIET_CLEAN, libbpf) $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d \
|
||||
$(RM) LIBBPF-CFLAGS
|
||||
$(call QUIET_CLEAN, core-gen) $(RM) $(OUTPUT)FEATURE-DUMP
|
||||
|
||||
|
||||
|
||||
PHONY += force elfdep bpfdep
|
||||
force:
|
||||
|
||||
elfdep:
|
||||
@if [ "$(feature-libelf)" != "1" ]; then echo "No libelf found"; exit -1 ; fi
|
||||
|
||||
bpfdep:
|
||||
@if [ "$(feature-bpf)" != "1" ]; then echo "BPF API too old"; exit -1 ; fi
|
||||
|
||||
# Declare the contents of the .PHONY variable as phony. We keep that
|
||||
# information in a variable so we can use it in if_changed and friends.
|
||||
.PHONY: $(PHONY)
|
85
tools/lib/bpf/bpf.c
Normal file
85
tools/lib/bpf/bpf.c
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* common eBPF ELF operations.
|
||||
*
|
||||
* Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
|
||||
* Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
|
||||
* Copyright (C) 2015 Huawei Inc.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <unistd.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <linux/bpf.h>
|
||||
#include "bpf.h"
|
||||
|
||||
/*
|
||||
* When building perf, unistd.h is override. Define __NR_bpf is
|
||||
* required to be defined.
|
||||
*/
|
||||
#ifndef __NR_bpf
|
||||
# if defined(__i386__)
|
||||
# define __NR_bpf 357
|
||||
# elif defined(__x86_64__)
|
||||
# define __NR_bpf 321
|
||||
# elif defined(__aarch64__)
|
||||
# define __NR_bpf 280
|
||||
# else
|
||||
# error __NR_bpf not defined. libbpf does not support your arch.
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static __u64 ptr_to_u64(void *ptr)
|
||||
{
|
||||
return (__u64) (unsigned long) ptr;
|
||||
}
|
||||
|
||||
static int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
|
||||
unsigned int size)
|
||||
{
|
||||
return syscall(__NR_bpf, cmd, attr, size);
|
||||
}
|
||||
|
||||
int bpf_create_map(enum bpf_map_type map_type, int key_size,
|
||||
int value_size, int max_entries)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
memset(&attr, '\0', sizeof(attr));
|
||||
|
||||
attr.map_type = map_type;
|
||||
attr.key_size = key_size;
|
||||
attr.value_size = value_size;
|
||||
attr.max_entries = max_entries;
|
||||
|
||||
return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns,
|
||||
size_t insns_cnt, char *license,
|
||||
u32 kern_version, char *log_buf, size_t log_buf_sz)
|
||||
{
|
||||
int fd;
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.prog_type = type;
|
||||
attr.insn_cnt = (__u32)insns_cnt;
|
||||
attr.insns = ptr_to_u64(insns);
|
||||
attr.license = ptr_to_u64(license);
|
||||
attr.log_buf = ptr_to_u64(NULL);
|
||||
attr.log_size = 0;
|
||||
attr.log_level = 0;
|
||||
attr.kern_version = kern_version;
|
||||
|
||||
fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
|
||||
if (fd >= 0 || !log_buf || !log_buf_sz)
|
||||
return fd;
|
||||
|
||||
/* Try again with log */
|
||||
attr.log_buf = ptr_to_u64(log_buf);
|
||||
attr.log_size = log_buf_sz;
|
||||
attr.log_level = 1;
|
||||
log_buf[0] = 0;
|
||||
return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
|
||||
}
|
23
tools/lib/bpf/bpf.h
Normal file
23
tools/lib/bpf/bpf.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* common eBPF ELF operations.
|
||||
*
|
||||
* Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
|
||||
* Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
|
||||
* Copyright (C) 2015 Huawei Inc.
|
||||
*/
|
||||
#ifndef __BPF_BPF_H
|
||||
#define __BPF_BPF_H
|
||||
|
||||
#include <linux/bpf.h>
|
||||
|
||||
int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
|
||||
int max_entries);
|
||||
|
||||
/* Recommend log buffer size */
|
||||
#define BPF_LOG_BUF_SIZE 65536
|
||||
int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns,
|
||||
size_t insns_cnt, char *license,
|
||||
u32 kern_version, char *log_buf,
|
||||
size_t log_buf_sz);
|
||||
|
||||
#endif
|
1037
tools/lib/bpf/libbpf.c
Normal file
1037
tools/lib/bpf/libbpf.c
Normal file
File diff suppressed because it is too large
Load Diff
81
tools/lib/bpf/libbpf.h
Normal file
81
tools/lib/bpf/libbpf.h
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Common eBPF ELF object loading operations.
|
||||
*
|
||||
* Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
|
||||
* Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
|
||||
* Copyright (C) 2015 Huawei Inc.
|
||||
*/
|
||||
#ifndef __BPF_LIBBPF_H
|
||||
#define __BPF_LIBBPF_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/*
|
||||
* In include/linux/compiler-gcc.h, __printf is defined. However
|
||||
* it should be better if libbpf.h doesn't depend on Linux header file.
|
||||
* So instead of __printf, here we use gcc attribute directly.
|
||||
*/
|
||||
typedef int (*libbpf_print_fn_t)(const char *, ...)
|
||||
__attribute__((format(printf, 1, 2)));
|
||||
|
||||
void libbpf_set_print(libbpf_print_fn_t warn,
|
||||
libbpf_print_fn_t info,
|
||||
libbpf_print_fn_t debug);
|
||||
|
||||
/* Hide internal to user */
|
||||
struct bpf_object;
|
||||
|
||||
struct bpf_object *bpf_object__open(const char *path);
|
||||
struct bpf_object *bpf_object__open_buffer(void *obj_buf,
|
||||
size_t obj_buf_sz);
|
||||
void bpf_object__close(struct bpf_object *object);
|
||||
|
||||
/* Load/unload object into/from kernel */
|
||||
int bpf_object__load(struct bpf_object *obj);
|
||||
int bpf_object__unload(struct bpf_object *obj);
|
||||
|
||||
struct bpf_object *bpf_object__next(struct bpf_object *prev);
|
||||
#define bpf_object__for_each_safe(pos, tmp) \
|
||||
for ((pos) = bpf_object__next(NULL), \
|
||||
(tmp) = bpf_object__next(pos); \
|
||||
(pos) != NULL; \
|
||||
(pos) = (tmp), (tmp) = bpf_object__next(tmp))
|
||||
|
||||
/* Accessors of bpf_program. */
|
||||
struct bpf_program;
|
||||
struct bpf_program *bpf_program__next(struct bpf_program *prog,
|
||||
struct bpf_object *obj);
|
||||
|
||||
#define bpf_object__for_each_program(pos, obj) \
|
||||
for ((pos) = bpf_program__next(NULL, (obj)); \
|
||||
(pos) != NULL; \
|
||||
(pos) = bpf_program__next((pos), (obj)))
|
||||
|
||||
typedef void (*bpf_program_clear_priv_t)(struct bpf_program *,
|
||||
void *);
|
||||
|
||||
int bpf_program__set_private(struct bpf_program *prog, void *priv,
|
||||
bpf_program_clear_priv_t clear_priv);
|
||||
|
||||
int bpf_program__get_private(struct bpf_program *prog,
|
||||
void **ppriv);
|
||||
|
||||
const char *bpf_program__title(struct bpf_program *prog, bool dup);
|
||||
|
||||
int bpf_program__fd(struct bpf_program *prog);
|
||||
|
||||
/*
|
||||
* We don't need __attribute__((packed)) now since it is
|
||||
* unnecessary for 'bpf_map_def' because they are all aligned.
|
||||
* In addition, using it will trigger -Wpacked warning message,
|
||||
* and will be treated as an error due to -Werror.
|
||||
*/
|
||||
struct bpf_map_def {
|
||||
unsigned int type;
|
||||
unsigned int key_size;
|
||||
unsigned int value_size;
|
||||
unsigned int max_entries;
|
||||
};
|
||||
|
||||
#endif
|
@ -18,6 +18,7 @@ tools/arch/x86/include/asm/atomic.h
|
||||
tools/arch/x86/include/asm/rmwcc.h
|
||||
tools/lib/traceevent
|
||||
tools/lib/api
|
||||
tools/lib/bpf
|
||||
tools/lib/hweight.c
|
||||
tools/lib/rbtree.c
|
||||
tools/lib/symbol/kallsyms.c
|
||||
|
@ -32,6 +32,7 @@ perf-y += sample-parsing.o
|
||||
perf-y += parse-no-sample-id-all.o
|
||||
perf-y += kmod-path.o
|
||||
perf-y += thread-map.o
|
||||
perf-y += llvm.o
|
||||
|
||||
perf-$(CONFIG_X86) += perf-time-to-tsc.o
|
||||
|
||||
|
@ -174,6 +174,10 @@ static struct test {
|
||||
.desc = "Test thread map",
|
||||
.func = test__thread_map,
|
||||
},
|
||||
{
|
||||
.desc = "Test LLVM searching and compiling",
|
||||
.func = test__llvm,
|
||||
},
|
||||
{
|
||||
.func = NULL,
|
||||
},
|
||||
|
98
tools/perf/tests/llvm.c
Normal file
98
tools/perf/tests/llvm.c
Normal file
@ -0,0 +1,98 @@
|
||||
#include <stdio.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include <util/llvm-utils.h>
|
||||
#include <util/cache.h>
|
||||
#include "tests.h"
|
||||
#include "debug.h"
|
||||
|
||||
static int perf_config_cb(const char *var, const char *val,
|
||||
void *arg __maybe_unused)
|
||||
{
|
||||
return perf_default_config(var, val, arg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Randomly give it a "version" section since we don't really load it
|
||||
* into kernel
|
||||
*/
|
||||
static const char test_bpf_prog[] =
|
||||
"__attribute__((section(\"do_fork\"), used)) "
|
||||
"int fork(void *ctx) {return 0;} "
|
||||
"char _license[] __attribute__((section(\"license\"), used)) = \"GPL\";"
|
||||
"int _version __attribute__((section(\"version\"), used)) = 0x40100;";
|
||||
|
||||
#ifdef HAVE_LIBBPF_SUPPORT
|
||||
static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz)
|
||||
{
|
||||
struct bpf_object *obj;
|
||||
|
||||
obj = bpf_object__open_buffer(obj_buf, obj_buf_sz);
|
||||
if (!obj)
|
||||
return -1;
|
||||
bpf_object__close(obj);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int test__bpf_parsing(void *obj_buf __maybe_unused,
|
||||
size_t obj_buf_sz __maybe_unused)
|
||||
{
|
||||
fprintf(stderr, " (skip bpf parsing)");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int test__llvm(void)
|
||||
{
|
||||
char *tmpl_new, *clang_opt_new;
|
||||
void *obj_buf;
|
||||
size_t obj_buf_sz;
|
||||
int err, old_verbose;
|
||||
|
||||
perf_config(perf_config_cb, NULL);
|
||||
|
||||
/*
|
||||
* Skip this test if user's .perfconfig doesn't set [llvm] section
|
||||
* and clang is not found in $PATH, and this is not perf test -v
|
||||
*/
|
||||
if (verbose == 0 && !llvm_param.user_set_param && llvm__search_clang()) {
|
||||
fprintf(stderr, " (no clang, try 'perf test -v LLVM')");
|
||||
return TEST_SKIP;
|
||||
}
|
||||
|
||||
old_verbose = verbose;
|
||||
/*
|
||||
* llvm is verbosity when error. Suppress all error output if
|
||||
* not 'perf test -v'.
|
||||
*/
|
||||
if (verbose == 0)
|
||||
verbose = -1;
|
||||
|
||||
if (!llvm_param.clang_bpf_cmd_template)
|
||||
return -1;
|
||||
|
||||
if (!llvm_param.clang_opt)
|
||||
llvm_param.clang_opt = strdup("");
|
||||
|
||||
err = asprintf(&tmpl_new, "echo '%s' | %s", test_bpf_prog,
|
||||
llvm_param.clang_bpf_cmd_template);
|
||||
if (err < 0)
|
||||
return -1;
|
||||
err = asprintf(&clang_opt_new, "-xc %s", llvm_param.clang_opt);
|
||||
if (err < 0)
|
||||
return -1;
|
||||
|
||||
llvm_param.clang_bpf_cmd_template = tmpl_new;
|
||||
llvm_param.clang_opt = clang_opt_new;
|
||||
err = llvm__compile_bpf("-", &obj_buf, &obj_buf_sz);
|
||||
|
||||
verbose = old_verbose;
|
||||
if (err) {
|
||||
if (!verbose)
|
||||
fprintf(stderr, " (use -v to see error message)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = test__bpf_parsing(obj_buf, obj_buf_sz);
|
||||
free(obj_buf);
|
||||
return err;
|
||||
}
|
@ -62,6 +62,7 @@ int test__fdarray__filter(void);
|
||||
int test__fdarray__add(void);
|
||||
int test__kmod_path__parse(void);
|
||||
int test__thread_map(void);
|
||||
int test__llvm(void);
|
||||
|
||||
#if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__)
|
||||
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
||||
|
@ -14,6 +14,7 @@ libperf-y += find_next_bit.o
|
||||
libperf-y += help.o
|
||||
libperf-y += kallsyms.o
|
||||
libperf-y += levenshtein.o
|
||||
libperf-y += llvm-utils.o
|
||||
libperf-y += parse-options.o
|
||||
libperf-y += parse-events.o
|
||||
libperf-y += path.o
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "cache.h"
|
||||
#include "exec_cmd.h"
|
||||
#include "util/hist.h" /* perf_hist_config */
|
||||
#include "util/llvm-utils.h" /* perf_llvm_config */
|
||||
|
||||
#define MAXNAME (256)
|
||||
|
||||
@ -408,6 +409,9 @@ int perf_default_config(const char *var, const char *value,
|
||||
if (!prefixcmp(var, "call-graph."))
|
||||
return perf_callchain_config(var, value);
|
||||
|
||||
if (!prefixcmp(var, "llvm."))
|
||||
return perf_llvm_config(var, value);
|
||||
|
||||
/* Add other config variables here. */
|
||||
return 0;
|
||||
}
|
||||
|
408
tools/perf/util/llvm-utils.c
Normal file
408
tools/perf/util/llvm-utils.c
Normal file
@ -0,0 +1,408 @@
|
||||
/*
|
||||
* Copyright (C) 2015, Wang Nan <wangnan0@huawei.com>
|
||||
* Copyright (C) 2015, Huawei Inc.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/utsname.h>
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
#include "llvm-utils.h"
|
||||
#include "cache.h"
|
||||
|
||||
#define CLANG_BPF_CMD_DEFAULT_TEMPLATE \
|
||||
"$CLANG_EXEC -D__KERNEL__ $CLANG_OPTIONS " \
|
||||
"$KERNEL_INC_OPTIONS -Wno-unused-value " \
|
||||
"-Wno-pointer-sign -working-directory " \
|
||||
"$WORKING_DIR -c \"$CLANG_SOURCE\" -target bpf -O2 -o -"
|
||||
|
||||
struct llvm_param llvm_param = {
|
||||
.clang_path = "clang",
|
||||
.clang_bpf_cmd_template = CLANG_BPF_CMD_DEFAULT_TEMPLATE,
|
||||
.clang_opt = NULL,
|
||||
.kbuild_dir = NULL,
|
||||
.kbuild_opts = NULL,
|
||||
.user_set_param = false,
|
||||
};
|
||||
|
||||
int perf_llvm_config(const char *var, const char *value)
|
||||
{
|
||||
if (prefixcmp(var, "llvm."))
|
||||
return 0;
|
||||
var += sizeof("llvm.") - 1;
|
||||
|
||||
if (!strcmp(var, "clang-path"))
|
||||
llvm_param.clang_path = strdup(value);
|
||||
else if (!strcmp(var, "clang-bpf-cmd-template"))
|
||||
llvm_param.clang_bpf_cmd_template = strdup(value);
|
||||
else if (!strcmp(var, "clang-opt"))
|
||||
llvm_param.clang_opt = strdup(value);
|
||||
else if (!strcmp(var, "kbuild-dir"))
|
||||
llvm_param.kbuild_dir = strdup(value);
|
||||
else if (!strcmp(var, "kbuild-opts"))
|
||||
llvm_param.kbuild_opts = strdup(value);
|
||||
else
|
||||
return -1;
|
||||
llvm_param.user_set_param = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
search_program(const char *def, const char *name,
|
||||
char *output)
|
||||
{
|
||||
char *env, *path, *tmp = NULL;
|
||||
char buf[PATH_MAX];
|
||||
int ret;
|
||||
|
||||
output[0] = '\0';
|
||||
if (def && def[0] != '\0') {
|
||||
if (def[0] == '/') {
|
||||
if (access(def, F_OK) == 0) {
|
||||
strlcpy(output, def, PATH_MAX);
|
||||
return 0;
|
||||
}
|
||||
} else if (def[0] != '\0')
|
||||
name = def;
|
||||
}
|
||||
|
||||
env = getenv("PATH");
|
||||
if (!env)
|
||||
return -1;
|
||||
env = strdup(env);
|
||||
if (!env)
|
||||
return -1;
|
||||
|
||||
ret = -ENOENT;
|
||||
path = strtok_r(env, ":", &tmp);
|
||||
while (path) {
|
||||
scnprintf(buf, sizeof(buf), "%s/%s", path, name);
|
||||
if (access(buf, F_OK) == 0) {
|
||||
strlcpy(output, buf, PATH_MAX);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
path = strtok_r(NULL, ":", &tmp);
|
||||
}
|
||||
|
||||
free(env);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define READ_SIZE 4096
|
||||
static int
|
||||
read_from_pipe(const char *cmd, void **p_buf, size_t *p_read_sz)
|
||||
{
|
||||
int err = 0;
|
||||
void *buf = NULL;
|
||||
FILE *file = NULL;
|
||||
size_t read_sz = 0, buf_sz = 0;
|
||||
|
||||
file = popen(cmd, "r");
|
||||
if (!file) {
|
||||
pr_err("ERROR: unable to popen cmd: %s\n",
|
||||
strerror(errno));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
while (!feof(file) && !ferror(file)) {
|
||||
/*
|
||||
* Make buf_sz always have obe byte extra space so we
|
||||
* can put '\0' there.
|
||||
*/
|
||||
if (buf_sz - read_sz < READ_SIZE + 1) {
|
||||
void *new_buf;
|
||||
|
||||
buf_sz = read_sz + READ_SIZE + 1;
|
||||
new_buf = realloc(buf, buf_sz);
|
||||
|
||||
if (!new_buf) {
|
||||
pr_err("ERROR: failed to realloc memory\n");
|
||||
err = -ENOMEM;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
buf = new_buf;
|
||||
}
|
||||
read_sz += fread(buf + read_sz, 1, READ_SIZE, file);
|
||||
}
|
||||
|
||||
if (buf_sz - read_sz < 1) {
|
||||
pr_err("ERROR: internal error\n");
|
||||
err = -EINVAL;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (ferror(file)) {
|
||||
pr_err("ERROR: error occurred when reading from pipe: %s\n",
|
||||
strerror(errno));
|
||||
err = -EIO;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
err = WEXITSTATUS(pclose(file));
|
||||
file = NULL;
|
||||
if (err) {
|
||||
err = -EINVAL;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/*
|
||||
* If buf is string, give it terminal '\0' to make our life
|
||||
* easier. If buf is not string, that '\0' is out of space
|
||||
* indicated by read_sz so caller won't even notice it.
|
||||
*/
|
||||
((char *)buf)[read_sz] = '\0';
|
||||
|
||||
if (!p_buf)
|
||||
free(buf);
|
||||
else
|
||||
*p_buf = buf;
|
||||
|
||||
if (p_read_sz)
|
||||
*p_read_sz = read_sz;
|
||||
return 0;
|
||||
|
||||
errout:
|
||||
if (file)
|
||||
pclose(file);
|
||||
free(buf);
|
||||
if (p_buf)
|
||||
*p_buf = NULL;
|
||||
if (p_read_sz)
|
||||
*p_read_sz = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline void
|
||||
force_set_env(const char *var, const char *value)
|
||||
{
|
||||
if (value) {
|
||||
setenv(var, value, 1);
|
||||
pr_debug("set env: %s=%s\n", var, value);
|
||||
} else {
|
||||
unsetenv(var);
|
||||
pr_debug("unset env: %s\n", var);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
version_notice(void)
|
||||
{
|
||||
pr_err(
|
||||
" \tLLVM 3.7 or newer is required. Which can be found from http://llvm.org\n"
|
||||
" \tYou may want to try git trunk:\n"
|
||||
" \t\tgit clone http://llvm.org/git/llvm.git\n"
|
||||
" \t\t and\n"
|
||||
" \t\tgit clone http://llvm.org/git/clang.git\n\n"
|
||||
" \tOr fetch the latest clang/llvm 3.7 from pre-built llvm packages for\n"
|
||||
" \tdebian/ubuntu:\n"
|
||||
" \t\thttp://llvm.org/apt\n\n"
|
||||
" \tIf you are using old version of clang, change 'clang-bpf-cmd-template'\n"
|
||||
" \toption in [llvm] section of ~/.perfconfig to:\n\n"
|
||||
" \t \"$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS \\\n"
|
||||
" \t -working-directory $WORKING_DIR -c $CLANG_SOURCE \\\n"
|
||||
" \t -emit-llvm -o - | /path/to/llc -march=bpf -filetype=obj -o -\"\n"
|
||||
" \t(Replace /path/to/llc with path to your llc)\n\n"
|
||||
);
|
||||
}
|
||||
|
||||
static int detect_kbuild_dir(char **kbuild_dir)
|
||||
{
|
||||
const char *test_dir = llvm_param.kbuild_dir;
|
||||
const char *prefix_dir = "";
|
||||
const char *suffix_dir = "";
|
||||
|
||||
char *autoconf_path;
|
||||
struct utsname utsname;
|
||||
|
||||
int err;
|
||||
|
||||
if (!test_dir) {
|
||||
err = uname(&utsname);
|
||||
if (err) {
|
||||
pr_warning("uname failed: %s\n", strerror(errno));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
test_dir = utsname.release;
|
||||
prefix_dir = "/lib/modules/";
|
||||
suffix_dir = "/build";
|
||||
}
|
||||
|
||||
err = asprintf(&autoconf_path, "%s%s%s/include/generated/autoconf.h",
|
||||
prefix_dir, test_dir, suffix_dir);
|
||||
if (err < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
if (access(autoconf_path, R_OK) == 0) {
|
||||
free(autoconf_path);
|
||||
|
||||
err = asprintf(kbuild_dir, "%s%s%s", prefix_dir, test_dir,
|
||||
suffix_dir);
|
||||
if (err < 0)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
free(autoconf_path);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static const char *kinc_fetch_script =
|
||||
"#!/usr/bin/env sh\n"
|
||||
"if ! test -d \"$KBUILD_DIR\"\n"
|
||||
"then\n"
|
||||
" exit -1\n"
|
||||
"fi\n"
|
||||
"if ! test -f \"$KBUILD_DIR/include/generated/autoconf.h\"\n"
|
||||
"then\n"
|
||||
" exit -1\n"
|
||||
"fi\n"
|
||||
"TMPDIR=`mktemp -d`\n"
|
||||
"if test -z \"$TMPDIR\"\n"
|
||||
"then\n"
|
||||
" exit -1\n"
|
||||
"fi\n"
|
||||
"cat << EOF > $TMPDIR/Makefile\n"
|
||||
"obj-y := dummy.o\n"
|
||||
"\\$(obj)/%.o: \\$(src)/%.c\n"
|
||||
"\t@echo -n \"\\$(NOSTDINC_FLAGS) \\$(LINUXINCLUDE) \\$(EXTRA_CFLAGS)\"\n"
|
||||
"EOF\n"
|
||||
"touch $TMPDIR/dummy.c\n"
|
||||
"make -s -C $KBUILD_DIR M=$TMPDIR $KBUILD_OPTS dummy.o 2>/dev/null\n"
|
||||
"RET=$?\n"
|
||||
"rm -rf $TMPDIR\n"
|
||||
"exit $RET\n";
|
||||
|
||||
static inline void
|
||||
get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!kbuild_dir || !kbuild_include_opts)
|
||||
return;
|
||||
|
||||
*kbuild_dir = NULL;
|
||||
*kbuild_include_opts = NULL;
|
||||
|
||||
if (llvm_param.kbuild_dir && !llvm_param.kbuild_dir[0]) {
|
||||
pr_debug("[llvm.kbuild-dir] is set to \"\" deliberately.\n");
|
||||
pr_debug("Skip kbuild options detection.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
err = detect_kbuild_dir(kbuild_dir);
|
||||
if (err) {
|
||||
pr_warning(
|
||||
"WARNING:\tunable to get correct kernel building directory.\n"
|
||||
"Hint:\tSet correct kbuild directory using 'kbuild-dir' option in [llvm]\n"
|
||||
" \tsection of ~/.perfconfig or set it to \"\" to suppress kbuild\n"
|
||||
" \tdetection.\n\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pr_debug("Kernel build dir is set to %s\n", *kbuild_dir);
|
||||
force_set_env("KBUILD_DIR", *kbuild_dir);
|
||||
force_set_env("KBUILD_OPTS", llvm_param.kbuild_opts);
|
||||
err = read_from_pipe(kinc_fetch_script,
|
||||
(void **)kbuild_include_opts,
|
||||
NULL);
|
||||
if (err) {
|
||||
pr_warning(
|
||||
"WARNING:\tunable to get kernel include directories from '%s'\n"
|
||||
"Hint:\tTry set clang include options using 'clang-bpf-cmd-template'\n"
|
||||
" \toption in [llvm] section of ~/.perfconfig and set 'kbuild-dir'\n"
|
||||
" \toption in [llvm] to \"\" to suppress this detection.\n\n",
|
||||
*kbuild_dir);
|
||||
|
||||
free(*kbuild_dir);
|
||||
*kbuild_dir = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
pr_debug("include option is set to %s\n", *kbuild_include_opts);
|
||||
}
|
||||
|
||||
int llvm__compile_bpf(const char *path, void **p_obj_buf,
|
||||
size_t *p_obj_buf_sz)
|
||||
{
|
||||
int err;
|
||||
char clang_path[PATH_MAX];
|
||||
const char *clang_opt = llvm_param.clang_opt;
|
||||
const char *template = llvm_param.clang_bpf_cmd_template;
|
||||
char *kbuild_dir = NULL, *kbuild_include_opts = NULL;
|
||||
void *obj_buf = NULL;
|
||||
size_t obj_buf_sz;
|
||||
|
||||
if (!template)
|
||||
template = CLANG_BPF_CMD_DEFAULT_TEMPLATE;
|
||||
|
||||
err = search_program(llvm_param.clang_path,
|
||||
"clang", clang_path);
|
||||
if (err) {
|
||||
pr_err(
|
||||
"ERROR:\tunable to find clang.\n"
|
||||
"Hint:\tTry to install latest clang/llvm to support BPF. Check your $PATH\n"
|
||||
" \tand 'clang-path' option in [llvm] section of ~/.perfconfig.\n");
|
||||
version_notice();
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is an optional work. Even it fail we can continue our
|
||||
* work. Needn't to check error return.
|
||||
*/
|
||||
get_kbuild_opts(&kbuild_dir, &kbuild_include_opts);
|
||||
|
||||
force_set_env("CLANG_EXEC", clang_path);
|
||||
force_set_env("CLANG_OPTIONS", clang_opt);
|
||||
force_set_env("KERNEL_INC_OPTIONS", kbuild_include_opts);
|
||||
force_set_env("WORKING_DIR", kbuild_dir ? : ".");
|
||||
|
||||
/*
|
||||
* Since we may reset clang's working dir, path of source file
|
||||
* should be transferred into absolute path, except we want
|
||||
* stdin to be source file (testing).
|
||||
*/
|
||||
force_set_env("CLANG_SOURCE",
|
||||
(path[0] == '-') ? path :
|
||||
make_nonrelative_path(path));
|
||||
|
||||
pr_debug("llvm compiling command template: %s\n", template);
|
||||
err = read_from_pipe(template, &obj_buf, &obj_buf_sz);
|
||||
if (err) {
|
||||
pr_err("ERROR:\tunable to compile %s\n", path);
|
||||
pr_err("Hint:\tCheck error message shown above.\n");
|
||||
pr_err("Hint:\tYou can also pre-compile it into .o using:\n");
|
||||
pr_err(" \t\tclang -target bpf -O2 -c %s\n", path);
|
||||
pr_err(" \twith proper -I and -D options.\n");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
free(kbuild_dir);
|
||||
free(kbuild_include_opts);
|
||||
if (!p_obj_buf)
|
||||
free(obj_buf);
|
||||
else
|
||||
*p_obj_buf = obj_buf;
|
||||
|
||||
if (p_obj_buf_sz)
|
||||
*p_obj_buf_sz = obj_buf_sz;
|
||||
return 0;
|
||||
errout:
|
||||
free(kbuild_dir);
|
||||
free(kbuild_include_opts);
|
||||
free(obj_buf);
|
||||
if (p_obj_buf)
|
||||
*p_obj_buf = NULL;
|
||||
if (p_obj_buf_sz)
|
||||
*p_obj_buf_sz = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
int llvm__search_clang(void)
|
||||
{
|
||||
char clang_path[PATH_MAX];
|
||||
|
||||
return search_program(llvm_param.clang_path, "clang", clang_path);
|
||||
}
|
49
tools/perf/util/llvm-utils.h
Normal file
49
tools/perf/util/llvm-utils.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2015, Wang Nan <wangnan0@huawei.com>
|
||||
* Copyright (C) 2015, Huawei Inc.
|
||||
*/
|
||||
#ifndef __LLVM_UTILS_H
|
||||
#define __LLVM_UTILS_H
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
struct llvm_param {
|
||||
/* Path of clang executable */
|
||||
const char *clang_path;
|
||||
/*
|
||||
* Template of clang bpf compiling. 5 env variables
|
||||
* can be used:
|
||||
* $CLANG_EXEC: Path to clang.
|
||||
* $CLANG_OPTIONS: Extra options to clang.
|
||||
* $KERNEL_INC_OPTIONS: Kernel include directories.
|
||||
* $WORKING_DIR: Kernel source directory.
|
||||
* $CLANG_SOURCE: Source file to be compiled.
|
||||
*/
|
||||
const char *clang_bpf_cmd_template;
|
||||
/* Will be filled in $CLANG_OPTIONS */
|
||||
const char *clang_opt;
|
||||
/* Where to find kbuild system */
|
||||
const char *kbuild_dir;
|
||||
/*
|
||||
* Arguments passed to make, like 'ARCH=arm' if doing cross
|
||||
* compiling. Should not be used for dynamic compiling.
|
||||
*/
|
||||
const char *kbuild_opts;
|
||||
/*
|
||||
* Default is false. If one of the above fields is set by user
|
||||
* explicitly then user_set_llvm is set to true. This is used
|
||||
* for perf test. If user doesn't set anything in .perfconfig
|
||||
* and clang is not found, don't trigger llvm test.
|
||||
*/
|
||||
bool user_set_param;
|
||||
};
|
||||
|
||||
extern struct llvm_param llvm_param;
|
||||
extern int perf_llvm_config(const char *var, const char *value);
|
||||
|
||||
extern int llvm__compile_bpf(const char *path, void **p_obj_buf,
|
||||
size_t *p_obj_buf_sz);
|
||||
|
||||
/* This function is for test__llvm() use only */
|
||||
extern int llvm__search_clang(void);
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user