linux/tools/perf/util/c++/clang.cpp
Wang Nan edd695b032 perf clang: Compile BPF script using builtin clang support
After this patch, perf utilizes builtin clang support to build BPF
script, no longer depend on external clang, but fallbacking to it
if for some reason the builtin compiling framework fails.

Test:

  $ type clang
  -bash: type: clang: not found
  $ cat ~/.perfconfig
  $ echo '#define LINUX_VERSION_CODE 0x040700' > ./test.c
  $ cat ./tools/perf/tests/bpf-script-example.c >> ./test.c
  $ ./perf record -v --dry-run -e ./test.c 2>&1 | grep builtin
  bpf: successfull builtin compilation
  $

Can't pass cflags so unable to include kernel headers now. Will be fixed
by following commits.

Committer notes:

Make sure '-v' comes before the '-e ./test.c' in the command line otherwise the
'verbose' variable will not be set when the bpf event is parsed and thus the
pr_debug indicating a 'successfull builtin compilation' will not be output, as
the debug level (1) will be less than what 'verbose' has at that point (0).

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Joe Stringer <joe@ovn.org>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/20161126070354.141764-16-wangnan0@huawei.com
[ Spell check/reflow successfull pr_debug string ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-12-05 15:51:45 -03:00

196 lines
4.9 KiB
C++

/*
* 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;
}
}