diff options
Diffstat (limited to 'tools/perf/util/c++')
-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 | 67 | ||||
-rw-r--r-- | tools/perf/util/c++/clang.cpp | 225 | ||||
-rw-r--r-- | tools/perf/util/c++/clang.h | 27 |
5 files changed, 364 insertions, 0 deletions
diff --git a/tools/perf/util/c++/Build b/tools/perf/util/c++/Build new file mode 100644 index 000000000..613ecfd76 --- /dev/null +++ b/tools/perf/util/c++/Build @@ -0,0 +1,2 @@ +perf-$(CONFIG_CLANGLLVM) += clang.o +perf-$(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 000000000..d3731a876 --- /dev/null +++ b/tools/perf/util/c++/clang-c.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef PERF_UTIL_CLANG_C_H +#define PERF_UTIL_CLANG_C_H + +#include <stddef.h> /* for size_t */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_LIBCLANGLLVM_SUPPORT +extern void perf_clang__init(void); +extern void perf_clang__cleanup(void); + +struct test_suite; +extern int test__clang_to_IR(struct test_suite *test, int subtest); +extern int test__clang_to_obj(struct test_suite *test, int subtest); + +extern int perf_clang__compile_bpf(const char *filename, + void **p_obj_buf, + size_t *p_obj_buf_sz); +#else + +#include <errno.h> +#include <linux/compiler.h> /* for __maybe_unused */ + +static inline void perf_clang__init(void) { } +static inline void perf_clang__cleanup(void) { } + +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 000000000..a4683ca53 --- /dev/null +++ b/tools/perf/util/c++/clang-test.cpp @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "clang.h" +#include "clang-c.h" +extern "C" { +#include "../util.h" +} +#include "llvm/IR/Function.h" +#include "llvm/IR/LLVMContext.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(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + 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_pwait") + return 0; + return -1; +} + +int test__clang_to_obj(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + 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 000000000..1aad7d6d3 --- /dev/null +++ b/tools/perf/util/c++/clang.cpp @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 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/Basic/Version.h" +#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" +#if CLANG_VERSION_MAJOR >= 14 +#include "llvm/MC/TargetRegistry.h" +#else +#include "llvm/Support/TargetRegistry.h" +#endif +#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", + "-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 +#if CLANG_VERSION_MAJOR >= 11 + ,/*BinaryName=*/nullptr +#endif + ); + + FrontendOptions& Opts = CI->getFrontendOpts(); + Opts.Inputs.clear(); + Opts.Inputs.emplace_back(Path, + FrontendOptions::getInputKindForExtension("c")); + return CI; +} + +static std::unique_ptr<llvm::Module> +getModuleFromSource(llvm::opt::ArgStringList CFlags, + StringRef Path, IntrusiveRefCntPtr<vfs::FileSystem> VFS) +{ + CompilerInstance Clang; + Clang.createDiagnostics(); + +#if CLANG_VERSION_MAJOR < 9 + Clang.setVirtualFileSystem(&*VFS); +#else + Clang.createFileManager(&*VFS); +#endif + +#if CLANG_VERSION_MAJOR < 4 + IntrusiveRefCntPtr<CompilerInvocation> CI = + createCompilerInvocation(std::move(CFlags), Path, + Clang.getDiagnostics()); + Clang.setInvocation(&*CI); +#else + std::shared_ptr<CompilerInvocation> CI( + createCompilerInvocation(std::move(CFlags), Path, + Clang.getDiagnostics())); + Clang.setInvocation(CI); +#endif + + 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; + bool NotAdded; + NotAdded = TargetMachine->addPassesToEmitFile(PM, ostream +#if CLANG_VERSION_MAJOR >= 7 + , /*DwoOut=*/nullptr +#endif +#if CLANG_VERSION_MAJOR < 10 + , TargetMachine::CGFT_ObjectFile +#else + , llvm::CGFT_ObjectFile +#endif + ); + if (NotAdded) { + llvm::errs() << "TargetMachine can't emit a file of this type\n"; + return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr); + } + PM.run(*Module); + + return 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 000000000..6ce33e22f --- /dev/null +++ b/tools/perf/util/c++/clang.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#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 |