diff options
Diffstat (limited to 'tools/perf')
-rw-r--r-- | tools/perf/Makefile.config | 62 | ||||
-rw-r--r-- | tools/perf/Makefile.perf | 56 | ||||
-rw-r--r-- | tools/perf/tests/Build | 1 | ||||
-rw-r--r-- | tools/perf/tests/builtin-test.c | 9 | ||||
-rw-r--r-- | tools/perf/tests/clang.c | 46 | ||||
-rw-r--r-- | tools/perf/tests/llvm.h | 7 | ||||
-rw-r--r-- | tools/perf/tests/make | 4 | ||||
-rw-r--r-- | tools/perf/tests/perf-hooks.c | 14 | ||||
-rw-r--r-- | tools/perf/tests/tests.h | 3 | ||||
-rw-r--r-- | tools/perf/util/Build | 2 | ||||
-rw-r--r-- | tools/perf/util/annotate.c | 3 | ||||
-rw-r--r-- | tools/perf/util/bpf-loader.c | 19 | ||||
-rw-r--r-- | tools/perf/util/c++/Build | 2 | ||||
-rw-r--r-- | tools/perf/util/c++/clang-c.h | 43 | ||||
-rw-r--r-- | tools/perf/util/c++/clang-test.cpp | 62 | ||||
-rw-r--r-- | tools/perf/util/c++/clang.cpp | 195 | ||||
-rw-r--r-- | tools/perf/util/c++/clang.h | 26 | ||||
-rw-r--r-- | tools/perf/util/llvm-utils.c | 76 | ||||
-rw-r--r-- | tools/perf/util/llvm-utils.h | 6 | ||||
-rw-r--r-- | tools/perf/util/perf-hooks.c | 10 | ||||
-rw-r--r-- | tools/perf/util/perf-hooks.h | 6 | ||||
-rw-r--r-- | tools/perf/util/util-cxx.h | 26 |
22 files changed, 615 insertions, 63 deletions
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 8a493d46fab9..09c2a9874f2f 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -136,6 +136,7 @@ endif # Treat warnings as errors unless directed not to ifneq ($(WERROR),0) CFLAGS += -Werror + CXXFLAGS += -Werror endif ifndef DEBUG @@ -182,6 +183,13 @@ CFLAGS += -Wall CFLAGS += -Wextra CFLAGS += -std=gnu99 +CXXFLAGS += -std=gnu++11 -fno-exceptions -fno-rtti +CXXFLAGS += -Wall +CXXFLAGS += -fno-omit-frame-pointer +CXXFLAGS += -ggdb3 +CXXFLAGS += -funwind-tables +CXXFLAGS += -Wno-strict-aliasing + # Enforce a non-executable stack, as we may regress (again) in the future by # adding assembler files missing the .GNU-stack linker note. LDFLAGS += -Wl,-z,noexecstack @@ -204,24 +212,27 @@ ifeq ($(DEBUG),0) endif endif -CFLAGS += -I$(src-perf)/util/include -CFLAGS += -I$(src-perf)/arch/$(ARCH)/include -CFLAGS += -I$(srctree)/tools/include/uapi -CFLAGS += -I$(srctree)/tools/include/ -CFLAGS += -I$(srctree)/tools/arch/$(ARCH)/include/uapi -CFLAGS += -I$(srctree)/tools/arch/$(ARCH)/include/ -CFLAGS += -I$(srctree)/tools/arch/$(ARCH)/ +INC_FLAGS += -I$(src-perf)/util/include +INC_FLAGS += -I$(src-perf)/arch/$(ARCH)/include +INC_FLAGS += -I$(srctree)/tools/include/uapi +INC_FLAGS += -I$(srctree)/tools/include/ +INC_FLAGS += -I$(srctree)/tools/arch/$(ARCH)/include/uapi +INC_FLAGS += -I$(srctree)/tools/arch/$(ARCH)/include/ +INC_FLAGS += -I$(srctree)/tools/arch/$(ARCH)/ # $(obj-perf) for generated common-cmds.h # $(obj-perf)/util for generated bison/flex headers ifneq ($(OUTPUT),) -CFLAGS += -I$(obj-perf)/util -CFLAGS += -I$(obj-perf) +INC_FLAGS += -I$(obj-perf)/util +INC_FLAGS += -I$(obj-perf) endif -CFLAGS += -I$(src-perf)/util -CFLAGS += -I$(src-perf) -CFLAGS += -I$(srctree)/tools/lib/ +INC_FLAGS += -I$(src-perf)/util +INC_FLAGS += -I$(src-perf) +INC_FLAGS += -I$(srctree)/tools/lib/ + +CFLAGS += $(INC_FLAGS) +CXXFLAGS += $(INC_FLAGS) CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE @@ -783,6 +794,33 @@ ifndef NO_JVMTI endif endif +USE_CXX = 0 +USE_CLANGLLVM = 0 +ifdef LIBCLANGLLVM + $(call feature_check,cxx) + ifneq ($(feature-cxx), 1) + msg := $(warning No g++ found, disable clang and llvm support. Please install g++) + else + $(call feature_check,llvm) + ifneq ($(feature-llvm), 1) + msg := $(warning No libLLVM found, disable clang and llvm support. Please install llvm-dev) + else + $(call feature_check,clang) + ifneq ($(feature-clang), 1) + msg := $(warning No libclang found, disable clang and llvm support. Please install libclang-dev) + else + CFLAGS += -DHAVE_LIBCLANGLLVM_SUPPORT + CXXFLAGS += -DHAVE_LIBCLANGLLVM_SUPPORT -I$(shell $(LLVM_CONFIG) --includedir) + $(call detected,CONFIG_CXX) + $(call detected,CONFIG_CLANGLLVM) + USE_CXX = 1 + USE_LLVM = 1 + USE_CLANG = 1 + endif + endif + endif +endif + # Among the variables below, these: # perfexecdir # template_dir diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 3cb1df43ad3e..10495c9dbe71 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -88,6 +88,10 @@ include ../scripts/utilities.mak # and bypass the feature detection # # Define NO_JVMTI if you do not want jvmti agent built +# +# Define LIBCLANGLLVM if you DO want builtin clang and llvm support. +# When selected, pass LLVM_CONFIG=/path/to/llvm-config to `make' if +# llvm-config is not in $PATH. # As per kernel Makefile, avoid funny character set dependencies unexport LC_ALL @@ -143,6 +147,7 @@ endef $(call allow-override,CC,$(CROSS_COMPILE)gcc) $(call allow-override,AR,$(CROSS_COMPILE)ar) $(call allow-override,LD,$(CROSS_COMPILE)ld) +$(call allow-override,CXX,$(CROSS_COMPILE)g++) LD += $(EXTRA_LDFLAGS) @@ -151,6 +156,7 @@ HOSTLD ?= ld HOSTAR ?= ar PKG_CONFIG = $(CROSS_COMPILE)pkg-config +LLVM_CONFIG ?= llvm-config RM = rm -f LN = ln -f @@ -171,7 +177,7 @@ SUBCMD_DIR = $(srctree)/tools/lib/subcmd/ # non-config cases config := 1 -NON_CONFIG_TARGETS := clean TAGS tags cscope help install-doc +NON_CONFIG_TARGETS := clean TAGS tags cscope help install-doc install-man install-html install-info install-pdf doc man html info pdf ifdef MAKECMDGOALS ifeq ($(filter-out $(NON_CONFIG_TARGETS),$(MAKECMDGOALS)),) @@ -262,17 +268,6 @@ python-clean := $(call QUIET_CLEAN, python) $(RM) -r $(PYTHON_EXTBUILD) $(OUTPUT PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources) PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py $(LIBTRACEEVENT) $(LIBAPI) -$(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) $(LIBTRACEEVENT_DYNAMIC_LIST) - $(QUIET_GEN)LDSHARED="$(CC) -pthread -shared" \ - CFLAGS='$(CFLAGS)' LDFLAGS='$(LDFLAGS) $(LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS)' \ - $(PYTHON_WORD) util/setup.py \ - --quiet build_ext; \ - mkdir -p $(OUTPUT)python && \ - cp $(PYTHON_EXTBUILD_LIB)perf.so $(OUTPUT)python/ -# -# No Perl scripts right now: -# - SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) PROGRAMS += $(OUTPUT)perf @@ -325,11 +320,6 @@ endif ifndef NO_GTK2 ALL_PROGRAMS += $(OUTPUT)libperf-gtk.so GTK_IN := $(OUTPUT)gtk-in.o - -install-gtk: $(OUTPUT)libperf-gtk.so - $(call QUIET_INSTALL, 'GTK UI') \ - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(libdir_SQ)'; \ - $(INSTALL) $(OUTPUT)libperf-gtk.so '$(DESTDIR_SQ)$(libdir_SQ)' endif ifdef ASCIIDOC8 @@ -338,6 +328,21 @@ endif LIBS = -Wl,--whole-archive $(PERFLIBS) -Wl,--no-whole-archive -Wl,--start-group $(EXTLIBS) -Wl,--end-group +ifeq ($(USE_CLANG), 1) + CLANGLIBS_LIST = AST Basic CodeGen Driver Frontend Lex Tooling Edit Sema Analysis Parse Serialization + LIBCLANG = $(foreach l,$(CLANGLIBS_LIST),$(wildcard $(shell $(LLVM_CONFIG) --libdir)/libclang$(l).a)) + LIBS += -Wl,--start-group $(LIBCLANG) -Wl,--end-group +endif + +ifeq ($(USE_LLVM), 1) + LIBLLVM = $(shell $(LLVM_CONFIG) --libs all) $(shell $(LLVM_CONFIG) --system-libs) + LIBS += -L$(shell $(LLVM_CONFIG) --libdir) $(LIBLLVM) +endif + +ifeq ($(USE_CXX), 1) + LIBS += -lstdc++ +endif + export INSTALL SHELL_PATH ### Build rules @@ -346,6 +351,14 @@ SHELL = $(SHELL_PATH) all: shell_compatibility_test $(ALL_PROGRAMS) $(LANG_BINDINGS) $(OTHER_PROGRAMS) +$(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) $(LIBTRACEEVENT_DYNAMIC_LIST) + $(QUIET_GEN)LDSHARED="$(CC) -pthread -shared" \ + CFLAGS='$(CFLAGS)' LDFLAGS='$(LDFLAGS) $(LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS)' \ + $(PYTHON_WORD) util/setup.py \ + --quiet build_ext; \ + mkdir -p $(OUTPUT)python && \ + cp $(PYTHON_EXTBUILD_LIB)perf.so $(OUTPUT)python/ + please_set_SHELL_PATH_to_a_more_modern_shell: $(Q)$$(:) @@ -356,7 +369,7 @@ strip: $(PROGRAMS) $(OUTPUT)perf PERF_IN := $(OUTPUT)perf-in.o -export srctree OUTPUT RM CC LD AR CFLAGS V BISON FLEX AWK +export srctree OUTPUT RM CC CXX LD AR CFLAGS CXXFLAGS V BISON FLEX AWK export HOSTCC HOSTLD HOSTAR include $(srctree)/tools/build/Makefile.include @@ -691,7 +704,14 @@ check: $(OUTPUT)common-cmds.h ### Installation rules +ifndef NO_GTK2 +install-gtk: $(OUTPUT)libperf-gtk.so + $(call QUIET_INSTALL, 'GTK UI') \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(libdir_SQ)'; \ + $(INSTALL) $(OUTPUT)libperf-gtk.so '$(DESTDIR_SQ)$(libdir_SQ)' +else install-gtk: +endif install-tools: all install-gtk $(call QUIET_INSTALL, binaries) \ diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index af3ec94869aa..6676c2dd6dcb 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -43,6 +43,7 @@ perf-y += sdt.o perf-y += is_printable_array.o perf-y += bitmap.o perf-y += perf-hooks.o +perf-y += clang.o $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build $(call rule_mkdir) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index d1bec0444be7..23605202d4a1 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -234,6 +234,15 @@ static struct test generic_tests[] = { .func = test__perf_hooks, }, { + .desc = "builtin clang support", + .func = test__clang, + .subtest = { + .skip_if_fail = true, + .get_nr = test__clang_subtest_get_nr, + .get_desc = test__clang_subtest_get_desc, + } + }, + { .func = NULL, }, }; diff --git a/tools/perf/tests/clang.c b/tools/perf/tests/clang.c new file mode 100644 index 000000000000..f853e242a86c --- /dev/null +++ b/tools/perf/tests/clang.c @@ -0,0 +1,46 @@ +#include "tests.h" +#include "debug.h" +#include "util.h" +#include "c++/clang-c.h" + +static struct { + int (*func)(void); + const char *desc; +} clang_testcase_table[] = { +#ifdef HAVE_LIBCLANGLLVM_SUPPORT + { + .func = test__clang_to_IR, + .desc = "builtin clang compile C source to IR", + }, + { + .func = test__clang_to_obj, + .desc = "builtin clang compile C source to ELF object", + }, +#endif +}; + +int test__clang_subtest_get_nr(void) +{ + return (int)ARRAY_SIZE(clang_testcase_table); +} + +const char *test__clang_subtest_get_desc(int i) +{ + if (i < 0 || i >= (int)ARRAY_SIZE(clang_testcase_table)) + return NULL; + return clang_testcase_table[i].desc; +} + +#ifndef HAVE_LIBCLANGLLVM_SUPPORT +int test__clang(int i __maybe_unused) +{ + return TEST_SKIP; +} +#else +int test__clang(int i) +{ + if (i < 0 || i >= (int)ARRAY_SIZE(clang_testcase_table)) + return TEST_FAIL; + return clang_testcase_table[i].func(); +} +#endif diff --git a/tools/perf/tests/llvm.h b/tools/perf/tests/llvm.h index 0eaa604be99d..b83571758d83 100644 --- a/tools/perf/tests/llvm.h +++ b/tools/perf/tests/llvm.h @@ -1,6 +1,10 @@ #ifndef PERF_TEST_LLVM_H #define PERF_TEST_LLVM_H +#ifdef __cplusplus +extern "C" { +#endif + #include <stddef.h> /* for size_t */ #include <stdbool.h> /* for bool */ @@ -20,4 +24,7 @@ enum test_llvm__testcase { int test_llvm__fetch_bpf_obj(void **p_obj_buf, size_t *p_obj_buf_sz, enum test_llvm__testcase index, bool force, bool *should_load_fail); +#ifdef __cplusplus +} +#endif #endif diff --git a/tools/perf/tests/make b/tools/perf/tests/make index 08ed7f12cc37..0784748f1670 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make @@ -83,6 +83,7 @@ make_no_libbpf := NO_LIBBPF=1 make_no_libcrypto := NO_LIBCRYPTO=1 make_with_babeltrace:= LIBBABELTRACE=1 make_no_sdt := NO_SDT=1 +make_with_clangllvm := LIBCLANGLLVM=1 make_tags := tags make_cscope := cscope make_help := help @@ -139,6 +140,7 @@ run += make_no_libbionic run += make_no_auxtrace run += make_no_libbpf run += make_with_babeltrace +run += make_with_clangllvm run += make_help run += make_doc run += make_perf_o @@ -278,7 +280,7 @@ endif MAKEFLAGS := --no-print-directory -clean := @(cd $(PERF); $(MAKE_F) -s $(O_OPT) clean >/dev/null) +clean := @(cd $(PERF); $(MAKE_F) -s $(O_OPT) clean >/dev/null && $(MAKE) -s $(O_OPT) -C ../build clean >/dev/null) $(run): $(call clean) diff --git a/tools/perf/tests/perf-hooks.c b/tools/perf/tests/perf-hooks.c index 9338cb2c25ab..665ecc19671c 100644 --- a/tools/perf/tests/perf-hooks.c +++ b/tools/perf/tests/perf-hooks.c @@ -15,13 +15,13 @@ static void sigsegv_handler(int sig __maybe_unused) exit(-1); } -static int hook_flags; -static void the_hook(void) +static void the_hook(void *_hook_flags) { + int *hook_flags = _hook_flags; int *p = NULL; - hook_flags = 1234; + *hook_flags = 1234; /* Generate a segfault, test perf_hooks__recover */ *p = 0; @@ -29,13 +29,17 @@ static void the_hook(void) int test__perf_hooks(int subtest __maybe_unused) { + int hook_flags = 0; + signal(SIGSEGV, sigsegv_handler); - perf_hooks__set_hook("test", the_hook); + perf_hooks__set_hook("test", the_hook, &hook_flags); perf_hooks__invoke_test(); /* hook is triggered? */ - if (hook_flags != 1234) + if (hook_flags != 1234) { + pr_debug("Setting failed: %d (%p)\n", hook_flags, &hook_flags); return TEST_FAIL; + } /* the buggy hook is removed? */ if (perf_hooks__get_hook("test")) diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 3a1f98f291ba..0d7b251305af 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -92,6 +92,9 @@ int test__sdt_event(int subtest); int test__is_printable_array(int subtest); int test__bitmap_print(int subtest); int test__perf_hooks(int subtest); +int test__clang(int subtest); +const char *test__clang_subtest_get_desc(int subtest); +int test__clang_subtest_get_nr(void); #if defined(__arm__) || defined(__aarch64__) #ifdef HAVE_DWARF_UNWIND_SUPPORT diff --git a/tools/perf/util/Build b/tools/perf/util/Build index bdad82a9812d..3840e3a87057 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -126,6 +126,8 @@ endif libperf-y += perf-hooks.o +libperf-$(CONFIG_CXX) += c++/ + CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" # avoid compiler warnings in 32-bit mode CFLAGS_genelf_debug.o += -Wno-packed diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 4012b1de2813..ea7e0de4b9c1 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -237,6 +237,9 @@ static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *op static int jump__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops) { + if (!ops->target.addr) + return ins__raw_scnprintf(ins, bf, size, ops); + return scnprintf(bf, size, "%-6.6s %" PRIx64, ins->name, ops->target.offset); } diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index a5fd275238f7..36c861103291 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -14,11 +14,11 @@ #include "debug.h" #include "bpf-loader.h" #include "bpf-prologue.h" -#include "llvm-utils.h" #include "probe-event.h" #include "probe-finder.h" // for MAX_PROBES #include "parse-events.h" #include "llvm-utils.h" +#include "c++/clang-c.h" #define DEFINE_PRINT_FN(name, level) \ static int libbpf_##name(const char *fmt, ...) \ @@ -86,10 +86,21 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source) void *obj_buf; size_t obj_buf_sz; - err = llvm__compile_bpf(filename, &obj_buf, &obj_buf_sz); - if (err) - return ERR_PTR(-BPF_LOADER_ERRNO__COMPILE); + perf_clang__init(); + err = perf_clang__compile_bpf(filename, &obj_buf, &obj_buf_sz); + perf_clang__cleanup(); + if (err) { + pr_warning("bpf: builtin compilation failed: %d, try external compiler\n", err); + err = llvm__compile_bpf(filename, &obj_buf, &obj_buf_sz); + if (err) + return ERR_PTR(-BPF_LOADER_ERRNO__COMPILE); + } else + pr_debug("bpf: successfull builtin compilation\n"); obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, filename); + + if (!IS_ERR(obj) && llvm_param.dump_obj) + llvm__dump_obj(filename, obj_buf, obj_buf_sz); + free(obj_buf); } else obj = bpf_object__open(filename); diff --git a/tools/perf/util/c++/Build b/tools/perf/util/c++/Build new file mode 100644 index 000000000000..988fef1b11d7 --- /dev/null +++ b/tools/perf/util/c++/Build @@ -0,0 +1,2 @@ +libperf-$(CONFIG_CLANGLLVM) += clang.o +libperf-$(CONFIG_CLANGLLVM) += clang-test.o diff --git a/tools/perf/util/c++/clang-c.h b/tools/perf/util/c++/clang-c.h new file mode 100644 index 000000000000..0eadd792ab1f --- /dev/null +++ b/tools/perf/util/c++/clang-c.h @@ -0,0 +1,43 @@ +#ifndef PERF_UTIL_CLANG_C_H +#define PERF_UTIL_CLANG_C_H + +#include <stddef.h> /* for size_t */ +#include <util-cxx.h> /* for __maybe_unused */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_LIBCLANGLLVM_SUPPORT +extern void perf_clang__init(void); +extern void perf_clang__cleanup(void); + +extern int test__clang_to_IR(void); +extern int test__clang_to_obj(void); + +extern int perf_clang__compile_bpf(const char *filename, + void **p_obj_buf, + size_t *p_obj_buf_sz); +#else + + +static inline void perf_clang__init(void) { } +static inline void perf_clang__cleanup(void) { } + +static inline int test__clang_to_IR(void) { return -1; } +static inline int test__clang_to_obj(void) { return -1;} + +static inline int +perf_clang__compile_bpf(const char *filename __maybe_unused, + void **p_obj_buf __maybe_unused, + size_t *p_obj_buf_sz __maybe_unused) +{ + return -ENOTSUP; +} + +#endif + +#ifdef __cplusplus +} +#endif +#endif diff --git a/tools/perf/util/c++/clang-test.cpp b/tools/perf/util/c++/clang-test.cpp new file mode 100644 index 000000000000..9b11e8c82798 --- /dev/null +++ b/tools/perf/util/c++/clang-test.cpp @@ -0,0 +1,62 @@ +#include "clang.h" +#include "clang-c.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/LLVMContext.h" + +#include <util-cxx.h> +#include <tests/llvm.h> +#include <string> + +class perf_clang_scope { +public: + explicit perf_clang_scope() {perf_clang__init();} + ~perf_clang_scope() {perf_clang__cleanup();} +}; + +static std::unique_ptr<llvm::Module> +__test__clang_to_IR(void) +{ + unsigned int kernel_version; + + if (fetch_kernel_version(&kernel_version, NULL, 0)) + return std::unique_ptr<llvm::Module>(nullptr); + + std::string cflag_kver("-DLINUX_VERSION_CODE=" + + std::to_string(kernel_version)); + + std::unique_ptr<llvm::Module> M = + perf::getModuleFromSource({cflag_kver.c_str()}, + "perf-test.c", + test_llvm__bpf_base_prog); + return M; +} + +extern "C" { +int test__clang_to_IR(void) +{ + perf_clang_scope _scope; + + auto M = __test__clang_to_IR(); + if (!M) + return -1; + for (llvm::Function& F : *M) + if (F.getName() == "bpf_func__SyS_epoll_wait") + return 0; + return -1; +} + +int test__clang_to_obj(void) +{ + perf_clang_scope _scope; + + auto M = __test__clang_to_IR(); + if (!M) + return -1; + + auto Buffer = perf::getBPFObjectFromModule(&*M); + if (!Buffer) + return -1; + return 0; +} + +} diff --git a/tools/perf/util/c++/clang.cpp b/tools/perf/util/c++/clang.cpp new file mode 100644 index 000000000000..1e974152cac2 --- /dev/null +++ b/tools/perf/util/c++/clang.cpp @@ -0,0 +1,195 @@ +/* + * llvm C frontend for perf. Support dynamically compile C file + * + * Inspired by clang example code: + * http://llvm.org/svn/llvm-project/cfe/trunk/examples/clang-interpreter/main.cpp + * + * Copyright (C) 2016 Wang Nan <wangnan0@huawei.com> + * Copyright (C) 2016 Huawei Inc. + */ + +#include "clang/CodeGen/CodeGenAction.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" +#include <memory> + +#include "clang.h" +#include "clang-c.h" + +namespace perf { + +static std::unique_ptr<llvm::LLVMContext> LLVMCtx; + +using namespace clang; + +static CompilerInvocation * +createCompilerInvocation(llvm::opt::ArgStringList CFlags, StringRef& Path, + DiagnosticsEngine& Diags) +{ + llvm::opt::ArgStringList CCArgs { + "-cc1", + "-triple", "bpf-pc-linux", + "-fsyntax-only", + "-ferror-limit", "19", + "-fmessage-length", "127", + "-O2", + "-nostdsysteminc", + "-nobuiltininc", + "-vectorize-loops", + "-vectorize-slp", + "-Wno-unused-value", + "-Wno-pointer-sign", + "-x", "c"}; + + CCArgs.append(CFlags.begin(), CFlags.end()); + CompilerInvocation *CI = tooling::newInvocation(&Diags, CCArgs); + + FrontendOptions& Opts = CI->getFrontendOpts(); + Opts.Inputs.clear(); + Opts.Inputs.emplace_back(Path, IK_C); + return CI; +} + +static std::unique_ptr<llvm::Module> +getModuleFromSource(llvm::opt::ArgStringList CFlags, + StringRef Path, IntrusiveRefCntPtr<vfs::FileSystem> VFS) +{ + CompilerInstance Clang; + Clang.createDiagnostics(); + + Clang.setVirtualFileSystem(&*VFS); + + IntrusiveRefCntPtr<CompilerInvocation> CI = + createCompilerInvocation(std::move(CFlags), Path, + Clang.getDiagnostics()); + Clang.setInvocation(&*CI); + + std::unique_ptr<CodeGenAction> Act(new EmitLLVMOnlyAction(&*LLVMCtx)); + if (!Clang.ExecuteAction(*Act)) + return std::unique_ptr<llvm::Module>(nullptr); + + return Act->takeModule(); +} + +std::unique_ptr<llvm::Module> +getModuleFromSource(llvm::opt::ArgStringList CFlags, + StringRef Name, StringRef Content) +{ + using namespace vfs; + + llvm::IntrusiveRefCntPtr<OverlayFileSystem> OverlayFS( + new OverlayFileSystem(getRealFileSystem())); + llvm::IntrusiveRefCntPtr<InMemoryFileSystem> MemFS( + new InMemoryFileSystem(true)); + + /* + * pushOverlay helps setting working dir for MemFS. Must call + * before addFile. + */ + OverlayFS->pushOverlay(MemFS); + MemFS->addFile(Twine(Name), 0, llvm::MemoryBuffer::getMemBuffer(Content)); + + return getModuleFromSource(std::move(CFlags), Name, OverlayFS); +} + +std::unique_ptr<llvm::Module> +getModuleFromSource(llvm::opt::ArgStringList CFlags, StringRef Path) +{ + IntrusiveRefCntPtr<vfs::FileSystem> VFS(vfs::getRealFileSystem()); + return getModuleFromSource(std::move(CFlags), Path, VFS); +} + +std::unique_ptr<llvm::SmallVectorImpl<char>> +getBPFObjectFromModule(llvm::Module *Module) +{ + using namespace llvm; + + std::string TargetTriple("bpf-pc-linux"); + std::string Error; + const Target* Target = TargetRegistry::lookupTarget(TargetTriple, Error); + if (!Target) { + llvm::errs() << Error; + return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr); + } + + llvm::TargetOptions Opt; + TargetMachine *TargetMachine = + Target->createTargetMachine(TargetTriple, + "generic", "", + Opt, Reloc::Static); + + Module->setDataLayout(TargetMachine->createDataLayout()); + Module->setTargetTriple(TargetTriple); + + std::unique_ptr<SmallVectorImpl<char>> Buffer(new SmallVector<char, 0>()); + raw_svector_ostream ostream(*Buffer); + + legacy::PassManager PM; + if (TargetMachine->addPassesToEmitFile(PM, ostream, + TargetMachine::CGFT_ObjectFile)) { + llvm::errs() << "TargetMachine can't emit a file of this type\n"; + return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr);; + } + PM.run(*Module); + + return std::move(Buffer); +} + +} + +extern "C" { +void perf_clang__init(void) +{ + perf::LLVMCtx.reset(new llvm::LLVMContext()); + LLVMInitializeBPFTargetInfo(); + LLVMInitializeBPFTarget(); + LLVMInitializeBPFTargetMC(); + LLVMInitializeBPFAsmPrinter(); +} + +void perf_clang__cleanup(void) +{ + perf::LLVMCtx.reset(nullptr); + llvm::llvm_shutdown(); +} + +int perf_clang__compile_bpf(const char *filename, + void **p_obj_buf, + size_t *p_obj_buf_sz) +{ + using namespace perf; + + if (!p_obj_buf || !p_obj_buf_sz) + return -EINVAL; + + llvm::opt::ArgStringList CFlags; + auto M = getModuleFromSource(std::move(CFlags), filename); + if (!M) + return -EINVAL; + auto O = getBPFObjectFromModule(&*M); + if (!O) + return -EINVAL; + + size_t size = O->size_in_bytes(); + void *buffer; + + buffer = malloc(size); + if (!buffer) + return -ENOMEM; + memcpy(buffer, O->data(), size); + *p_obj_buf = buffer; + *p_obj_buf_sz = size; + return 0; +} +} diff --git a/tools/perf/util/c++/clang.h b/tools/perf/util/c++/clang.h new file mode 100644 index 000000000000..dd8b0427550d --- /dev/null +++ b/tools/perf/util/c++/clang.h @@ -0,0 +1,26 @@ +#ifndef PERF_UTIL_CLANG_H +#define PERF_UTIL_CLANG_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Option/Option.h" +#include <memory> + +namespace perf { + +using namespace llvm; + +std::unique_ptr<Module> +getModuleFromSource(opt::ArgStringList CFlags, + StringRef Name, StringRef Content); + +std::unique_ptr<Module> +getModuleFromSource(opt::ArgStringList CFlags, + StringRef Path); + +std::unique_ptr<llvm::SmallVectorImpl<char>> +getBPFObjectFromModule(llvm::Module *Module); + +} +#endif diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c index 27b6f303720a..b23ff44cf214 100644 --- a/tools/perf/util/llvm-utils.c +++ b/tools/perf/util/llvm-utils.c @@ -7,6 +7,7 @@ #include <limits.h> #include <stdio.h> #include <stdlib.h> +#include <linux/err.h> #include "debug.h" #include "llvm-utils.h" #include "config.h" @@ -282,9 +283,10 @@ static const char *kinc_fetch_script = "rm -rf $TMPDIR\n" "exit $RET\n"; -static inline void -get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts) +void llvm__get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts) { + static char *saved_kbuild_dir; + static char *saved_kbuild_include_opts; int err; if (!kbuild_dir || !kbuild_include_opts) @@ -293,10 +295,28 @@ get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts) *kbuild_dir = NULL; *kbuild_include_opts = NULL; + if (saved_kbuild_dir && saved_kbuild_include_opts && + !IS_ERR(saved_kbuild_dir) && !IS_ERR(saved_kbuild_include_opts)) { + *kbuild_dir = strdup(saved_kbuild_dir); + *kbuild_include_opts = strdup(saved_kbuild_include_opts); + + if (*kbuild_dir && *kbuild_include_opts) + return; + + zfree(kbuild_dir); + zfree(kbuild_include_opts); + /* + * Don't fall through: it may breaks saved_kbuild_dir and + * saved_kbuild_include_opts if detect them again when + * memory is low. + */ + return; + } + 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; + goto errout; } err = detect_kbuild_dir(kbuild_dir); @@ -306,7 +326,7 @@ get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts) "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; + goto errout; } pr_debug("Kernel build dir is set to %s\n", *kbuild_dir); @@ -325,14 +345,43 @@ get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts) free(*kbuild_dir); *kbuild_dir = NULL; - return; + goto errout; } pr_debug("include option is set to %s\n", *kbuild_include_opts); + + saved_kbuild_dir = strdup(*kbuild_dir); + saved_kbuild_include_opts = strdup(*kbuild_include_opts); + + if (!saved_kbuild_dir || !saved_kbuild_include_opts) { + zfree(&saved_kbuild_dir); + zfree(&saved_kbuild_include_opts); + } + return; +errout: + saved_kbuild_dir = ERR_PTR(-EINVAL); + saved_kbuild_include_opts = ERR_PTR(-EINVAL); } -static void -dump_obj(const char *path, void *obj_buf, size_t size) +int llvm__get_nr_cpus(void) +{ + static int nr_cpus_avail = 0; + char serr[STRERR_BUFSIZE]; + + if (nr_cpus_avail > 0) + return nr_cpus_avail; + + nr_cpus_avail = sysconf(_SC_NPROCESSORS_CONF); + if (nr_cpus_avail <= 0) { + pr_err( +"WARNING:\tunable to get available CPUs in this system: %s\n" +" \tUse 128 instead.\n", str_error_r(errno, serr, sizeof(serr))); + nr_cpus_avail = 128; + } + return nr_cpus_avail; +} + +void llvm__dump_obj(const char *path, void *obj_buf, size_t size) { char *obj_path = strdup(path); FILE *fp; @@ -406,15 +455,9 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, * 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); + llvm__get_kbuild_opts(&kbuild_dir, &kbuild_include_opts); - nr_cpus_avail = sysconf(_SC_NPROCESSORS_CONF); - if (nr_cpus_avail <= 0) { - pr_err( -"WARNING:\tunable to get available CPUs in this system: %s\n" -" \tUse 128 instead.\n", str_error_r(errno, serr, sizeof(serr))); - nr_cpus_avail = 128; - } + nr_cpus_avail = llvm__get_nr_cpus(); snprintf(nr_cpus_avail_str, sizeof(nr_cpus_avail_str), "%d", nr_cpus_avail); @@ -453,9 +496,6 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, free(kbuild_dir); free(kbuild_include_opts); - if (llvm_param.dump_obj) - dump_obj(path, obj_buf, obj_buf_sz); - if (!p_obj_buf) free(obj_buf); else diff --git a/tools/perf/util/llvm-utils.h b/tools/perf/util/llvm-utils.h index 9f501cef06a1..c87a2a92a88f 100644 --- a/tools/perf/util/llvm-utils.h +++ b/tools/perf/util/llvm-utils.h @@ -50,4 +50,10 @@ 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 */ int llvm__search_clang(void); + +/* Following functions are reused by builtin clang support */ +void llvm__get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts); +int llvm__get_nr_cpus(void); + +void llvm__dump_obj(const char *path, void *obj_buf, size_t size); #endif diff --git a/tools/perf/util/perf-hooks.c b/tools/perf/util/perf-hooks.c index 4ce88e37dd63..cb368306b12b 100644 --- a/tools/perf/util/perf-hooks.c +++ b/tools/perf/util/perf-hooks.c @@ -27,7 +27,7 @@ void perf_hooks__invoke(const struct perf_hook_desc *desc) *(current_perf_hook->p_hook_func) = NULL; } else { current_perf_hook = desc; - (**desc->p_hook_func)(); + (**desc->p_hook_func)(desc->hook_ctx); } current_perf_hook = NULL; } @@ -41,7 +41,9 @@ void perf_hooks__recover(void) #define PERF_HOOK(name) \ perf_hook_func_t __perf_hook_func_##name = NULL; \ struct perf_hook_desc __perf_hook_desc_##name = \ - {.hook_name = #name, .p_hook_func = &__perf_hook_func_##name}; + {.hook_name = #name, \ + .p_hook_func = &__perf_hook_func_##name, \ + .hook_ctx = NULL}; #include "perf-hooks-list.h" #undef PERF_HOOK @@ -54,7 +56,8 @@ static struct perf_hook_desc *perf_hooks[] = { #undef PERF_HOOK int perf_hooks__set_hook(const char *hook_name, - perf_hook_func_t hook_func) + perf_hook_func_t hook_func, + void *hook_ctx) { unsigned int i; @@ -65,6 +68,7 @@ int perf_hooks__set_hook(const char *hook_name, if (*(perf_hooks[i]->p_hook_func)) pr_warning("Overwrite existing hook: %s\n", hook_name); *(perf_hooks[i]->p_hook_func) = hook_func; + perf_hooks[i]->hook_ctx = hook_ctx; return 0; } return -ENOENT; diff --git a/tools/perf/util/perf-hooks.h b/tools/perf/util/perf-hooks.h index 1d482b26b4b9..838d5797bc1e 100644 --- a/tools/perf/util/perf-hooks.h +++ b/tools/perf/util/perf-hooks.h @@ -5,10 +5,11 @@ extern "C" { #endif -typedef void (*perf_hook_func_t)(void); +typedef void (*perf_hook_func_t)(void *ctx); struct perf_hook_desc { const char * const hook_name; perf_hook_func_t * const p_hook_func; + void *hook_ctx; }; extern void perf_hooks__invoke(const struct perf_hook_desc *); @@ -26,7 +27,8 @@ static inline void perf_hooks__invoke_##name(void) \ extern int perf_hooks__set_hook(const char *hook_name, - perf_hook_func_t hook_func); + perf_hook_func_t hook_func, + void *hook_ctx); extern perf_hook_func_t perf_hooks__get_hook(const char *hook_name); diff --git a/tools/perf/util/util-cxx.h b/tools/perf/util/util-cxx.h new file mode 100644 index 000000000000..0e0e019c9f34 --- /dev/null +++ b/tools/perf/util/util-cxx.h @@ -0,0 +1,26 @@ +/* + * Support C++ source use utilities defined in util.h + */ + +#ifndef PERF_UTIL_UTIL_CXX_H +#define PERF_UTIL_UTIL_CXX_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Now 'new' is the only C++ keyword found in util.h: + * in tools/include/linux/rbtree.h + * + * Other keywords, like class and delete, should be + * redefined if necessary. + */ +#define new _new +#include "util.h" +#undef new + +#ifdef __cplusplus +} +#endif +#endif |