diff options
| -rw-r--r-- | tools/build/feature/Makefile | 6 | ||||
| -rw-r--r-- | tools/build/feature/test-bpf.c | 18 | ||||
| -rw-r--r-- | tools/lib/bpf/.gitignore | 2 | ||||
| -rw-r--r-- | tools/lib/bpf/Build | 1 | ||||
| -rw-r--r-- | tools/lib/bpf/Makefile | 195 | ||||
| -rw-r--r-- | tools/lib/bpf/bpf.c | 85 | ||||
| -rw-r--r-- | tools/lib/bpf/bpf.h | 23 | ||||
| -rw-r--r-- | tools/lib/bpf/libbpf.c | 1037 | ||||
| -rw-r--r-- | tools/lib/bpf/libbpf.h | 81 | ||||
| -rw-r--r-- | tools/perf/MANIFEST | 1 | ||||
| -rw-r--r-- | tools/perf/tests/Build | 1 | ||||
| -rw-r--r-- | tools/perf/tests/builtin-test.c | 4 | ||||
| -rw-r--r-- | tools/perf/tests/llvm.c | 98 | ||||
| -rw-r--r-- | tools/perf/tests/tests.h | 1 | ||||
| -rw-r--r-- | tools/perf/util/Build | 1 | ||||
| -rw-r--r-- | tools/perf/util/config.c | 4 | ||||
| -rw-r--r-- | tools/perf/util/llvm-utils.c | 408 | ||||
| -rw-r--r-- | tools/perf/util/llvm-utils.h | 49 |
18 files changed, 2014 insertions, 1 deletions
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index 463ed8f2a267..1c0d69f44552 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile | |||
| @@ -33,7 +33,8 @@ FILES= \ | |||
| 33 | test-compile-32.bin \ | 33 | test-compile-32.bin \ |
| 34 | test-compile-x32.bin \ | 34 | test-compile-x32.bin \ |
| 35 | test-zlib.bin \ | 35 | test-zlib.bin \ |
| 36 | test-lzma.bin | 36 | test-lzma.bin \ |
| 37 | test-bpf.bin | ||
| 37 | 38 | ||
| 38 | CC := $(CROSS_COMPILE)gcc -MD | 39 | CC := $(CROSS_COMPILE)gcc -MD |
| 39 | PKG_CONFIG := $(CROSS_COMPILE)pkg-config | 40 | PKG_CONFIG := $(CROSS_COMPILE)pkg-config |
| @@ -156,6 +157,9 @@ test-zlib.bin: | |||
| 156 | test-lzma.bin: | 157 | test-lzma.bin: |
| 157 | $(BUILD) -llzma | 158 | $(BUILD) -llzma |
| 158 | 159 | ||
| 160 | test-bpf.bin: | ||
| 161 | $(BUILD) | ||
| 162 | |||
| 159 | -include *.d | 163 | -include *.d |
| 160 | 164 | ||
| 161 | ############################### | 165 | ############################### |
diff --git a/tools/build/feature/test-bpf.c b/tools/build/feature/test-bpf.c new file mode 100644 index 000000000000..062bac811af9 --- /dev/null +++ b/tools/build/feature/test-bpf.c | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | #include <linux/bpf.h> | ||
| 2 | |||
| 3 | int main(void) | ||
| 4 | { | ||
| 5 | union bpf_attr attr; | ||
| 6 | |||
| 7 | attr.prog_type = BPF_PROG_TYPE_KPROBE; | ||
| 8 | attr.insn_cnt = 0; | ||
| 9 | attr.insns = 0; | ||
| 10 | attr.license = 0; | ||
| 11 | attr.log_buf = 0; | ||
| 12 | attr.log_size = 0; | ||
| 13 | attr.log_level = 0; | ||
| 14 | attr.kern_version = 0; | ||
| 15 | |||
| 16 | attr = attr; | ||
| 17 | return 0; | ||
| 18 | } | ||
diff --git a/tools/lib/bpf/.gitignore b/tools/lib/bpf/.gitignore new file mode 100644 index 000000000000..812aeedaea38 --- /dev/null +++ b/tools/lib/bpf/.gitignore | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | libbpf_version.h | ||
| 2 | FEATURE-DUMP | ||
diff --git a/tools/lib/bpf/Build b/tools/lib/bpf/Build new file mode 100644 index 000000000000..d8749756352d --- /dev/null +++ b/tools/lib/bpf/Build | |||
| @@ -0,0 +1 @@ | |||
| libbpf-y := libbpf.o bpf.o | |||
diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile new file mode 100644 index 000000000000..f68d23a0b487 --- /dev/null +++ b/tools/lib/bpf/Makefile | |||
| @@ -0,0 +1,195 @@ | |||
| 1 | # Most of this file is copied from tools/lib/traceevent/Makefile | ||
| 2 | |||
| 3 | BPF_VERSION = 0 | ||
| 4 | BPF_PATCHLEVEL = 0 | ||
| 5 | BPF_EXTRAVERSION = 1 | ||
| 6 | |||
| 7 | MAKEFLAGS += --no-print-directory | ||
| 8 | |||
| 9 | |||
| 10 | # Makefiles suck: This macro sets a default value of $(2) for the | ||
| 11 | # variable named by $(1), unless the variable has been set by | ||
| 12 | # environment or command line. This is necessary for CC and AR | ||
| 13 | # because make sets default values, so the simpler ?= approach | ||
| 14 | # won't work as expected. | ||
| 15 | define allow-override | ||
| 16 | $(if $(or $(findstring environment,$(origin $(1))),\ | ||
| 17 | $(findstring command line,$(origin $(1)))),,\ | ||
| 18 | $(eval $(1) = $(2))) | ||
| 19 | endef | ||
| 20 | |||
| 21 | # Allow setting CC and AR, or setting CROSS_COMPILE as a prefix. | ||
| 22 | $(call allow-override,CC,$(CROSS_COMPILE)gcc) | ||
| 23 | $(call allow-override,AR,$(CROSS_COMPILE)ar) | ||
| 24 | |||
| 25 | INSTALL = install | ||
| 26 | |||
| 27 | # Use DESTDIR for installing into a different root directory. | ||
| 28 | # This is useful for building a package. The program will be | ||
| 29 | # installed in this directory as if it was the root directory. | ||
| 30 | # Then the build tool can move it later. | ||
| 31 | DESTDIR ?= | ||
| 32 | DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))' | ||
| 33 | |||
| 34 | LP64 := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1) | ||
| 35 | ifeq ($(LP64), 1) | ||
| 36 | libdir_relative = lib64 | ||
| 37 | else | ||
| 38 | libdir_relative = lib | ||
| 39 | endif | ||
| 40 | |||
| 41 | prefix ?= /usr/local | ||
| 42 | libdir = $(prefix)/$(libdir_relative) | ||
| 43 | man_dir = $(prefix)/share/man | ||
| 44 | man_dir_SQ = '$(subst ','\'',$(man_dir))' | ||
| 45 | |||
| 46 | export man_dir man_dir_SQ INSTALL | ||
| 47 | export DESTDIR DESTDIR_SQ | ||
| 48 | |||
| 49 | include ../../scripts/Makefile.include | ||
| 50 | |||
| 51 | # copy a bit from Linux kbuild | ||
| 52 | |||
| 53 | ifeq ("$(origin V)", "command line") | ||
| 54 | VERBOSE = $(V) | ||
| 55 | endif | ||
| 56 | ifndef VERBOSE | ||
| 57 | VERBOSE = 0 | ||
| 58 | endif | ||
| 59 | |||
| 60 | ifeq ($(srctree),) | ||
| 61 | srctree := $(patsubst %/,%,$(dir $(shell pwd))) | ||
| 62 | srctree := $(patsubst %/,%,$(dir $(srctree))) | ||
| 63 | srctree := $(patsubst %/,%,$(dir $(srctree))) | ||
| 64 | #$(info Determined 'srctree' to be $(srctree)) | ||
| 65 | endif | ||
| 66 | |||
| 67 | FEATURE_DISPLAY = libelf libelf-getphdrnum libelf-mmap bpf | ||
| 68 | FEATURE_TESTS = libelf bpf | ||
| 69 | |||
| 70 | INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/arch/$(ARCH)/include/uapi -I$(srctree)/include/uapi | ||
| 71 | FEATURE_CHECK_CFLAGS-bpf = $(INCLUDES) | ||
| 72 | |||
| 73 | include $(srctree)/tools/build/Makefile.feature | ||
| 74 | |||
| 75 | export prefix libdir src obj | ||
| 76 | |||
| 77 | # Shell quotes | ||
| 78 | libdir_SQ = $(subst ','\'',$(libdir)) | ||
| 79 | libdir_relative_SQ = $(subst ','\'',$(libdir_relative)) | ||
| 80 | plugin_dir_SQ = $(subst ','\'',$(plugin_dir)) | ||
| 81 | |||
| 82 | LIB_FILE = libbpf.a libbpf.so | ||
| 83 | |||
| 84 | VERSION = $(BPF_VERSION) | ||
| 85 | PATCHLEVEL = $(BPF_PATCHLEVEL) | ||
| 86 | EXTRAVERSION = $(BPF_EXTRAVERSION) | ||
| 87 | |||
| 88 | OBJ = $@ | ||
| 89 | N = | ||
| 90 | |||
| 91 | LIBBPF_VERSION = $(BPF_VERSION).$(BPF_PATCHLEVEL).$(BPF_EXTRAVERSION) | ||
| 92 | |||
| 93 | # Set compile option CFLAGS | ||
| 94 | ifdef EXTRA_CFLAGS | ||
| 95 | CFLAGS := $(EXTRA_CFLAGS) | ||
| 96 | else | ||
| 97 | CFLAGS := -g -Wall | ||
| 98 | endif | ||
| 99 | |||
| 100 | ifeq ($(feature-libelf-mmap), 1) | ||
| 101 | override CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT | ||
| 102 | endif | ||
| 103 | |||
| 104 | ifeq ($(feature-libelf-getphdrnum), 1) | ||
| 105 | override CFLAGS += -DHAVE_ELF_GETPHDRNUM_SUPPORT | ||
| 106 | endif | ||
| 107 | |||
| 108 | # Append required CFLAGS | ||
| 109 | override CFLAGS += $(EXTRA_WARNINGS) | ||
| 110 | override CFLAGS += -Werror -Wall | ||
| 111 | override CFLAGS += -fPIC | ||
| 112 | override CFLAGS += $(INCLUDES) | ||
| 113 | |||
| 114 | ifeq ($(VERBOSE),1) | ||
| 115 | Q = | ||
| 116 | else | ||
| 117 | Q = @ | ||
| 118 | endif | ||
| 119 | |||
| 120 | # Disable command line variables (CFLAGS) overide from top | ||
| 121 | # level Makefile (perf), otherwise build Makefile will get | ||
| 122 | # the same command line setup. | ||
| 123 | MAKEOVERRIDES= | ||
| 124 | |||
| 125 | export srctree OUTPUT CC LD CFLAGS V | ||
| 126 | build := -f $(srctree)/tools/build/Makefile.build dir=. obj | ||
| 127 | |||
| 128 | BPF_IN := $(OUTPUT)libbpf-in.o | ||
| 129 | LIB_FILE := $(addprefix $(OUTPUT),$(LIB_FILE)) | ||
| 130 | |||
| 131 | CMD_TARGETS = $(LIB_FILE) | ||
| 132 | |||
| 133 | TARGETS = $(CMD_TARGETS) | ||
| 134 | |||
| 135 | all: $(VERSION_FILES) all_cmd | ||
| 136 | |||
| 137 | all_cmd: $(CMD_TARGETS) | ||
| 138 | |||
| 139 | $(BPF_IN): force elfdep bpfdep | ||
| 140 | $(Q)$(MAKE) $(build)=libbpf | ||
| 141 | |||
| 142 | $(OUTPUT)libbpf.so: $(BPF_IN) | ||
| 143 | $(QUIET_LINK)$(CC) --shared $^ -o $@ | ||
| 144 | |||
| 145 | $(OUTPUT)libbpf.a: $(BPF_IN) | ||
| 146 | $(QUIET_LINK)$(RM) $@; $(AR) rcs $@ $^ | ||
| 147 | |||
| 148 | define update_dir | ||
| 149 | (echo $1 > $@.tmp; \ | ||
| 150 | if [ -r $@ ] && cmp -s $@ $@.tmp; then \ | ||
| 151 | rm -f $@.tmp; \ | ||
| 152 | else \ | ||
| 153 | echo ' UPDATE $@'; \ | ||
| 154 | mv -f $@.tmp $@; \ | ||
| 155 | fi); | ||
| 156 | endef | ||
| 157 | |||
| 158 | define do_install | ||
| 159 | if [ ! -d '$(DESTDIR_SQ)$2' ]; then \ | ||
| 160 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \ | ||
| 161 | fi; \ | ||
| 162 | $(INSTALL) $1 '$(DESTDIR_SQ)$2' | ||
| 163 | endef | ||
| 164 | |||
| 165 | install_lib: all_cmd | ||
| 166 | $(call QUIET_INSTALL, $(LIB_FILE)) \ | ||
| 167 | $(call do_install,$(LIB_FILE),$(libdir_SQ)) | ||
| 168 | |||
| 169 | install: install_lib | ||
| 170 | |||
| 171 | ### Cleaning rules | ||
| 172 | |||
| 173 | config-clean: | ||
| 174 | $(call QUIET_CLEAN, config) | ||
| 175 | $(Q)$(MAKE) -C $(srctree)/tools/build/feature/ clean >/dev/null | ||
| 176 | |||
| 177 | clean: | ||
| 178 | $(call QUIET_CLEAN, libbpf) $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d \ | ||
| 179 | $(RM) LIBBPF-CFLAGS | ||
| 180 | $(call QUIET_CLEAN, core-gen) $(RM) $(OUTPUT)FEATURE-DUMP | ||
| 181 | |||
| 182 | |||
| 183 | |||
| 184 | PHONY += force elfdep bpfdep | ||
| 185 | force: | ||
| 186 | |||
| 187 | elfdep: | ||
| 188 | @if [ "$(feature-libelf)" != "1" ]; then echo "No libelf found"; exit -1 ; fi | ||
| 189 | |||
| 190 | bpfdep: | ||
| 191 | @if [ "$(feature-bpf)" != "1" ]; then echo "BPF API too old"; exit -1 ; fi | ||
| 192 | |||
| 193 | # Declare the contents of the .PHONY variable as phony. We keep that | ||
| 194 | # information in a variable so we can use it in if_changed and friends. | ||
| 195 | .PHONY: $(PHONY) | ||
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c new file mode 100644 index 000000000000..a6331050ab79 --- /dev/null +++ b/tools/lib/bpf/bpf.c | |||
| @@ -0,0 +1,85 @@ | |||
| 1 | /* | ||
| 2 | * common eBPF ELF operations. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org> | ||
| 5 | * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com> | ||
| 6 | * Copyright (C) 2015 Huawei Inc. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <stdlib.h> | ||
| 10 | #include <memory.h> | ||
| 11 | #include <unistd.h> | ||
| 12 | #include <asm/unistd.h> | ||
| 13 | #include <linux/bpf.h> | ||
| 14 | #include "bpf.h" | ||
| 15 | |||
| 16 | /* | ||
| 17 | * When building perf, unistd.h is override. Define __NR_bpf is | ||
| 18 | * required to be defined. | ||
| 19 | */ | ||
| 20 | #ifndef __NR_bpf | ||
| 21 | # if defined(__i386__) | ||
| 22 | # define __NR_bpf 357 | ||
| 23 | # elif defined(__x86_64__) | ||
| 24 | # define __NR_bpf 321 | ||
| 25 | # elif defined(__aarch64__) | ||
| 26 | # define __NR_bpf 280 | ||
| 27 | # else | ||
| 28 | # error __NR_bpf not defined. libbpf does not support your arch. | ||
| 29 | # endif | ||
| 30 | #endif | ||
| 31 | |||
| 32 | static __u64 ptr_to_u64(void *ptr) | ||
| 33 | { | ||
| 34 | return (__u64) (unsigned long) ptr; | ||
| 35 | } | ||
| 36 | |||
| 37 | static int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr, | ||
| 38 | unsigned int size) | ||
| 39 | { | ||
| 40 | return syscall(__NR_bpf, cmd, attr, size); | ||
| 41 | } | ||
| 42 | |||
| 43 | int bpf_create_map(enum bpf_map_type map_type, int key_size, | ||
| 44 | int value_size, int max_entries) | ||
| 45 | { | ||
| 46 | union bpf_attr attr; | ||
| 47 | |||
| 48 | memset(&attr, '\0', sizeof(attr)); | ||
| 49 | |||
| 50 | attr.map_type = map_type; | ||
| 51 | attr.key_size = key_size; | ||
| 52 | attr.value_size = value_size; | ||
| 53 | attr.max_entries = max_entries; | ||
| 54 | |||
| 55 | return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); | ||
| 56 | } | ||
| 57 | |||
| 58 | int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns, | ||
| 59 | size_t insns_cnt, char *license, | ||
| 60 | u32 kern_version, char *log_buf, size_t log_buf_sz) | ||
| 61 | { | ||
| 62 | int fd; | ||
| 63 | union bpf_attr attr; | ||
| 64 | |||
| 65 | bzero(&attr, sizeof(attr)); | ||
| 66 | attr.prog_type = type; | ||
| 67 | attr.insn_cnt = (__u32)insns_cnt; | ||
| 68 | attr.insns = ptr_to_u64(insns); | ||
| 69 | attr.license = ptr_to_u64(license); | ||
| 70 | attr.log_buf = ptr_to_u64(NULL); | ||
| 71 | attr.log_size = 0; | ||
| 72 | attr.log_level = 0; | ||
| 73 | attr.kern_version = kern_version; | ||
| 74 | |||
| 75 | fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); | ||
| 76 | if (fd >= 0 || !log_buf || !log_buf_sz) | ||
| 77 | return fd; | ||
| 78 | |||
| 79 | /* Try again with log */ | ||
| 80 | attr.log_buf = ptr_to_u64(log_buf); | ||
| 81 | attr.log_size = log_buf_sz; | ||
| 82 | attr.log_level = 1; | ||
| 83 | log_buf[0] = 0; | ||
| 84 | return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); | ||
| 85 | } | ||
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h new file mode 100644 index 000000000000..854b7361b784 --- /dev/null +++ b/tools/lib/bpf/bpf.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | /* | ||
| 2 | * common eBPF ELF operations. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org> | ||
| 5 | * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com> | ||
| 6 | * Copyright (C) 2015 Huawei Inc. | ||
| 7 | */ | ||
| 8 | #ifndef __BPF_BPF_H | ||
| 9 | #define __BPF_BPF_H | ||
| 10 | |||
| 11 | #include <linux/bpf.h> | ||
| 12 | |||
| 13 | int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, | ||
| 14 | int max_entries); | ||
| 15 | |||
| 16 | /* Recommend log buffer size */ | ||
| 17 | #define BPF_LOG_BUF_SIZE 65536 | ||
| 18 | int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns, | ||
| 19 | size_t insns_cnt, char *license, | ||
| 20 | u32 kern_version, char *log_buf, | ||
| 21 | size_t log_buf_sz); | ||
| 22 | |||
| 23 | #endif | ||
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c new file mode 100644 index 000000000000..4fa4bc4505f5 --- /dev/null +++ b/tools/lib/bpf/libbpf.c | |||
| @@ -0,0 +1,1037 @@ | |||
| 1 | /* | ||
| 2 | * Common eBPF ELF object loading operations. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org> | ||
| 5 | * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com> | ||
| 6 | * Copyright (C) 2015 Huawei Inc. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <stdlib.h> | ||
| 10 | #include <stdio.h> | ||
| 11 | #include <stdarg.h> | ||
| 12 | #include <inttypes.h> | ||
| 13 | #include <string.h> | ||
| 14 | #include <unistd.h> | ||
| 15 | #include <fcntl.h> | ||
| 16 | #include <errno.h> | ||
| 17 | #include <asm/unistd.h> | ||
| 18 | #include <linux/kernel.h> | ||
| 19 | #include <linux/bpf.h> | ||
| 20 | #include <linux/list.h> | ||
| 21 | #include <libelf.h> | ||
| 22 | #include <gelf.h> | ||
| 23 | |||
| 24 | #include "libbpf.h" | ||
| 25 | #include "bpf.h" | ||
| 26 | |||
| 27 | #define __printf(a, b) __attribute__((format(printf, a, b))) | ||
| 28 | |||
| 29 | __printf(1, 2) | ||
| 30 | static int __base_pr(const char *format, ...) | ||
| 31 | { | ||
| 32 | va_list args; | ||
| 33 | int err; | ||
| 34 | |||
| 35 | va_start(args, format); | ||
| 36 | err = vfprintf(stderr, format, args); | ||
| 37 | va_end(args); | ||
| 38 | return err; | ||
| 39 | } | ||
| 40 | |||
| 41 | static __printf(1, 2) libbpf_print_fn_t __pr_warning = __base_pr; | ||
| 42 | static __printf(1, 2) libbpf_print_fn_t __pr_info = __base_pr; | ||
| 43 | static __printf(1, 2) libbpf_print_fn_t __pr_debug; | ||
| 44 | |||
| 45 | #define __pr(func, fmt, ...) \ | ||
| 46 | do { \ | ||
| 47 | if ((func)) \ | ||
| 48 | (func)("libbpf: " fmt, ##__VA_ARGS__); \ | ||
| 49 | } while (0) | ||
| 50 | |||
| 51 | #define pr_warning(fmt, ...) __pr(__pr_warning, fmt, ##__VA_ARGS__) | ||
| 52 | #define pr_info(fmt, ...) __pr(__pr_info, fmt, ##__VA_ARGS__) | ||
| 53 | #define pr_debug(fmt, ...) __pr(__pr_debug, fmt, ##__VA_ARGS__) | ||
| 54 | |||
| 55 | void libbpf_set_print(libbpf_print_fn_t warn, | ||
| 56 | libbpf_print_fn_t info, | ||
| 57 | libbpf_print_fn_t debug) | ||
| 58 | { | ||
| 59 | __pr_warning = warn; | ||
| 60 | __pr_info = info; | ||
| 61 | __pr_debug = debug; | ||
| 62 | } | ||
| 63 | |||
| 64 | /* Copied from tools/perf/util/util.h */ | ||
| 65 | #ifndef zfree | ||
| 66 | # define zfree(ptr) ({ free(*ptr); *ptr = NULL; }) | ||
| 67 | #endif | ||
| 68 | |||
| 69 | #ifndef zclose | ||
| 70 | # define zclose(fd) ({ \ | ||
| 71 | int ___err = 0; \ | ||
| 72 | if ((fd) >= 0) \ | ||
| 73 | ___err = close((fd)); \ | ||
| 74 | fd = -1; \ | ||
| 75 | ___err; }) | ||
| 76 | #endif | ||
| 77 | |||
| 78 | #ifdef HAVE_LIBELF_MMAP_SUPPORT | ||
| 79 | # define LIBBPF_ELF_C_READ_MMAP ELF_C_READ_MMAP | ||
| 80 | #else | ||
| 81 | # define LIBBPF_ELF_C_READ_MMAP ELF_C_READ | ||
| 82 | #endif | ||
| 83 | |||
| 84 | /* | ||
| 85 | * bpf_prog should be a better name but it has been used in | ||
| 86 | * linux/filter.h. | ||
| 87 | */ | ||
| 88 | struct bpf_program { | ||
| 89 | /* Index in elf obj file, for relocation use. */ | ||
| 90 | int idx; | ||
| 91 | char *section_name; | ||
| 92 | struct bpf_insn *insns; | ||
| 93 | size_t insns_cnt; | ||
| 94 | |||
| 95 | struct { | ||
| 96 | int insn_idx; | ||
| 97 | int map_idx; | ||
| 98 | } *reloc_desc; | ||
| 99 | int nr_reloc; | ||
| 100 | |||
| 101 | int fd; | ||
| 102 | |||
| 103 | struct bpf_object *obj; | ||
| 104 | void *priv; | ||
| 105 | bpf_program_clear_priv_t clear_priv; | ||
| 106 | }; | ||
| 107 | |||
| 108 | static LIST_HEAD(bpf_objects_list); | ||
| 109 | |||
| 110 | struct bpf_object { | ||
| 111 | char license[64]; | ||
| 112 | u32 kern_version; | ||
| 113 | void *maps_buf; | ||
| 114 | size_t maps_buf_sz; | ||
| 115 | |||
| 116 | struct bpf_program *programs; | ||
| 117 | size_t nr_programs; | ||
| 118 | int *map_fds; | ||
| 119 | /* | ||
| 120 | * This field is required because maps_buf will be freed and | ||
| 121 | * maps_buf_sz will be set to 0 after loaded. | ||
| 122 | */ | ||
| 123 | size_t nr_map_fds; | ||
| 124 | bool loaded; | ||
| 125 | |||
| 126 | /* | ||
| 127 | * Information when doing elf related work. Only valid if fd | ||
| 128 | * is valid. | ||
| 129 | */ | ||
| 130 | struct { | ||
| 131 | int fd; | ||
| 132 | void *obj_buf; | ||
| 133 | size_t obj_buf_sz; | ||
| 134 | Elf *elf; | ||
| 135 | GElf_Ehdr ehdr; | ||
| 136 | Elf_Data *symbols; | ||
| 137 | struct { | ||
| 138 | GElf_Shdr shdr; | ||
| 139 | Elf_Data *data; | ||
| 140 | } *reloc; | ||
| 141 | int nr_reloc; | ||
| 142 | } efile; | ||
| 143 | /* | ||
| 144 | * All loaded bpf_object is linked in a list, which is | ||
| 145 | * hidden to caller. bpf_objects__<func> handlers deal with | ||
| 146 | * all objects. | ||
| 147 | */ | ||
| 148 | struct list_head list; | ||
| 149 | char path[]; | ||
| 150 | }; | ||
| 151 | #define obj_elf_valid(o) ((o)->efile.elf) | ||
| 152 | |||
| 153 | static void bpf_program__unload(struct bpf_program *prog) | ||
| 154 | { | ||
| 155 | if (!prog) | ||
| 156 | return; | ||
| 157 | |||
| 158 | zclose(prog->fd); | ||
| 159 | } | ||
| 160 | |||
| 161 | static void bpf_program__exit(struct bpf_program *prog) | ||
| 162 | { | ||
| 163 | if (!prog) | ||
| 164 | return; | ||
| 165 | |||
| 166 | if (prog->clear_priv) | ||
| 167 | prog->clear_priv(prog, prog->priv); | ||
| 168 | |||
| 169 | prog->priv = NULL; | ||
| 170 | prog->clear_priv = NULL; | ||
| 171 | |||
| 172 | bpf_program__unload(prog); | ||
| 173 | zfree(&prog->section_name); | ||
| 174 | zfree(&prog->insns); | ||
| 175 | zfree(&prog->reloc_desc); | ||
| 176 | |||
| 177 | prog->nr_reloc = 0; | ||
| 178 | prog->insns_cnt = 0; | ||
| 179 | prog->idx = -1; | ||
| 180 | } | ||
| 181 | |||
| 182 | static int | ||
| 183 | bpf_program__init(void *data, size_t size, char *name, int idx, | ||
| 184 | struct bpf_program *prog) | ||
| 185 | { | ||
| 186 | if (size < sizeof(struct bpf_insn)) { | ||
| 187 | pr_warning("corrupted section '%s'\n", name); | ||
| 188 | return -EINVAL; | ||
| 189 | } | ||
| 190 | |||
| 191 | bzero(prog, sizeof(*prog)); | ||
| 192 | |||
| 193 | prog->section_name = strdup(name); | ||
| 194 | if (!prog->section_name) { | ||
| 195 | pr_warning("failed to alloc name for prog %s\n", | ||
| 196 | name); | ||
| 197 | goto errout; | ||
| 198 | } | ||
| 199 | |||
| 200 | prog->insns = malloc(size); | ||
| 201 | if (!prog->insns) { | ||
| 202 | pr_warning("failed to alloc insns for %s\n", name); | ||
| 203 | goto errout; | ||
| 204 | } | ||
| 205 | prog->insns_cnt = size / sizeof(struct bpf_insn); | ||
| 206 | memcpy(prog->insns, data, | ||
| 207 | prog->insns_cnt * sizeof(struct bpf_insn)); | ||
| 208 | prog->idx = idx; | ||
| 209 | prog->fd = -1; | ||
| 210 | |||
| 211 | return 0; | ||
| 212 | errout: | ||
| 213 | bpf_program__exit(prog); | ||
| 214 | return -ENOMEM; | ||
| 215 | } | ||
| 216 | |||
| 217 | static int | ||
| 218 | bpf_object__add_program(struct bpf_object *obj, void *data, size_t size, | ||
| 219 | char *name, int idx) | ||
| 220 | { | ||
| 221 | struct bpf_program prog, *progs; | ||
| 222 | int nr_progs, err; | ||
| 223 | |||
| 224 | err = bpf_program__init(data, size, name, idx, &prog); | ||
| 225 | if (err) | ||
| 226 | return err; | ||
| 227 | |||
| 228 | progs = obj->programs; | ||
| 229 | nr_progs = obj->nr_programs; | ||
| 230 | |||
| 231 | progs = realloc(progs, sizeof(progs[0]) * (nr_progs + 1)); | ||
| 232 | if (!progs) { | ||
| 233 | /* | ||
| 234 | * In this case the original obj->programs | ||
| 235 | * is still valid, so don't need special treat for | ||
| 236 | * bpf_close_object(). | ||
| 237 | */ | ||
| 238 | pr_warning("failed to alloc a new program '%s'\n", | ||
| 239 | name); | ||
| 240 | bpf_program__exit(&prog); | ||
| 241 | return -ENOMEM; | ||
| 242 | } | ||
| 243 | |||
| 244 | pr_debug("found program %s\n", prog.section_name); | ||
| 245 | obj->programs = progs; | ||
| 246 | obj->nr_programs = nr_progs + 1; | ||
| 247 | prog.obj = obj; | ||
| 248 | progs[nr_progs] = prog; | ||
| 249 | return 0; | ||
| 250 | } | ||
| 251 | |||
| 252 | static struct bpf_object *bpf_object__new(const char *path, | ||
| 253 | void *obj_buf, | ||
| 254 | size_t obj_buf_sz) | ||
| 255 | { | ||
| 256 | struct bpf_object *obj; | ||
| 257 | |||
| 258 | obj = calloc(1, sizeof(struct bpf_object) + strlen(path) + 1); | ||
| 259 | if (!obj) { | ||
| 260 | pr_warning("alloc memory failed for %s\n", path); | ||
| 261 | return NULL; | ||
| 262 | } | ||
| 263 | |||
| 264 | strcpy(obj->path, path); | ||
| 265 | obj->efile.fd = -1; | ||
| 266 | |||
| 267 | /* | ||
| 268 | * Caller of this function should also calls | ||
| 269 | * bpf_object__elf_finish() after data collection to return | ||
| 270 | * obj_buf to user. If not, we should duplicate the buffer to | ||
| 271 | * avoid user freeing them before elf finish. | ||
| 272 | */ | ||
| 273 | obj->efile.obj_buf = obj_buf; | ||
| 274 | obj->efile.obj_buf_sz = obj_buf_sz; | ||
| 275 | |||
| 276 | obj->loaded = false; | ||
| 277 | |||
| 278 | INIT_LIST_HEAD(&obj->list); | ||
| 279 | list_add(&obj->list, &bpf_objects_list); | ||
| 280 | return obj; | ||
| 281 | } | ||
| 282 | |||
| 283 | static void bpf_object__elf_finish(struct bpf_object *obj) | ||
| 284 | { | ||
| 285 | if (!obj_elf_valid(obj)) | ||
| 286 | return; | ||
| 287 | |||
| 288 | if (obj->efile.elf) { | ||
| 289 | elf_end(obj->efile.elf); | ||
| 290 | obj->efile.elf = NULL; | ||
| 291 | } | ||
| 292 | obj->efile.symbols = NULL; | ||
| 293 | |||
| 294 | zfree(&obj->efile.reloc); | ||
| 295 | obj->efile.nr_reloc = 0; | ||
| 296 | zclose(obj->efile.fd); | ||
| 297 | obj->efile.obj_buf = NULL; | ||
| 298 | obj->efile.obj_buf_sz = 0; | ||
| 299 | } | ||
| 300 | |||
| 301 | static int bpf_object__elf_init(struct bpf_object *obj) | ||
| 302 | { | ||
| 303 | int err = 0; | ||
| 304 | GElf_Ehdr *ep; | ||
| 305 | |||
| 306 | if (obj_elf_valid(obj)) { | ||
| 307 | pr_warning("elf init: internal error\n"); | ||
| 308 | return -EEXIST; | ||
| 309 | } | ||
| 310 | |||
| 311 | if (obj->efile.obj_buf_sz > 0) { | ||
| 312 | /* | ||
| 313 | * obj_buf should have been validated by | ||
| 314 | * bpf_object__open_buffer(). | ||
| 315 | */ | ||
| 316 | obj->efile.elf = elf_memory(obj->efile.obj_buf, | ||
| 317 | obj->efile.obj_buf_sz); | ||
| 318 | } else { | ||
| 319 | obj->efile.fd = open(obj->path, O_RDONLY); | ||
| 320 | if (obj->efile.fd < 0) { | ||
| 321 | pr_warning("failed to open %s: %s\n", obj->path, | ||
| 322 | strerror(errno)); | ||
| 323 | return -errno; | ||
| 324 | } | ||
| 325 | |||
| 326 | obj->efile.elf = elf_begin(obj->efile.fd, | ||
| 327 | LIBBPF_ELF_C_READ_MMAP, | ||
| 328 | NULL); | ||
| 329 | } | ||
| 330 | |||
| 331 | if (!obj->efile.elf) { | ||
| 332 | pr_warning("failed to open %s as ELF file\n", | ||
| 333 | obj->path); | ||
| 334 | err = -EINVAL; | ||
| 335 | goto errout; | ||
| 336 | } | ||
| 337 | |||
| 338 | if (!gelf_getehdr(obj->efile.elf, &obj->efile.ehdr)) { | ||
| 339 | pr_warning("failed to get EHDR from %s\n", | ||
| 340 | obj->path); | ||
| 341 | err = -EINVAL; | ||
| 342 | goto errout; | ||
| 343 | } | ||
| 344 | ep = &obj->efile.ehdr; | ||
| 345 | |||
| 346 | if ((ep->e_type != ET_REL) || (ep->e_machine != 0)) { | ||
| 347 | pr_warning("%s is not an eBPF object file\n", | ||
| 348 | obj->path); | ||
| 349 | err = -EINVAL; | ||
| 350 | goto errout; | ||
| 351 | } | ||
| 352 | |||
| 353 | return 0; | ||
| 354 | errout: | ||
| 355 | bpf_object__elf_finish(obj); | ||
| 356 | return err; | ||
| 357 | } | ||
| 358 | |||
| 359 | static int | ||
| 360 | bpf_object__check_endianness(struct bpf_object *obj) | ||
| 361 | { | ||
| 362 | static unsigned int const endian = 1; | ||
| 363 | |||
| 364 | switch (obj->efile.ehdr.e_ident[EI_DATA]) { | ||
| 365 | case ELFDATA2LSB: | ||
| 366 | /* We are big endian, BPF obj is little endian. */ | ||
| 367 | if (*(unsigned char const *)&endian != 1) | ||
| 368 | goto mismatch; | ||
| 369 | break; | ||
| 370 | |||
| 371 | case ELFDATA2MSB: | ||
| 372 | /* We are little endian, BPF obj is big endian. */ | ||
| 373 | if (*(unsigned char const *)&endian != 0) | ||
| 374 | goto mismatch; | ||
| 375 | break; | ||
| 376 | default: | ||
| 377 | return -EINVAL; | ||
| 378 | } | ||
| 379 | |||
| 380 | return 0; | ||
| 381 | |||
| 382 | mismatch: | ||
| 383 | pr_warning("Error: endianness mismatch.\n"); | ||
| 384 | return -EINVAL; | ||
| 385 | } | ||
| 386 | |||
| 387 | static int | ||
| 388 | bpf_object__init_license(struct bpf_object *obj, | ||
| 389 | void *data, size_t size) | ||
| 390 | { | ||
| 391 | memcpy(obj->license, data, | ||
| 392 | min(size, sizeof(obj->license) - 1)); | ||
| 393 | pr_debug("license of %s is %s\n", obj->path, obj->license); | ||
| 394 | return 0; | ||
| 395 | } | ||
| 396 | |||
| 397 | static int | ||
| 398 | bpf_object__init_kversion(struct bpf_object *obj, | ||
| 399 | void *data, size_t size) | ||
| 400 | { | ||
| 401 | u32 kver; | ||
| 402 | |||
| 403 | if (size != sizeof(kver)) { | ||
| 404 | pr_warning("invalid kver section in %s\n", obj->path); | ||
| 405 | return -EINVAL; | ||
| 406 | } | ||
| 407 | memcpy(&kver, data, sizeof(kver)); | ||
| 408 | obj->kern_version = kver; | ||
| 409 | pr_debug("kernel version of %s is %x\n", obj->path, | ||
| 410 | obj->kern_version); | ||
| 411 | return 0; | ||
| 412 | } | ||
| 413 | |||
| 414 | static int | ||
| 415 | bpf_object__init_maps(struct bpf_object *obj, void *data, | ||
| 416 | size_t size) | ||
| 417 | { | ||
| 418 | if (size == 0) { | ||
| 419 | pr_debug("%s doesn't need map definition\n", | ||
| 420 | obj->path); | ||
| 421 | return 0; | ||
| 422 | } | ||
| 423 | |||
| 424 | obj->maps_buf = malloc(size); | ||
| 425 | if (!obj->maps_buf) { | ||
| 426 | pr_warning("malloc maps failed: %s\n", obj->path); | ||
| 427 | return -ENOMEM; | ||
| 428 | } | ||
| 429 | |||
| 430 | obj->maps_buf_sz = size; | ||
| 431 | memcpy(obj->maps_buf, data, size); | ||
| 432 | pr_debug("maps in %s: %ld bytes\n", obj->path, (long)size); | ||
| 433 | return 0; | ||
| 434 | } | ||
| 435 | |||
| 436 | static int bpf_object__elf_collect(struct bpf_object *obj) | ||
| 437 | { | ||
| 438 | Elf *elf = obj->efile.elf; | ||
| 439 | GElf_Ehdr *ep = &obj->efile.ehdr; | ||
| 440 | Elf_Scn *scn = NULL; | ||
| 441 | int idx = 0, err = 0; | ||
| 442 | |||
| 443 | /* Elf is corrupted/truncated, avoid calling elf_strptr. */ | ||
| 444 | if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) { | ||
| 445 | pr_warning("failed to get e_shstrndx from %s\n", | ||
| 446 | obj->path); | ||
| 447 | return -EINVAL; | ||
| 448 | } | ||
| 449 | |||
| 450 | while ((scn = elf_nextscn(elf, scn)) != NULL) { | ||
| 451 | char *name; | ||
| 452 | GElf_Shdr sh; | ||
| 453 | Elf_Data *data; | ||
| 454 | |||
| 455 | idx++; | ||
| 456 | if (gelf_getshdr(scn, &sh) != &sh) { | ||
| 457 | pr_warning("failed to get section header from %s\n", | ||
| 458 | obj->path); | ||
| 459 | err = -EINVAL; | ||
| 460 | goto out; | ||
| 461 | } | ||
| 462 | |||
| 463 | name = elf_strptr(elf, ep->e_shstrndx, sh.sh_name); | ||
| 464 | if (!name) { | ||
| 465 | pr_warning("failed to get section name from %s\n", | ||
| 466 | obj->path); | ||
| 467 | err = -EINVAL; | ||
| 468 | goto out; | ||
| 469 | } | ||
| 470 | |||
| 471 | data = elf_getdata(scn, 0); | ||
| 472 | if (!data) { | ||
| 473 | pr_warning("failed to get section data from %s(%s)\n", | ||
| 474 | name, obj->path); | ||
| 475 | err = -EINVAL; | ||
| 476 | goto out; | ||
| 477 | } | ||
| 478 | pr_debug("section %s, size %ld, link %d, flags %lx, type=%d\n", | ||
| 479 | name, (unsigned long)data->d_size, | ||
| 480 | (int)sh.sh_link, (unsigned long)sh.sh_flags, | ||
| 481 | (int)sh.sh_type); | ||
| 482 | |||
| 483 | if (strcmp(name, "license") == 0) | ||
| 484 | err = bpf_object__init_license(obj, | ||
| 485 | data->d_buf, | ||
| 486 | data->d_size); | ||
| 487 | else if (strcmp(name, "version") == 0) | ||
| 488 | err = bpf_object__init_kversion(obj, | ||
| 489 | data->d_buf, | ||
| 490 | data->d_size); | ||
| 491 | else if (strcmp(name, "maps") == 0) | ||
| 492 | err = bpf_object__init_maps(obj, data->d_buf, | ||
| 493 | data->d_size); | ||
| 494 | else if (sh.sh_type == SHT_SYMTAB) { | ||
| 495 | if (obj->efile.symbols) { | ||
| 496 | pr_warning("bpf: multiple SYMTAB in %s\n", | ||
| 497 | obj->path); | ||
| 498 | err = -EEXIST; | ||
| 499 | } else | ||
| 500 | obj->efile.symbols = data; | ||
| 501 | } else if ((sh.sh_type == SHT_PROGBITS) && | ||
| 502 | (sh.sh_flags & SHF_EXECINSTR) && | ||
| 503 | (data->d_size > 0)) { | ||
| 504 | err = bpf_object__add_program(obj, data->d_buf, | ||
| 505 | data->d_size, name, idx); | ||
| 506 | if (err) { | ||
| 507 | char errmsg[128]; | ||
| 508 | strerror_r(-err, errmsg, sizeof(errmsg)); | ||
| 509 | pr_warning("failed to alloc program %s (%s): %s", | ||
| 510 | name, obj->path, errmsg); | ||
| 511 | } | ||
| 512 | } else if (sh.sh_type == SHT_REL) { | ||
| 513 | void *reloc = obj->efile.reloc; | ||
| 514 | int nr_reloc = obj->efile.nr_reloc + 1; | ||
| 515 | |||
| 516 | reloc = realloc(reloc, | ||
| 517 | sizeof(*obj->efile.reloc) * nr_reloc); | ||
| 518 | if (!reloc) { | ||
| 519 | pr_warning("realloc failed\n"); | ||
| 520 | err = -ENOMEM; | ||
| 521 | } else { | ||
| 522 | int n = nr_reloc - 1; | ||
| 523 | |||
| 524 | obj->efile.reloc = reloc; | ||
| 525 | obj->efile.nr_reloc = nr_reloc; | ||
| 526 | |||
| 527 | obj->efile.reloc[n].shdr = sh; | ||
| 528 | obj->efile.reloc[n].data = data; | ||
| 529 | } | ||
| 530 | } | ||
| 531 | if (err) | ||
| 532 | goto out; | ||
| 533 | } | ||
| 534 | out: | ||
| 535 | return err; | ||
| 536 | } | ||
| 537 | |||
| 538 | static struct bpf_program * | ||
| 539 | bpf_object__find_prog_by_idx(struct bpf_object *obj, int idx) | ||
| 540 | { | ||
| 541 | struct bpf_program *prog; | ||
| 542 | size_t i; | ||
| 543 | |||
| 544 | for (i = 0; i < obj->nr_programs; i++) { | ||
| 545 | prog = &obj->programs[i]; | ||
| 546 | if (prog->idx == idx) | ||
| 547 | return prog; | ||
| 548 | } | ||
| 549 | return NULL; | ||
| 550 | } | ||
| 551 | |||
| 552 | static int | ||
| 553 | bpf_program__collect_reloc(struct bpf_program *prog, | ||
| 554 | size_t nr_maps, GElf_Shdr *shdr, | ||
| 555 | Elf_Data *data, Elf_Data *symbols) | ||
| 556 | { | ||
| 557 | int i, nrels; | ||
| 558 | |||
| 559 | pr_debug("collecting relocating info for: '%s'\n", | ||
| 560 | prog->section_name); | ||
| 561 | nrels = shdr->sh_size / shdr->sh_entsize; | ||
| 562 | |||
| 563 | prog->reloc_desc = malloc(sizeof(*prog->reloc_desc) * nrels); | ||
| 564 | if (!prog->reloc_desc) { | ||
| 565 | pr_warning("failed to alloc memory in relocation\n"); | ||
| 566 | return -ENOMEM; | ||
| 567 | } | ||
| 568 | prog->nr_reloc = nrels; | ||
| 569 | |||
| 570 | for (i = 0; i < nrels; i++) { | ||
| 571 | GElf_Sym sym; | ||
| 572 | GElf_Rel rel; | ||
| 573 | unsigned int insn_idx; | ||
| 574 | struct bpf_insn *insns = prog->insns; | ||
| 575 | size_t map_idx; | ||
| 576 | |||
| 577 | if (!gelf_getrel(data, i, &rel)) { | ||
| 578 | pr_warning("relocation: failed to get %d reloc\n", i); | ||
| 579 | return -EINVAL; | ||
| 580 | } | ||
| 581 | |||
| 582 | insn_idx = rel.r_offset / sizeof(struct bpf_insn); | ||
| 583 | pr_debug("relocation: insn_idx=%u\n", insn_idx); | ||
| 584 | |||
| 585 | if (!gelf_getsym(symbols, | ||
| 586 | GELF_R_SYM(rel.r_info), | ||
| 587 | &sym)) { | ||
| 588 | pr_warning("relocation: symbol %"PRIx64" not found\n", | ||
| 589 | GELF_R_SYM(rel.r_info)); | ||
| 590 | return -EINVAL; | ||
| 591 | } | ||
| 592 | |||
| 593 | if (insns[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) { | ||
| 594 | pr_warning("bpf: relocation: invalid relo for insns[%d].code 0x%x\n", | ||
| 595 | insn_idx, insns[insn_idx].code); | ||
| 596 | return -EINVAL; | ||
| 597 | } | ||
| 598 | |||
| 599 | map_idx = sym.st_value / sizeof(struct bpf_map_def); | ||
| 600 | if (map_idx >= nr_maps) { | ||
| 601 | pr_warning("bpf relocation: map_idx %d large than %d\n", | ||
| 602 | (int)map_idx, (int)nr_maps - 1); | ||
| 603 | return -EINVAL; | ||
| 604 | } | ||
| 605 | |||
| 606 | prog->reloc_desc[i].insn_idx = insn_idx; | ||
| 607 | prog->reloc_desc[i].map_idx = map_idx; | ||
| 608 | } | ||
| 609 | return 0; | ||
| 610 | } | ||
| 611 | |||
| 612 | static int | ||
| 613 | bpf_object__create_maps(struct bpf_object *obj) | ||
| 614 | { | ||
| 615 | unsigned int i; | ||
| 616 | size_t nr_maps; | ||
| 617 | int *pfd; | ||
| 618 | |||
| 619 | nr_maps = obj->maps_buf_sz / sizeof(struct bpf_map_def); | ||
| 620 | if (!obj->maps_buf || !nr_maps) { | ||
| 621 | pr_debug("don't need create maps for %s\n", | ||
| 622 | obj->path); | ||
| 623 | return 0; | ||
| 624 | } | ||
| 625 | |||
| 626 | obj->map_fds = malloc(sizeof(int) * nr_maps); | ||
| 627 | if (!obj->map_fds) { | ||
| 628 | pr_warning("realloc perf_bpf_map_fds failed\n"); | ||
| 629 | return -ENOMEM; | ||
| 630 | } | ||
| 631 | obj->nr_map_fds = nr_maps; | ||
| 632 | |||
| 633 | /* fill all fd with -1 */ | ||
| 634 | memset(obj->map_fds, -1, sizeof(int) * nr_maps); | ||
| 635 | |||
| 636 | pfd = obj->map_fds; | ||
| 637 | for (i = 0; i < nr_maps; i++) { | ||
| 638 | struct bpf_map_def def; | ||
| 639 | |||
| 640 | def = *(struct bpf_map_def *)(obj->maps_buf + | ||
| 641 | i * sizeof(struct bpf_map_def)); | ||
| 642 | |||
| 643 | *pfd = bpf_create_map(def.type, | ||
| 644 | def.key_size, | ||
| 645 | def.value_size, | ||
| 646 | def.max_entries); | ||
| 647 | if (*pfd < 0) { | ||
| 648 | size_t j; | ||
| 649 | int err = *pfd; | ||
| 650 | |||
| 651 | pr_warning("failed to create map: %s\n", | ||
| 652 | strerror(errno)); | ||
| 653 | for (j = 0; j < i; j++) | ||
| 654 | zclose(obj->map_fds[j]); | ||
| 655 | obj->nr_map_fds = 0; | ||
| 656 | zfree(&obj->map_fds); | ||
| 657 | return err; | ||
| 658 | } | ||
| 659 | pr_debug("create map: fd=%d\n", *pfd); | ||
| 660 | pfd++; | ||
| 661 | } | ||
| 662 | |||
| 663 | zfree(&obj->maps_buf); | ||
| 664 | obj->maps_buf_sz = 0; | ||
| 665 | return 0; | ||
| 666 | } | ||
| 667 | |||
| 668 | static int | ||
| 669 | bpf_program__relocate(struct bpf_program *prog, int *map_fds) | ||
| 670 | { | ||
| 671 | int i; | ||
| 672 | |||
| 673 | if (!prog || !prog->reloc_desc) | ||
| 674 | return 0; | ||
| 675 | |||
| 676 | for (i = 0; i < prog->nr_reloc; i++) { | ||
| 677 | int insn_idx, map_idx; | ||
| 678 | struct bpf_insn *insns = prog->insns; | ||
| 679 | |||
| 680 | insn_idx = prog->reloc_desc[i].insn_idx; | ||
| 681 | map_idx = prog->reloc_desc[i].map_idx; | ||
| 682 | |||
| 683 | if (insn_idx >= (int)prog->insns_cnt) { | ||
| 684 | pr_warning("relocation out of range: '%s'\n", | ||
| 685 | prog->section_name); | ||
| 686 | return -ERANGE; | ||
| 687 | } | ||
| 688 | insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD; | ||
| 689 | insns[insn_idx].imm = map_fds[map_idx]; | ||
| 690 | } | ||
| 691 | |||
| 692 | zfree(&prog->reloc_desc); | ||
| 693 | prog->nr_reloc = 0; | ||
| 694 | return 0; | ||
| 695 | } | ||
| 696 | |||
| 697 | |||
| 698 | static int | ||
| 699 | bpf_object__relocate(struct bpf_object *obj) | ||
| 700 | { | ||
| 701 | struct bpf_program *prog; | ||
| 702 | size_t i; | ||
| 703 | int err; | ||
| 704 | |||
| 705 | for (i = 0; i < obj->nr_programs; i++) { | ||
| 706 | prog = &obj->programs[i]; | ||
| 707 | |||
| 708 | err = bpf_program__relocate(prog, obj->map_fds); | ||
| 709 | if (err) { | ||
| 710 | pr_warning("failed to relocate '%s'\n", | ||
| 711 | prog->section_name); | ||
| 712 | return err; | ||
| 713 | } | ||
| 714 | } | ||
| 715 | return 0; | ||
| 716 | } | ||
| 717 | |||
| 718 | static int bpf_object__collect_reloc(struct bpf_object *obj) | ||
| 719 | { | ||
| 720 | int i, err; | ||
| 721 | |||
| 722 | if (!obj_elf_valid(obj)) { | ||
| 723 | pr_warning("Internal error: elf object is closed\n"); | ||
| 724 | return -EINVAL; | ||
| 725 | } | ||
| 726 | |||
| 727 | for (i = 0; i < obj->efile.nr_reloc; i++) { | ||
| 728 | GElf_Shdr *shdr = &obj->efile.reloc[i].shdr; | ||
| 729 | Elf_Data *data = obj->efile.reloc[i].data; | ||
| 730 | int idx = shdr->sh_info; | ||
| 731 | struct bpf_program *prog; | ||
| 732 | size_t nr_maps = obj->maps_buf_sz / | ||
| 733 | sizeof(struct bpf_map_def); | ||
| 734 | |||
| 735 | if (shdr->sh_type != SHT_REL) { | ||
| 736 | pr_warning("internal error at %d\n", __LINE__); | ||
| 737 | return -EINVAL; | ||
| 738 | } | ||
| 739 | |||
| 740 | prog = bpf_object__find_prog_by_idx(obj, idx); | ||
| 741 | if (!prog) { | ||
| 742 | pr_warning("relocation failed: no %d section\n", | ||
| 743 | idx); | ||
| 744 | return -ENOENT; | ||
| 745 | } | ||
| 746 | |||
| 747 | err = bpf_program__collect_reloc(prog, nr_maps, | ||
| 748 | shdr, data, | ||
| 749 | obj->efile.symbols); | ||
| 750 | if (err) | ||
| 751 | return -EINVAL; | ||
| 752 | } | ||
| 753 | return 0; | ||
| 754 | } | ||
| 755 | |||
| 756 | static int | ||
| 757 | load_program(struct bpf_insn *insns, int insns_cnt, | ||
| 758 | char *license, u32 kern_version, int *pfd) | ||
| 759 | { | ||
| 760 | int ret; | ||
| 761 | char *log_buf; | ||
| 762 | |||
| 763 | if (!insns || !insns_cnt) | ||
| 764 | return -EINVAL; | ||
| 765 | |||
| 766 | log_buf = malloc(BPF_LOG_BUF_SIZE); | ||
| 767 | if (!log_buf) | ||
| 768 | pr_warning("Alloc log buffer for bpf loader error, continue without log\n"); | ||
| 769 | |||
| 770 | ret = bpf_load_program(BPF_PROG_TYPE_KPROBE, insns, | ||
| 771 | insns_cnt, license, kern_version, | ||
| 772 | log_buf, BPF_LOG_BUF_SIZE); | ||
| 773 | |||
| 774 | if (ret >= 0) { | ||
| 775 | *pfd = ret; | ||
| 776 | ret = 0; | ||
| 777 | goto out; | ||
| 778 | } | ||
| 779 | |||
| 780 | ret = -EINVAL; | ||
| 781 | pr_warning("load bpf program failed: %s\n", strerror(errno)); | ||
| 782 | |||
| 783 | if (log_buf) { | ||
| 784 | pr_warning("-- BEGIN DUMP LOG ---\n"); | ||
| 785 | pr_warning("\n%s\n", log_buf); | ||
| 786 | pr_warning("-- END LOG --\n"); | ||
| 787 | } | ||
| 788 | |||
| 789 | out: | ||
| 790 | free(log_buf); | ||
| 791 | return ret; | ||
| 792 | } | ||
| 793 | |||
| 794 | static int | ||
| 795 | bpf_program__load(struct bpf_program *prog, | ||
| 796 | char *license, u32 kern_version) | ||
| 797 | { | ||
| 798 | int err, fd; | ||
| 799 | |||
| 800 | err = load_program(prog->insns, prog->insns_cnt, | ||
| 801 | license, kern_version, &fd); | ||
| 802 | if (!err) | ||
| 803 | prog->fd = fd; | ||
| 804 | |||
| 805 | if (err) | ||
| 806 | pr_warning("failed to load program '%s'\n", | ||
| 807 | prog->section_name); | ||
| 808 | zfree(&prog->insns); | ||
| 809 | prog->insns_cnt = 0; | ||
| 810 | return err; | ||
| 811 | } | ||
| 812 | |||
| 813 | static int | ||
| 814 | bpf_object__load_progs(struct bpf_object *obj) | ||
| 815 | { | ||
| 816 | size_t i; | ||
| 817 | int err; | ||
| 818 | |||
| 819 | for (i = 0; i < obj->nr_programs; i++) { | ||
| 820 | err = bpf_program__load(&obj->programs[i], | ||
| 821 | obj->license, | ||
| 822 | obj->kern_version); | ||
| 823 | if (err) | ||
| 824 | return err; | ||
| 825 | } | ||
| 826 | return 0; | ||
| 827 | } | ||
| 828 | |||
| 829 | static int bpf_object__validate(struct bpf_object *obj) | ||
| 830 | { | ||
| 831 | if (obj->kern_version == 0) { | ||
| 832 | pr_warning("%s doesn't provide kernel version\n", | ||
| 833 | obj->path); | ||
| 834 | return -EINVAL; | ||
| 835 | } | ||
| 836 | return 0; | ||
| 837 | } | ||
| 838 | |||
| 839 | static struct bpf_object * | ||
| 840 | __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz) | ||
| 841 | { | ||
| 842 | struct bpf_object *obj; | ||
| 843 | |||
| 844 | if (elf_version(EV_CURRENT) == EV_NONE) { | ||
| 845 | pr_warning("failed to init libelf for %s\n", path); | ||
| 846 | return NULL; | ||
| 847 | } | ||
| 848 | |||
| 849 | obj = bpf_object__new(path, obj_buf, obj_buf_sz); | ||
| 850 | if (!obj) | ||
| 851 | return NULL; | ||
| 852 | |||
| 853 | if (bpf_object__elf_init(obj)) | ||
| 854 | goto out; | ||
| 855 | if (bpf_object__check_endianness(obj)) | ||
| 856 | goto out; | ||
| 857 | if (bpf_object__elf_collect(obj)) | ||
| 858 | goto out; | ||
| 859 | if (bpf_object__collect_reloc(obj)) | ||
| 860 | goto out; | ||
| 861 | if (bpf_object__validate(obj)) | ||
| 862 | goto out; | ||
| 863 | |||
| 864 | bpf_object__elf_finish(obj); | ||
| 865 | return obj; | ||
| 866 | out: | ||
| 867 | bpf_object__close(obj); | ||
| 868 | return NULL; | ||
| 869 | } | ||
| 870 | |||
| 871 | struct bpf_object *bpf_object__open(const char *path) | ||
| 872 | { | ||
| 873 | /* param validation */ | ||
| 874 | if (!path) | ||
| 875 | return NULL; | ||
| 876 | |||
| 877 | pr_debug("loading %s\n", path); | ||
| 878 | |||
| 879 | return __bpf_object__open(path, NULL, 0); | ||
| 880 | } | ||
| 881 | |||
| 882 | struct bpf_object *bpf_object__open_buffer(void *obj_buf, | ||
| 883 | size_t obj_buf_sz) | ||
| 884 | { | ||
| 885 | /* param validation */ | ||
| 886 | if (!obj_buf || obj_buf_sz <= 0) | ||
| 887 | return NULL; | ||
| 888 | |||
| 889 | pr_debug("loading object from buffer\n"); | ||
| 890 | |||
| 891 | return __bpf_object__open("[buffer]", obj_buf, obj_buf_sz); | ||
| 892 | } | ||
| 893 | |||
| 894 | int bpf_object__unload(struct bpf_object *obj) | ||
| 895 | { | ||
| 896 | size_t i; | ||
| 897 | |||
| 898 | if (!obj) | ||
| 899 | return -EINVAL; | ||
| 900 | |||
| 901 | for (i = 0; i < obj->nr_map_fds; i++) | ||
| 902 | zclose(obj->map_fds[i]); | ||
| 903 | zfree(&obj->map_fds); | ||
| 904 | obj->nr_map_fds = 0; | ||
| 905 | |||
| 906 | for (i = 0; i < obj->nr_programs; i++) | ||
| 907 | bpf_program__unload(&obj->programs[i]); | ||
| 908 | |||
| 909 | return 0; | ||
| 910 | } | ||
| 911 | |||
| 912 | int bpf_object__load(struct bpf_object *obj) | ||
| 913 | { | ||
| 914 | if (!obj) | ||
| 915 | return -EINVAL; | ||
| 916 | |||
| 917 | if (obj->loaded) { | ||
| 918 | pr_warning("object should not be loaded twice\n"); | ||
| 919 | return -EINVAL; | ||
| 920 | } | ||
| 921 | |||
| 922 | obj->loaded = true; | ||
| 923 | if (bpf_object__create_maps(obj)) | ||
| 924 | goto out; | ||
| 925 | if (bpf_object__relocate(obj)) | ||
| 926 | goto out; | ||
| 927 | if (bpf_object__load_progs(obj)) | ||
| 928 | goto out; | ||
| 929 | |||
| 930 | return 0; | ||
| 931 | out: | ||
| 932 | bpf_object__unload(obj); | ||
| 933 | pr_warning("failed to load object '%s'\n", obj->path); | ||
| 934 | return -EINVAL; | ||
| 935 | } | ||
| 936 | |||
| 937 | void bpf_object__close(struct bpf_object *obj) | ||
| 938 | { | ||
| 939 | size_t i; | ||
| 940 | |||
| 941 | if (!obj) | ||
| 942 | return; | ||
| 943 | |||
| 944 | bpf_object__elf_finish(obj); | ||
| 945 | bpf_object__unload(obj); | ||
| 946 | |||
| 947 | zfree(&obj->maps_buf); | ||
| 948 | |||
| 949 | if (obj->programs && obj->nr_programs) { | ||
| 950 | for (i = 0; i < obj->nr_programs; i++) | ||
| 951 | bpf_program__exit(&obj->programs[i]); | ||
| 952 | } | ||
| 953 | zfree(&obj->programs); | ||
| 954 | |||
| 955 | list_del(&obj->list); | ||
| 956 | free(obj); | ||
| 957 | } | ||
| 958 | |||
| 959 | struct bpf_object * | ||
| 960 | bpf_object__next(struct bpf_object *prev) | ||
| 961 | { | ||
| 962 | struct bpf_object *next; | ||
| 963 | |||
| 964 | if (!prev) | ||
| 965 | next = list_first_entry(&bpf_objects_list, | ||
| 966 | struct bpf_object, | ||
| 967 | list); | ||
| 968 | else | ||
| 969 | next = list_next_entry(prev, list); | ||
| 970 | |||
| 971 | /* Empty list is noticed here so don't need checking on entry. */ | ||
| 972 | if (&next->list == &bpf_objects_list) | ||
| 973 | return NULL; | ||
| 974 | |||
| 975 | return next; | ||
| 976 | } | ||
| 977 | |||
| 978 | struct bpf_program * | ||
| 979 | bpf_program__next(struct bpf_program *prev, struct bpf_object *obj) | ||
| 980 | { | ||
| 981 | size_t idx; | ||
| 982 | |||
| 983 | if (!obj->programs) | ||
| 984 | return NULL; | ||
| 985 | /* First handler */ | ||
| 986 | if (prev == NULL) | ||
| 987 | return &obj->programs[0]; | ||
| 988 | |||
| 989 | if (prev->obj != obj) { | ||
| 990 | pr_warning("error: program handler doesn't match object\n"); | ||
| 991 | return NULL; | ||
| 992 | } | ||
| 993 | |||
| 994 | idx = (prev - obj->programs) + 1; | ||
| 995 | if (idx >= obj->nr_programs) | ||
| 996 | return NULL; | ||
| 997 | return &obj->programs[idx]; | ||
| 998 | } | ||
| 999 | |||
| 1000 | int bpf_program__set_private(struct bpf_program *prog, | ||
| 1001 | void *priv, | ||
| 1002 | bpf_program_clear_priv_t clear_priv) | ||
| 1003 | { | ||
| 1004 | if (prog->priv && prog->clear_priv) | ||
| 1005 | prog->clear_priv(prog, prog->priv); | ||
| 1006 | |||
| 1007 | prog->priv = priv; | ||
| 1008 | prog->clear_priv = clear_priv; | ||
| 1009 | return 0; | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | int bpf_program__get_private(struct bpf_program *prog, void **ppriv) | ||
| 1013 | { | ||
| 1014 | *ppriv = prog->priv; | ||
| 1015 | return 0; | ||
| 1016 | } | ||
| 1017 | |||
| 1018 | const char *bpf_program__title(struct bpf_program *prog, bool dup) | ||
| 1019 | { | ||
| 1020 | const char *title; | ||
| 1021 | |||
| 1022 | title = prog->section_name; | ||
| 1023 | if (dup) { | ||
| 1024 | title = strdup(title); | ||
| 1025 | if (!title) { | ||
| 1026 | pr_warning("failed to strdup program title\n"); | ||
| 1027 | return NULL; | ||
| 1028 | } | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | return title; | ||
| 1032 | } | ||
| 1033 | |||
| 1034 | int bpf_program__fd(struct bpf_program *prog) | ||
| 1035 | { | ||
| 1036 | return prog->fd; | ||
| 1037 | } | ||
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h new file mode 100644 index 000000000000..ea8adc206b62 --- /dev/null +++ b/tools/lib/bpf/libbpf.h | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | /* | ||
| 2 | * Common eBPF ELF object loading operations. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org> | ||
| 5 | * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com> | ||
| 6 | * Copyright (C) 2015 Huawei Inc. | ||
| 7 | */ | ||
| 8 | #ifndef __BPF_LIBBPF_H | ||
| 9 | #define __BPF_LIBBPF_H | ||
| 10 | |||
| 11 | #include <stdio.h> | ||
| 12 | #include <stdbool.h> | ||
| 13 | |||
| 14 | /* | ||
| 15 | * In include/linux/compiler-gcc.h, __printf is defined. However | ||
| 16 | * it should be better if libbpf.h doesn't depend on Linux header file. | ||
| 17 | * So instead of __printf, here we use gcc attribute directly. | ||
| 18 | */ | ||
| 19 | typedef int (*libbpf_print_fn_t)(const char *, ...) | ||
| 20 | __attribute__((format(printf, 1, 2))); | ||
| 21 | |||
| 22 | void libbpf_set_print(libbpf_print_fn_t warn, | ||
| 23 | libbpf_print_fn_t info, | ||
| 24 | libbpf_print_fn_t debug); | ||
| 25 | |||
| 26 | /* Hide internal to user */ | ||
| 27 | struct bpf_object; | ||
| 28 | |||
| 29 | struct bpf_object *bpf_object__open(const char *path); | ||
| 30 | struct bpf_object *bpf_object__open_buffer(void *obj_buf, | ||
| 31 | size_t obj_buf_sz); | ||
| 32 | void bpf_object__close(struct bpf_object *object); | ||
| 33 | |||
| 34 | /* Load/unload object into/from kernel */ | ||
| 35 | int bpf_object__load(struct bpf_object *obj); | ||
| 36 | int bpf_object__unload(struct bpf_object *obj); | ||
| 37 | |||
| 38 | struct bpf_object *bpf_object__next(struct bpf_object *prev); | ||
| 39 | #define bpf_object__for_each_safe(pos, tmp) \ | ||
| 40 | for ((pos) = bpf_object__next(NULL), \ | ||
| 41 | (tmp) = bpf_object__next(pos); \ | ||
| 42 | (pos) != NULL; \ | ||
| 43 | (pos) = (tmp), (tmp) = bpf_object__next(tmp)) | ||
| 44 | |||
| 45 | /* Accessors of bpf_program. */ | ||
| 46 | struct bpf_program; | ||
| 47 | struct bpf_program *bpf_program__next(struct bpf_program *prog, | ||
| 48 | struct bpf_object *obj); | ||
| 49 | |||
| 50 | #define bpf_object__for_each_program(pos, obj) \ | ||
| 51 | for ((pos) = bpf_program__next(NULL, (obj)); \ | ||
| 52 | (pos) != NULL; \ | ||
| 53 | (pos) = bpf_program__next((pos), (obj))) | ||
| 54 | |||
| 55 | typedef void (*bpf_program_clear_priv_t)(struct bpf_program *, | ||
| 56 | void *); | ||
| 57 | |||
| 58 | int bpf_program__set_private(struct bpf_program *prog, void *priv, | ||
| 59 | bpf_program_clear_priv_t clear_priv); | ||
| 60 | |||
| 61 | int bpf_program__get_private(struct bpf_program *prog, | ||
| 62 | void **ppriv); | ||
| 63 | |||
| 64 | const char *bpf_program__title(struct bpf_program *prog, bool dup); | ||
| 65 | |||
| 66 | int bpf_program__fd(struct bpf_program *prog); | ||
| 67 | |||
| 68 | /* | ||
| 69 | * We don't need __attribute__((packed)) now since it is | ||
| 70 | * unnecessary for 'bpf_map_def' because they are all aligned. | ||
| 71 | * In addition, using it will trigger -Wpacked warning message, | ||
| 72 | * and will be treated as an error due to -Werror. | ||
| 73 | */ | ||
| 74 | struct bpf_map_def { | ||
| 75 | unsigned int type; | ||
| 76 | unsigned int key_size; | ||
| 77 | unsigned int value_size; | ||
| 78 | unsigned int max_entries; | ||
| 79 | }; | ||
| 80 | |||
| 81 | #endif | ||
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index d01a0aad5a01..f31f15a5f873 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST | |||
| @@ -18,6 +18,7 @@ tools/arch/x86/include/asm/atomic.h | |||
| 18 | tools/arch/x86/include/asm/rmwcc.h | 18 | tools/arch/x86/include/asm/rmwcc.h |
| 19 | tools/lib/traceevent | 19 | tools/lib/traceevent |
| 20 | tools/lib/api | 20 | tools/lib/api |
| 21 | tools/lib/bpf | ||
| 21 | tools/lib/hweight.c | 22 | tools/lib/hweight.c |
| 22 | tools/lib/rbtree.c | 23 | tools/lib/rbtree.c |
| 23 | tools/lib/symbol/kallsyms.c | 24 | tools/lib/symbol/kallsyms.c |
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index d20d6e6ab65b..c1518bdd0f1b 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build | |||
| @@ -32,6 +32,7 @@ perf-y += sample-parsing.o | |||
| 32 | perf-y += parse-no-sample-id-all.o | 32 | perf-y += parse-no-sample-id-all.o |
| 33 | perf-y += kmod-path.o | 33 | perf-y += kmod-path.o |
| 34 | perf-y += thread-map.o | 34 | perf-y += thread-map.o |
| 35 | perf-y += llvm.o | ||
| 35 | 36 | ||
| 36 | perf-$(CONFIG_X86) += perf-time-to-tsc.o | 37 | perf-$(CONFIG_X86) += perf-time-to-tsc.o |
| 37 | 38 | ||
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index c1dde733c3a6..136cd934be66 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
| @@ -175,6 +175,10 @@ static struct test { | |||
| 175 | .func = test__thread_map, | 175 | .func = test__thread_map, |
| 176 | }, | 176 | }, |
| 177 | { | 177 | { |
| 178 | .desc = "Test LLVM searching and compiling", | ||
| 179 | .func = test__llvm, | ||
| 180 | }, | ||
| 181 | { | ||
| 178 | .func = NULL, | 182 | .func = NULL, |
| 179 | }, | 183 | }, |
| 180 | }; | 184 | }; |
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c new file mode 100644 index 000000000000..a337356fd979 --- /dev/null +++ b/tools/perf/tests/llvm.c | |||
| @@ -0,0 +1,98 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | #include <bpf/libbpf.h> | ||
| 3 | #include <util/llvm-utils.h> | ||
| 4 | #include <util/cache.h> | ||
| 5 | #include "tests.h" | ||
| 6 | #include "debug.h" | ||
| 7 | |||
| 8 | static int perf_config_cb(const char *var, const char *val, | ||
| 9 | void *arg __maybe_unused) | ||
| 10 | { | ||
| 11 | return perf_default_config(var, val, arg); | ||
| 12 | } | ||
| 13 | |||
| 14 | /* | ||
| 15 | * Randomly give it a "version" section since we don't really load it | ||
| 16 | * into kernel | ||
| 17 | */ | ||
| 18 | static const char test_bpf_prog[] = | ||
| 19 | "__attribute__((section(\"do_fork\"), used)) " | ||
| 20 | "int fork(void *ctx) {return 0;} " | ||
| 21 | "char _license[] __attribute__((section(\"license\"), used)) = \"GPL\";" | ||
| 22 | "int _version __attribute__((section(\"version\"), used)) = 0x40100;"; | ||
| 23 | |||
| 24 | #ifdef HAVE_LIBBPF_SUPPORT | ||
| 25 | static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz) | ||
| 26 | { | ||
| 27 | struct bpf_object *obj; | ||
| 28 | |||
| 29 | obj = bpf_object__open_buffer(obj_buf, obj_buf_sz); | ||
| 30 | if (!obj) | ||
| 31 | return -1; | ||
| 32 | bpf_object__close(obj); | ||
| 33 | return 0; | ||
| 34 | } | ||
| 35 | #else | ||
| 36 | static int test__bpf_parsing(void *obj_buf __maybe_unused, | ||
| 37 | size_t obj_buf_sz __maybe_unused) | ||
| 38 | { | ||
| 39 | fprintf(stderr, " (skip bpf parsing)"); | ||
| 40 | return 0; | ||
| 41 | } | ||
| 42 | #endif | ||
| 43 | |||
| 44 | int test__llvm(void) | ||
| 45 | { | ||
| 46 | char *tmpl_new, *clang_opt_new; | ||
| 47 | void *obj_buf; | ||
| 48 | size_t obj_buf_sz; | ||
| 49 | int err, old_verbose; | ||
| 50 | |||
| 51 | perf_config(perf_config_cb, NULL); | ||
| 52 | |||
| 53 | /* | ||
| 54 | * Skip this test if user's .perfconfig doesn't set [llvm] section | ||
| 55 | * and clang is not found in $PATH, and this is not perf test -v | ||
| 56 | */ | ||
| 57 | if (verbose == 0 && !llvm_param.user_set_param && llvm__search_clang()) { | ||
| 58 | fprintf(stderr, " (no clang, try 'perf test -v LLVM')"); | ||
| 59 | return TEST_SKIP; | ||
| 60 | } | ||
| 61 | |||
| 62 | old_verbose = verbose; | ||
| 63 | /* | ||
| 64 | * llvm is verbosity when error. Suppress all error output if | ||
| 65 | * not 'perf test -v'. | ||
| 66 | */ | ||
| 67 | if (verbose == 0) | ||
| 68 | verbose = -1; | ||
| 69 | |||
| 70 | if (!llvm_param.clang_bpf_cmd_template) | ||
| 71 | return -1; | ||
| 72 | |||
| 73 | if (!llvm_param.clang_opt) | ||
| 74 | llvm_param.clang_opt = strdup(""); | ||
| 75 | |||
| 76 | err = asprintf(&tmpl_new, "echo '%s' | %s", test_bpf_prog, | ||
| 77 | llvm_param.clang_bpf_cmd_template); | ||
| 78 | if (err < 0) | ||
| 79 | return -1; | ||
| 80 | err = asprintf(&clang_opt_new, "-xc %s", llvm_param.clang_opt); | ||
| 81 | if (err < 0) | ||
| 82 | return -1; | ||
| 83 | |||
| 84 | llvm_param.clang_bpf_cmd_template = tmpl_new; | ||
| 85 | llvm_param.clang_opt = clang_opt_new; | ||
| 86 | err = llvm__compile_bpf("-", &obj_buf, &obj_buf_sz); | ||
| 87 | |||
| 88 | verbose = old_verbose; | ||
| 89 | if (err) { | ||
| 90 | if (!verbose) | ||
| 91 | fprintf(stderr, " (use -v to see error message)"); | ||
| 92 | return -1; | ||
| 93 | } | ||
| 94 | |||
| 95 | err = test__bpf_parsing(obj_buf, obj_buf_sz); | ||
| 96 | free(obj_buf); | ||
| 97 | return err; | ||
| 98 | } | ||
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index ebb47d96bc0b..bf113a247987 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h | |||
| @@ -62,6 +62,7 @@ int test__fdarray__filter(void); | |||
| 62 | int test__fdarray__add(void); | 62 | int test__fdarray__add(void); |
| 63 | int test__kmod_path__parse(void); | 63 | int test__kmod_path__parse(void); |
| 64 | int test__thread_map(void); | 64 | int test__thread_map(void); |
| 65 | int test__llvm(void); | ||
| 65 | 66 | ||
| 66 | #if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__) | 67 | #if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__) |
| 67 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 68 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index a1e5168dc1fb..2ee81d74cf45 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build | |||
| @@ -14,6 +14,7 @@ libperf-y += find_next_bit.o | |||
| 14 | libperf-y += help.o | 14 | libperf-y += help.o |
| 15 | libperf-y += kallsyms.o | 15 | libperf-y += kallsyms.o |
| 16 | libperf-y += levenshtein.o | 16 | libperf-y += levenshtein.o |
| 17 | libperf-y += llvm-utils.o | ||
| 17 | libperf-y += parse-options.o | 18 | libperf-y += parse-options.o |
| 18 | libperf-y += parse-events.o | 19 | libperf-y += parse-events.o |
| 19 | libperf-y += path.o | 20 | libperf-y += path.o |
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index e18f653cd7db..2e452ac1353d 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "cache.h" | 12 | #include "cache.h" |
| 13 | #include "exec_cmd.h" | 13 | #include "exec_cmd.h" |
| 14 | #include "util/hist.h" /* perf_hist_config */ | 14 | #include "util/hist.h" /* perf_hist_config */ |
| 15 | #include "util/llvm-utils.h" /* perf_llvm_config */ | ||
| 15 | 16 | ||
| 16 | #define MAXNAME (256) | 17 | #define MAXNAME (256) |
| 17 | 18 | ||
| @@ -408,6 +409,9 @@ int perf_default_config(const char *var, const char *value, | |||
| 408 | if (!prefixcmp(var, "call-graph.")) | 409 | if (!prefixcmp(var, "call-graph.")) |
| 409 | return perf_callchain_config(var, value); | 410 | return perf_callchain_config(var, value); |
| 410 | 411 | ||
| 412 | if (!prefixcmp(var, "llvm.")) | ||
| 413 | return perf_llvm_config(var, value); | ||
| 414 | |||
| 411 | /* Add other config variables here. */ | 415 | /* Add other config variables here. */ |
| 412 | return 0; | 416 | return 0; |
| 413 | } | 417 | } |
diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c new file mode 100644 index 000000000000..4f6a4780bd5f --- /dev/null +++ b/tools/perf/util/llvm-utils.c | |||
| @@ -0,0 +1,408 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2015, Wang Nan <wangnan0@huawei.com> | ||
| 3 | * Copyright (C) 2015, Huawei Inc. | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <stdio.h> | ||
| 7 | #include <sys/utsname.h> | ||
| 8 | #include "util.h" | ||
| 9 | #include "debug.h" | ||
| 10 | #include "llvm-utils.h" | ||
| 11 | #include "cache.h" | ||
| 12 | |||
| 13 | #define CLANG_BPF_CMD_DEFAULT_TEMPLATE \ | ||
| 14 | "$CLANG_EXEC -D__KERNEL__ $CLANG_OPTIONS " \ | ||
| 15 | "$KERNEL_INC_OPTIONS -Wno-unused-value " \ | ||
| 16 | "-Wno-pointer-sign -working-directory " \ | ||
| 17 | "$WORKING_DIR -c \"$CLANG_SOURCE\" -target bpf -O2 -o -" | ||
| 18 | |||
| 19 | struct llvm_param llvm_param = { | ||
| 20 | .clang_path = "clang", | ||
| 21 | .clang_bpf_cmd_template = CLANG_BPF_CMD_DEFAULT_TEMPLATE, | ||
| 22 | .clang_opt = NULL, | ||
| 23 | .kbuild_dir = NULL, | ||
| 24 | .kbuild_opts = NULL, | ||
| 25 | .user_set_param = false, | ||
| 26 | }; | ||
| 27 | |||
| 28 | int perf_llvm_config(const char *var, const char *value) | ||
| 29 | { | ||
| 30 | if (prefixcmp(var, "llvm.")) | ||
| 31 | return 0; | ||
| 32 | var += sizeof("llvm.") - 1; | ||
| 33 | |||
| 34 | if (!strcmp(var, "clang-path")) | ||
| 35 | llvm_param.clang_path = strdup(value); | ||
| 36 | else if (!strcmp(var, "clang-bpf-cmd-template")) | ||
| 37 | llvm_param.clang_bpf_cmd_template = strdup(value); | ||
| 38 | else if (!strcmp(var, "clang-opt")) | ||
| 39 | llvm_param.clang_opt = strdup(value); | ||
| 40 | else if (!strcmp(var, "kbuild-dir")) | ||
| 41 | llvm_param.kbuild_dir = strdup(value); | ||
| 42 | else if (!strcmp(var, "kbuild-opts")) | ||
| 43 | llvm_param.kbuild_opts = strdup(value); | ||
| 44 | else | ||
| 45 | return -1; | ||
| 46 | llvm_param.user_set_param = true; | ||
| 47 | return 0; | ||
| 48 | } | ||
| 49 | |||
| 50 | static int | ||
| 51 | search_program(const char *def, const char *name, | ||
| 52 | char *output) | ||
| 53 | { | ||
| 54 | char *env, *path, *tmp = NULL; | ||
| 55 | char buf[PATH_MAX]; | ||
| 56 | int ret; | ||
| 57 | |||
| 58 | output[0] = '\0'; | ||
| 59 | if (def && def[0] != '\0') { | ||
| 60 | if (def[0] == '/') { | ||
| 61 | if (access(def, F_OK) == 0) { | ||
| 62 | strlcpy(output, def, PATH_MAX); | ||
| 63 | return 0; | ||
| 64 | } | ||
| 65 | } else if (def[0] != '\0') | ||
| 66 | name = def; | ||
| 67 | } | ||
| 68 | |||
| 69 | env = getenv("PATH"); | ||
| 70 | if (!env) | ||
| 71 | return -1; | ||
| 72 | env = strdup(env); | ||
| 73 | if (!env) | ||
| 74 | return -1; | ||
| 75 | |||
| 76 | ret = -ENOENT; | ||
| 77 | path = strtok_r(env, ":", &tmp); | ||
| 78 | while (path) { | ||
| 79 | scnprintf(buf, sizeof(buf), "%s/%s", path, name); | ||
| 80 | if (access(buf, F_OK) == 0) { | ||
| 81 | strlcpy(output, buf, PATH_MAX); | ||
| 82 | ret = 0; | ||
| 83 | break; | ||
| 84 | } | ||
| 85 | path = strtok_r(NULL, ":", &tmp); | ||
| 86 | } | ||
| 87 | |||
| 88 | free(env); | ||
| 89 | return ret; | ||
| 90 | } | ||
| 91 | |||
| 92 | #define READ_SIZE 4096 | ||
| 93 | static int | ||
| 94 | read_from_pipe(const char *cmd, void **p_buf, size_t *p_read_sz) | ||
| 95 | { | ||
| 96 | int err = 0; | ||
| 97 | void *buf = NULL; | ||
| 98 | FILE *file = NULL; | ||
| 99 | size_t read_sz = 0, buf_sz = 0; | ||
| 100 | |||
| 101 | file = popen(cmd, "r"); | ||
| 102 | if (!file) { | ||
| 103 | pr_err("ERROR: unable to popen cmd: %s\n", | ||
| 104 | strerror(errno)); | ||
| 105 | return -EINVAL; | ||
| 106 | } | ||
| 107 | |||
| 108 | while (!feof(file) && !ferror(file)) { | ||
| 109 | /* | ||
| 110 | * Make buf_sz always have obe byte extra space so we | ||
| 111 | * can put '\0' there. | ||
| 112 | */ | ||
| 113 | if (buf_sz - read_sz < READ_SIZE + 1) { | ||
| 114 | void *new_buf; | ||
| 115 | |||
| 116 | buf_sz = read_sz + READ_SIZE + 1; | ||
| 117 | new_buf = realloc(buf, buf_sz); | ||
| 118 | |||
| 119 | if (!new_buf) { | ||
| 120 | pr_err("ERROR: failed to realloc memory\n"); | ||
| 121 | err = -ENOMEM; | ||
| 122 | goto errout; | ||
| 123 | } | ||
| 124 | |||
| 125 | buf = new_buf; | ||
| 126 | } | ||
| 127 | read_sz += fread(buf + read_sz, 1, READ_SIZE, file); | ||
| 128 | } | ||
| 129 | |||
| 130 | if (buf_sz - read_sz < 1) { | ||
| 131 | pr_err("ERROR: internal error\n"); | ||
| 132 | err = -EINVAL; | ||
| 133 | goto errout; | ||
| 134 | } | ||
| 135 | |||
| 136 | if (ferror(file)) { | ||
| 137 | pr_err("ERROR: error occurred when reading from pipe: %s\n", | ||
| 138 | strerror(errno)); | ||
| 139 | err = -EIO; | ||
| 140 | goto errout; | ||
| 141 | } | ||
| 142 | |||
| 143 | err = WEXITSTATUS(pclose(file)); | ||
| 144 | file = NULL; | ||
| 145 | if (err) { | ||
| 146 | err = -EINVAL; | ||
| 147 | goto errout; | ||
| 148 | } | ||
| 149 | |||
| 150 | /* | ||
| 151 | * If buf is string, give it terminal '\0' to make our life | ||
| 152 | * easier. If buf is not string, that '\0' is out of space | ||
| 153 | * indicated by read_sz so caller won't even notice it. | ||
| 154 | */ | ||
| 155 | ((char *)buf)[read_sz] = '\0'; | ||
| 156 | |||
| 157 | if (!p_buf) | ||
| 158 | free(buf); | ||
| 159 | else | ||
| 160 | *p_buf = buf; | ||
| 161 | |||
| 162 | if (p_read_sz) | ||
| 163 | *p_read_sz = read_sz; | ||
| 164 | return 0; | ||
| 165 | |||
| 166 | errout: | ||
| 167 | if (file) | ||
| 168 | pclose(file); | ||
| 169 | free(buf); | ||
| 170 | if (p_buf) | ||
| 171 | *p_buf = NULL; | ||
| 172 | if (p_read_sz) | ||
| 173 | *p_read_sz = 0; | ||
| 174 | return err; | ||
| 175 | } | ||
| 176 | |||
| 177 | static inline void | ||
| 178 | force_set_env(const char *var, const char *value) | ||
| 179 | { | ||
| 180 | if (value) { | ||
| 181 | setenv(var, value, 1); | ||
| 182 | pr_debug("set env: %s=%s\n", var, value); | ||
| 183 | } else { | ||
| 184 | unsetenv(var); | ||
| 185 | pr_debug("unset env: %s\n", var); | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | static void | ||
| 190 | version_notice(void) | ||
| 191 | { | ||
| 192 | pr_err( | ||
| 193 | " \tLLVM 3.7 or newer is required. Which can be found from http://llvm.org\n" | ||
| 194 | " \tYou may want to try git trunk:\n" | ||
| 195 | " \t\tgit clone http://llvm.org/git/llvm.git\n" | ||
| 196 | " \t\t and\n" | ||
| 197 | " \t\tgit clone http://llvm.org/git/clang.git\n\n" | ||
| 198 | " \tOr fetch the latest clang/llvm 3.7 from pre-built llvm packages for\n" | ||
| 199 | " \tdebian/ubuntu:\n" | ||
| 200 | " \t\thttp://llvm.org/apt\n\n" | ||
| 201 | " \tIf you are using old version of clang, change 'clang-bpf-cmd-template'\n" | ||
| 202 | " \toption in [llvm] section of ~/.perfconfig to:\n\n" | ||
| 203 | " \t \"$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS \\\n" | ||
| 204 | " \t -working-directory $WORKING_DIR -c $CLANG_SOURCE \\\n" | ||
| 205 | " \t -emit-llvm -o - | /path/to/llc -march=bpf -filetype=obj -o -\"\n" | ||
| 206 | " \t(Replace /path/to/llc with path to your llc)\n\n" | ||
| 207 | ); | ||
| 208 | } | ||
| 209 | |||
| 210 | static int detect_kbuild_dir(char **kbuild_dir) | ||
| 211 | { | ||
| 212 | const char *test_dir = llvm_param.kbuild_dir; | ||
| 213 | const char *prefix_dir = ""; | ||
| 214 | const char *suffix_dir = ""; | ||
| 215 | |||
| 216 | char *autoconf_path; | ||
| 217 | struct utsname utsname; | ||
| 218 | |||
| 219 | int err; | ||
| 220 | |||
| 221 | if (!test_dir) { | ||
| 222 | err = uname(&utsname); | ||
| 223 | if (err) { | ||
| 224 | pr_warning("uname failed: %s\n", strerror(errno)); | ||
| 225 | return -EINVAL; | ||
| 226 | } | ||
| 227 | |||
| 228 | test_dir = utsname.release; | ||
| 229 | prefix_dir = "/lib/modules/"; | ||
| 230 | suffix_dir = "/build"; | ||
| 231 | } | ||
| 232 | |||
| 233 | err = asprintf(&autoconf_path, "%s%s%s/include/generated/autoconf.h", | ||
| 234 | prefix_dir, test_dir, suffix_dir); | ||
| 235 | if (err < 0) | ||
| 236 | return -ENOMEM; | ||
| 237 | |||
| 238 | if (access(autoconf_path, R_OK) == 0) { | ||
| 239 | free(autoconf_path); | ||
| 240 | |||
| 241 | err = asprintf(kbuild_dir, "%s%s%s", prefix_dir, test_dir, | ||
| 242 | suffix_dir); | ||
| 243 | if (err < 0) | ||
| 244 | return -ENOMEM; | ||
| 245 | return 0; | ||
| 246 | } | ||
| 247 | free(autoconf_path); | ||
| 248 | return -ENOENT; | ||
| 249 | } | ||
| 250 | |||
| 251 | static const char *kinc_fetch_script = | ||
| 252 | "#!/usr/bin/env sh\n" | ||
| 253 | "if ! test -d \"$KBUILD_DIR\"\n" | ||
| 254 | "then\n" | ||
| 255 | " exit -1\n" | ||
| 256 | "fi\n" | ||
| 257 | "if ! test -f \"$KBUILD_DIR/include/generated/autoconf.h\"\n" | ||
| 258 | "then\n" | ||
| 259 | " exit -1\n" | ||
| 260 | "fi\n" | ||
| 261 | "TMPDIR=`mktemp -d`\n" | ||
| 262 | "if test -z \"$TMPDIR\"\n" | ||
| 263 | "then\n" | ||
| 264 | " exit -1\n" | ||
| 265 | "fi\n" | ||
| 266 | "cat << EOF > $TMPDIR/Makefile\n" | ||
| 267 | "obj-y := dummy.o\n" | ||
| 268 | "\\$(obj)/%.o: \\$(src)/%.c\n" | ||
| 269 | "\t@echo -n \"\\$(NOSTDINC_FLAGS) \\$(LINUXINCLUDE) \\$(EXTRA_CFLAGS)\"\n" | ||
| 270 | "EOF\n" | ||
| 271 | "touch $TMPDIR/dummy.c\n" | ||
| 272 | "make -s -C $KBUILD_DIR M=$TMPDIR $KBUILD_OPTS dummy.o 2>/dev/null\n" | ||
| 273 | "RET=$?\n" | ||
| 274 | "rm -rf $TMPDIR\n" | ||
| 275 | "exit $RET\n"; | ||
| 276 | |||
| 277 | static inline void | ||
| 278 | get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts) | ||
| 279 | { | ||
| 280 | int err; | ||
| 281 | |||
| 282 | if (!kbuild_dir || !kbuild_include_opts) | ||
| 283 | return; | ||
| 284 | |||
| 285 | *kbuild_dir = NULL; | ||
| 286 | *kbuild_include_opts = NULL; | ||
| 287 | |||
| 288 | if (llvm_param.kbuild_dir && !llvm_param.kbuild_dir[0]) { | ||
| 289 | pr_debug("[llvm.kbuild-dir] is set to \"\" deliberately.\n"); | ||
| 290 | pr_debug("Skip kbuild options detection.\n"); | ||
| 291 | return; | ||
| 292 | } | ||
| 293 | |||
| 294 | err = detect_kbuild_dir(kbuild_dir); | ||
| 295 | if (err) { | ||
| 296 | pr_warning( | ||
| 297 | "WARNING:\tunable to get correct kernel building directory.\n" | ||
| 298 | "Hint:\tSet correct kbuild directory using 'kbuild-dir' option in [llvm]\n" | ||
| 299 | " \tsection of ~/.perfconfig or set it to \"\" to suppress kbuild\n" | ||
| 300 | " \tdetection.\n\n"); | ||
| 301 | return; | ||
| 302 | } | ||
| 303 | |||
| 304 | pr_debug("Kernel build dir is set to %s\n", *kbuild_dir); | ||
| 305 | force_set_env("KBUILD_DIR", *kbuild_dir); | ||
| 306 | force_set_env("KBUILD_OPTS", llvm_param.kbuild_opts); | ||
| 307 | err = read_from_pipe(kinc_fetch_script, | ||
| 308 | (void **)kbuild_include_opts, | ||
| 309 | NULL); | ||
| 310 | if (err) { | ||
| 311 | pr_warning( | ||
| 312 | "WARNING:\tunable to get kernel include directories from '%s'\n" | ||
| 313 | "Hint:\tTry set clang include options using 'clang-bpf-cmd-template'\n" | ||
| 314 | " \toption in [llvm] section of ~/.perfconfig and set 'kbuild-dir'\n" | ||
| 315 | " \toption in [llvm] to \"\" to suppress this detection.\n\n", | ||
| 316 | *kbuild_dir); | ||
| 317 | |||
| 318 | free(*kbuild_dir); | ||
| 319 | *kbuild_dir = NULL; | ||
| 320 | return; | ||
| 321 | } | ||
| 322 | |||
| 323 | pr_debug("include option is set to %s\n", *kbuild_include_opts); | ||
| 324 | } | ||
| 325 | |||
| 326 | int llvm__compile_bpf(const char *path, void **p_obj_buf, | ||
| 327 | size_t *p_obj_buf_sz) | ||
| 328 | { | ||
| 329 | int err; | ||
| 330 | char clang_path[PATH_MAX]; | ||
| 331 | const char *clang_opt = llvm_param.clang_opt; | ||
| 332 | const char *template = llvm_param.clang_bpf_cmd_template; | ||
| 333 | char *kbuild_dir = NULL, *kbuild_include_opts = NULL; | ||
| 334 | void *obj_buf = NULL; | ||
| 335 | size_t obj_buf_sz; | ||
| 336 | |||
| 337 | if (!template) | ||
| 338 | template = CLANG_BPF_CMD_DEFAULT_TEMPLATE; | ||
| 339 | |||
| 340 | err = search_program(llvm_param.clang_path, | ||
| 341 | "clang", clang_path); | ||
| 342 | if (err) { | ||
| 343 | pr_err( | ||
| 344 | "ERROR:\tunable to find clang.\n" | ||
| 345 | "Hint:\tTry to install latest clang/llvm to support BPF. Check your $PATH\n" | ||
| 346 | " \tand 'clang-path' option in [llvm] section of ~/.perfconfig.\n"); | ||
| 347 | version_notice(); | ||
| 348 | return -ENOENT; | ||
| 349 | } | ||
| 350 | |||
| 351 | /* | ||
| 352 | * This is an optional work. Even it fail we can continue our | ||
| 353 | * work. Needn't to check error return. | ||
| 354 | */ | ||
| 355 | get_kbuild_opts(&kbuild_dir, &kbuild_include_opts); | ||
| 356 | |||
| 357 | force_set_env("CLANG_EXEC", clang_path); | ||
| 358 | force_set_env("CLANG_OPTIONS", clang_opt); | ||
| 359 | force_set_env("KERNEL_INC_OPTIONS", kbuild_include_opts); | ||
| 360 | force_set_env("WORKING_DIR", kbuild_dir ? : "."); | ||
| 361 | |||
| 362 | /* | ||
| 363 | * Since we may reset clang's working dir, path of source file | ||
| 364 | * should be transferred into absolute path, except we want | ||
| 365 | * stdin to be source file (testing). | ||
| 366 | */ | ||
| 367 | force_set_env("CLANG_SOURCE", | ||
| 368 | (path[0] == '-') ? path : | ||
| 369 | make_nonrelative_path(path)); | ||
| 370 | |||
| 371 | pr_debug("llvm compiling command template: %s\n", template); | ||
| 372 | err = read_from_pipe(template, &obj_buf, &obj_buf_sz); | ||
| 373 | if (err) { | ||
| 374 | pr_err("ERROR:\tunable to compile %s\n", path); | ||
| 375 | pr_err("Hint:\tCheck error message shown above.\n"); | ||
| 376 | pr_err("Hint:\tYou can also pre-compile it into .o using:\n"); | ||
| 377 | pr_err(" \t\tclang -target bpf -O2 -c %s\n", path); | ||
| 378 | pr_err(" \twith proper -I and -D options.\n"); | ||
| 379 | goto errout; | ||
| 380 | } | ||
| 381 | |||
| 382 | free(kbuild_dir); | ||
| 383 | free(kbuild_include_opts); | ||
| 384 | if (!p_obj_buf) | ||
| 385 | free(obj_buf); | ||
| 386 | else | ||
| 387 | *p_obj_buf = obj_buf; | ||
| 388 | |||
| 389 | if (p_obj_buf_sz) | ||
| 390 | *p_obj_buf_sz = obj_buf_sz; | ||
| 391 | return 0; | ||
| 392 | errout: | ||
| 393 | free(kbuild_dir); | ||
| 394 | free(kbuild_include_opts); | ||
| 395 | free(obj_buf); | ||
| 396 | if (p_obj_buf) | ||
| 397 | *p_obj_buf = NULL; | ||
| 398 | if (p_obj_buf_sz) | ||
| 399 | *p_obj_buf_sz = 0; | ||
| 400 | return err; | ||
| 401 | } | ||
| 402 | |||
| 403 | int llvm__search_clang(void) | ||
| 404 | { | ||
| 405 | char clang_path[PATH_MAX]; | ||
| 406 | |||
| 407 | return search_program(llvm_param.clang_path, "clang", clang_path); | ||
| 408 | } | ||
diff --git a/tools/perf/util/llvm-utils.h b/tools/perf/util/llvm-utils.h new file mode 100644 index 000000000000..5b3cf1c229e2 --- /dev/null +++ b/tools/perf/util/llvm-utils.h | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2015, Wang Nan <wangnan0@huawei.com> | ||
| 3 | * Copyright (C) 2015, Huawei Inc. | ||
| 4 | */ | ||
| 5 | #ifndef __LLVM_UTILS_H | ||
| 6 | #define __LLVM_UTILS_H | ||
| 7 | |||
| 8 | #include "debug.h" | ||
| 9 | |||
| 10 | struct llvm_param { | ||
| 11 | /* Path of clang executable */ | ||
| 12 | const char *clang_path; | ||
| 13 | /* | ||
| 14 | * Template of clang bpf compiling. 5 env variables | ||
| 15 | * can be used: | ||
| 16 | * $CLANG_EXEC: Path to clang. | ||
| 17 | * $CLANG_OPTIONS: Extra options to clang. | ||
| 18 | * $KERNEL_INC_OPTIONS: Kernel include directories. | ||
| 19 | * $WORKING_DIR: Kernel source directory. | ||
| 20 | * $CLANG_SOURCE: Source file to be compiled. | ||
| 21 | */ | ||
| 22 | const char *clang_bpf_cmd_template; | ||
| 23 | /* Will be filled in $CLANG_OPTIONS */ | ||
| 24 | const char *clang_opt; | ||
| 25 | /* Where to find kbuild system */ | ||
| 26 | const char *kbuild_dir; | ||
| 27 | /* | ||
| 28 | * Arguments passed to make, like 'ARCH=arm' if doing cross | ||
| 29 | * compiling. Should not be used for dynamic compiling. | ||
| 30 | */ | ||
| 31 | const char *kbuild_opts; | ||
| 32 | /* | ||
| 33 | * Default is false. If one of the above fields is set by user | ||
| 34 | * explicitly then user_set_llvm is set to true. This is used | ||
| 35 | * for perf test. If user doesn't set anything in .perfconfig | ||
| 36 | * and clang is not found, don't trigger llvm test. | ||
| 37 | */ | ||
| 38 | bool user_set_param; | ||
| 39 | }; | ||
| 40 | |||
| 41 | extern struct llvm_param llvm_param; | ||
| 42 | extern int perf_llvm_config(const char *var, const char *value); | ||
| 43 | |||
| 44 | extern int llvm__compile_bpf(const char *path, void **p_obj_buf, | ||
| 45 | size_t *p_obj_buf_sz); | ||
| 46 | |||
| 47 | /* This function is for test__llvm() use only */ | ||
| 48 | extern int llvm__search_clang(void); | ||
| 49 | #endif | ||
