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:
Ingo Molnar 2015-08-08 10:05:17 +02:00
commit f1d800bf61
18 changed files with 2014 additions and 1 deletions

View File

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

View 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
View File

@ -0,0 +1,2 @@
libbpf_version.h
FEATURE-DUMP

1
tools/lib/bpf/Build Normal file
View File

@ -0,0 +1 @@
libbpf-y := libbpf.o bpf.o

195
tools/lib/bpf/Makefile Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

81
tools/lib/bpf/libbpf.h Normal file
View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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);
}

View 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