From 00b86691c77c6576861b82a3cfe4d609800758fe Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Sat, 26 Nov 2016 07:03:34 +0000 Subject: perf clang: Add builtin clang support ant test case Add basic clang support in clang.cpp and test__clang() testcase. The first testcase checks if builtin clang is able to generate LLVM IR. tests/clang.c is a proxy. Real testcase resides in utils/c++/clang-test.cpp in c++ and exports C interface to perf test subsystem. Test result: $ perf test -v clang 51: builtin clang support : 51.1: Test builtin clang compile C source to IR : --- start --- test child forked, pid 13215 test child finished with 0 ---- end ---- Test builtin clang support subtest 0: Ok Committer note: Make sure you've enabled CLANG and LLVM builtin support by setting the LIBCLANGLLVM variable on the make command line, e.g.: make LIBCLANGLLVM=1 O=/tmp/build/perf -C tools/perf install-bin Otherwise you'll get this when trying to do the 'perf test' call above: # perf test clang 51: builtin clang support : Skip (not compiled in) # Signed-off-by: Wang Nan Tested-by: Arnaldo Carvalho de Melo Cc: Alexei Starovoitov Cc: He Kuang Cc: Jiri Olsa Cc: Joe Stringer Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/20161126070354.141764-11-wangnan0@huawei.com [ Removed "Test" from descriptions, redundant and already removed from all the other entries ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/c++/clang-test.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 tools/perf/util/c++/clang-test.cpp (limited to 'tools/perf/util/c++/clang-test.cpp') diff --git a/tools/perf/util/c++/clang-test.cpp b/tools/perf/util/c++/clang-test.cpp new file mode 100644 index 000000000000..3da6bfa4bc54 --- /dev/null +++ b/tools/perf/util/c++/clang-test.cpp @@ -0,0 +1,31 @@ +#include "clang.h" +#include "clang-c.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/LLVMContext.h" + +class perf_clang_scope { +public: + explicit perf_clang_scope() {perf_clang__init();} + ~perf_clang_scope() {perf_clang__cleanup();} +}; + +extern "C" { + +int test__clang_to_IR(void) +{ + perf_clang_scope _scope; + + std::unique_ptr M = + perf::getModuleFromSource("perf-test.c", + "int myfunc(void) {return 1;}"); + + if (!M) + return -1; + + for (llvm::Function& F : *M) + if (F.getName() == "myfunc") + return 0; + return -1; +} + +} -- cgit v1.2.3 From a9495fe9dc63bee1166772b6f10e199ef1747892 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Sat, 26 Nov 2016 07:03:36 +0000 Subject: perf clang: Allow passing CFLAGS to builtin clang Improve getModuleFromSource() API to accept a cflags list. This feature will be used to pass LINUX_VERSION_CODE and -I flags. Signed-off-by: Wang Nan Cc: Alexei Starovoitov Cc: He Kuang Cc: Jiri Olsa Cc: Joe Stringer Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/20161126070354.141764-13-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/c++/clang-test.cpp | 5 +++-- tools/perf/util/c++/clang.cpp | 21 +++++++++++++-------- tools/perf/util/c++/clang.h | 8 ++++++-- 3 files changed, 22 insertions(+), 12 deletions(-) (limited to 'tools/perf/util/c++/clang-test.cpp') diff --git a/tools/perf/util/c++/clang-test.cpp b/tools/perf/util/c++/clang-test.cpp index 3da6bfa4bc54..0f484fbb2b58 100644 --- a/tools/perf/util/c++/clang-test.cpp +++ b/tools/perf/util/c++/clang-test.cpp @@ -16,8 +16,9 @@ int test__clang_to_IR(void) perf_clang_scope _scope; std::unique_ptr M = - perf::getModuleFromSource("perf-test.c", - "int myfunc(void) {return 1;}"); + perf::getModuleFromSource({"-DRESULT=1"}, + "perf-test.c", + "int myfunc(void) {return RESULT;}"); if (!M) return -1; diff --git a/tools/perf/util/c++/clang.cpp b/tools/perf/util/c++/clang.cpp index cf96199b4b6f..715ca0a3dee0 100644 --- a/tools/perf/util/c++/clang.cpp +++ b/tools/perf/util/c++/clang.cpp @@ -29,7 +29,8 @@ static std::unique_ptr LLVMCtx; using namespace clang; static CompilerInvocation * -createCompilerInvocation(StringRef& Path, DiagnosticsEngine& Diags) +createCompilerInvocation(llvm::opt::ArgStringList CFlags, StringRef& Path, + DiagnosticsEngine& Diags) { llvm::opt::ArgStringList CCArgs { "-cc1", @@ -45,6 +46,8 @@ createCompilerInvocation(StringRef& Path, DiagnosticsEngine& Diags) "-Wno-unused-value", "-Wno-pointer-sign", "-x", "c"}; + + CCArgs.append(CFlags.begin(), CFlags.end()); CompilerInvocation *CI = tooling::newInvocation(&Diags, CCArgs); FrontendOptions& Opts = CI->getFrontendOpts(); @@ -54,8 +57,8 @@ createCompilerInvocation(StringRef& Path, DiagnosticsEngine& Diags) } static std::unique_ptr -getModuleFromSource(StringRef Path, - IntrusiveRefCntPtr VFS) +getModuleFromSource(llvm::opt::ArgStringList CFlags, + StringRef Path, IntrusiveRefCntPtr VFS) { CompilerInstance Clang; Clang.createDiagnostics(); @@ -63,7 +66,8 @@ getModuleFromSource(StringRef Path, Clang.setVirtualFileSystem(&*VFS); IntrusiveRefCntPtr CI = - createCompilerInvocation(Path, Clang.getDiagnostics()); + createCompilerInvocation(std::move(CFlags), Path, + Clang.getDiagnostics()); Clang.setInvocation(&*CI); std::unique_ptr Act(new EmitLLVMOnlyAction(&*LLVMCtx)); @@ -74,7 +78,8 @@ getModuleFromSource(StringRef Path, } std::unique_ptr -getModuleFromSource(StringRef Name, StringRef Content) +getModuleFromSource(llvm::opt::ArgStringList CFlags, + StringRef Name, StringRef Content) { using namespace vfs; @@ -90,14 +95,14 @@ getModuleFromSource(StringRef Name, StringRef Content) OverlayFS->pushOverlay(MemFS); MemFS->addFile(Twine(Name), 0, llvm::MemoryBuffer::getMemBuffer(Content)); - return getModuleFromSource(Name, OverlayFS); + return getModuleFromSource(std::move(CFlags), Name, OverlayFS); } std::unique_ptr -getModuleFromSource(StringRef Path) +getModuleFromSource(llvm::opt::ArgStringList CFlags, StringRef Path) { IntrusiveRefCntPtr VFS(vfs::getRealFileSystem()); - return getModuleFromSource(Path, VFS); + return getModuleFromSource(std::move(CFlags), Path, VFS); } } diff --git a/tools/perf/util/c++/clang.h b/tools/perf/util/c++/clang.h index 90aff0162f1c..b4fc2a96b79d 100644 --- a/tools/perf/util/c++/clang.h +++ b/tools/perf/util/c++/clang.h @@ -4,16 +4,20 @@ #include "llvm/ADT/StringRef.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" +#include "llvm/Option/Option.h" #include + namespace perf { using namespace llvm; std::unique_ptr -getModuleFromSource(StringRef Name, StringRef Content); +getModuleFromSource(opt::ArgStringList CFlags, + StringRef Name, StringRef Content); std::unique_ptr -getModuleFromSource(StringRef Path); +getModuleFromSource(opt::ArgStringList CFlags, + StringRef Path); } #endif -- cgit v1.2.3 From e67d52d411c3562263735479db2efd2ebd178db9 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Sat, 26 Nov 2016 07:03:37 +0000 Subject: perf clang: Update test case to use real BPF script Allow C++ code to use util.h and tests/llvm.h. Let 'perf test' compile a real BPF script. Signed-off-by: Wang Nan Cc: Alexei Starovoitov Cc: He Kuang Cc: Jiri Olsa Cc: Joe Stringer Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/20161126070354.141764-14-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.config | 27 +++++++++++++++------------ tools/perf/tests/llvm.h | 7 +++++++ tools/perf/util/c++/clang-test.cpp | 17 ++++++++++++++--- tools/perf/util/util-cxx.h | 26 ++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 15 deletions(-) create mode 100644 tools/perf/util/util-cxx.h (limited to 'tools/perf/util/c++/clang-test.cpp') diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index b7c9c8051a33..09c2a9874f2f 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -212,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 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 /* for size_t */ #include /* 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/util/c++/clang-test.cpp b/tools/perf/util/c++/clang-test.cpp index 0f484fbb2b58..d84e760d2aab 100644 --- a/tools/perf/util/c++/clang-test.cpp +++ b/tools/perf/util/c++/clang-test.cpp @@ -3,6 +3,10 @@ #include "llvm/IR/Function.h" #include "llvm/IR/LLVMContext.h" +#include +#include +#include + class perf_clang_scope { public: explicit perf_clang_scope() {perf_clang__init();} @@ -14,17 +18,24 @@ extern "C" { int test__clang_to_IR(void) { perf_clang_scope _scope; + unsigned int kernel_version; + + if (fetch_kernel_version(&kernel_version, NULL, 0)) + return -1; + + std::string cflag_kver("-DLINUX_VERSION_CODE=" + + std::to_string(kernel_version)); std::unique_ptr M = - perf::getModuleFromSource({"-DRESULT=1"}, + perf::getModuleFromSource({cflag_kver.c_str()}, "perf-test.c", - "int myfunc(void) {return RESULT;}"); + test_llvm__bpf_base_prog); if (!M) return -1; for (llvm::Function& F : *M) - if (F.getName() == "myfunc") + if (F.getName() == "bpf_func__SyS_epoll_wait") return 0; return -1; } 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 -- cgit v1.2.3 From 5e08a76525b8f5e9aeb8b27d0466614abec070a9 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Sat, 26 Nov 2016 07:03:38 +0000 Subject: perf clang: Support compile IR to BPF object and add testcase getBPFObjectFromModule() is introduced to compile LLVM IR(Module) to BPF object. Add new testcase for it. Test result: $ ./buildperf/perf test -v clang 51: builtin clang support : 51.1: builtin clang compile C source to IR : --- start --- test child forked, pid 21822 test child finished with 0 ---- end ---- builtin clang support subtest 0: Ok 51.2: builtin clang compile C source to ELF object : --- start --- test child forked, pid 21823 test child finished with 0 ---- end ---- builtin clang support subtest 1: Ok Signed-off-by: Wang Nan Cc: Alexei Starovoitov Cc: He Kuang Cc: Jiri Olsa Cc: Joe Stringer Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/20161126070354.141764-15-wangnan0@huawei.com [ Remove redundant "Test" from entry descriptions ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/clang.c | 6 ++++- tools/perf/util/c++/clang-c.h | 1 + tools/perf/util/c++/clang-test.cpp | 31 +++++++++++++++++++++----- tools/perf/util/c++/clang.cpp | 45 ++++++++++++++++++++++++++++++++++++++ tools/perf/util/c++/clang.h | 3 +++ 5 files changed, 79 insertions(+), 7 deletions(-) (limited to 'tools/perf/util/c++/clang-test.cpp') diff --git a/tools/perf/tests/clang.c b/tools/perf/tests/clang.c index 636d6d0e9037..f853e242a86c 100644 --- a/tools/perf/tests/clang.c +++ b/tools/perf/tests/clang.c @@ -12,6 +12,10 @@ static struct { .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 }; @@ -33,7 +37,7 @@ int test__clang(int i __maybe_unused) return TEST_SKIP; } #else -int test__clang(int i __maybe_unused) +int test__clang(int i) { if (i < 0 || i >= (int)ARRAY_SIZE(clang_testcase_table)) return TEST_FAIL; diff --git a/tools/perf/util/c++/clang-c.h b/tools/perf/util/c++/clang-c.h index dcde4b564f3b..22b3936d1f09 100644 --- a/tools/perf/util/c++/clang-c.h +++ b/tools/perf/util/c++/clang-c.h @@ -9,6 +9,7 @@ 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); #ifdef __cplusplus } diff --git a/tools/perf/util/c++/clang-test.cpp b/tools/perf/util/c++/clang-test.cpp index d84e760d2aab..9b11e8c82798 100644 --- a/tools/perf/util/c++/clang-test.cpp +++ b/tools/perf/util/c++/clang-test.cpp @@ -13,15 +13,13 @@ public: ~perf_clang_scope() {perf_clang__cleanup();} }; -extern "C" { - -int test__clang_to_IR(void) +static std::unique_ptr +__test__clang_to_IR(void) { - perf_clang_scope _scope; unsigned int kernel_version; if (fetch_kernel_version(&kernel_version, NULL, 0)) - return -1; + return std::unique_ptr(nullptr); std::string cflag_kver("-DLINUX_VERSION_CODE=" + std::to_string(kernel_version)); @@ -30,14 +28,35 @@ int test__clang_to_IR(void) 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 index 715ca0a3dee0..2a1a75df204f 100644 --- a/tools/perf/util/c++/clang.cpp +++ b/tools/perf/util/c++/clang.cpp @@ -13,10 +13,15 @@ #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 #include "clang.h" @@ -105,12 +110,52 @@ getModuleFromSource(llvm::opt::ArgStringList CFlags, StringRef Path) return getModuleFromSource(std::move(CFlags), Path, VFS); } +std::unique_ptr> +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>(nullptr); + } + + llvm::TargetOptions Opt; + TargetMachine *TargetMachine = + Target->createTargetMachine(TargetTriple, + "generic", "", + Opt, Reloc::Static); + + Module->setDataLayout(TargetMachine->createDataLayout()); + Module->setTargetTriple(TargetTriple); + + std::unique_ptr> Buffer(new SmallVector()); + 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>(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) diff --git a/tools/perf/util/c++/clang.h b/tools/perf/util/c++/clang.h index b4fc2a96b79d..dd8b0427550d 100644 --- a/tools/perf/util/c++/clang.h +++ b/tools/perf/util/c++/clang.h @@ -19,5 +19,8 @@ std::unique_ptr getModuleFromSource(opt::ArgStringList CFlags, StringRef Path); +std::unique_ptr> +getBPFObjectFromModule(llvm::Module *Module); + } #endif -- cgit v1.2.3