@@ -46,3 +46,11 @@ int bpf_func__SyS_epoll_wait(void *ctx)
}
char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE;
+
+#ifdef TEST_PERF_HOOK
+SEC("perfhook:test")
+void hook_test(void)
+{
+ return;
+}
+#endif
@@ -16,6 +16,10 @@ static struct {
.func = test__clang_to_obj,
.desc = "builtin clang compile C source to ELF object",
},
+ {
+ .func = test__clang_jit,
+ .desc = "builtin clang compile mixed BPF and native code",
+ },
#endif
};
@@ -14,6 +14,7 @@ extern void perf_clang__cleanup(void);
extern int test__clang_to_IR(void);
extern int test__clang_to_obj(void);
+extern int test__clang_jit(void);
extern int perf_clang__compile_bpf(const char *filename,
void **p_obj_buf,
@@ -26,6 +27,7 @@ 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 test__clang_jit(void) { return -1;}
static inline int
perf_clang__compile_bpf(const char *filename __maybe_unused,
@@ -5,6 +5,7 @@
#include <util-cxx.h>
#include <tests/llvm.h>
+#include <perf-hooks.h>
#include <string>
class perf_clang_scope {
@@ -14,7 +15,7 @@ public:
};
static std::unique_ptr<perf::PerfModule>
-__test__clang_to_IR(void)
+__test__clang_to_IR(bool perfhook)
{
unsigned int kernel_version;
@@ -23,14 +24,22 @@ __test__clang_to_IR(void)
std::string cflag_kver("-DLINUX_VERSION_CODE=" +
std::to_string(kernel_version));
+ std::string cflag_perfhook(perfhook ? "-DTEST_PERF_HOOK=1" : "");
std::unique_ptr<perf::PerfModule> M =
- perf::getModuleFromSource({cflag_kver.c_str()},
+ perf::getModuleFromSource({cflag_kver.c_str(),
+ cflag_perfhook.c_str()},
"perf-test.c",
test_llvm__bpf_base_prog);
return M;
}
+static std::unique_ptr<perf::PerfModule>
+__test__clang_to_IR(void)
+{
+ return __test__clang_to_IR(false);
+}
+
extern "C" {
int test__clang_to_IR(void)
{
@@ -59,4 +68,23 @@ int test__clang_to_obj(void)
return 0;
}
+int test__clang_jit(void)
+{
+ perf_clang_scope _scope;
+
+ auto M = __test__clang_to_IR(true);
+ if (!M)
+ return -1;
+
+ if (M->doJIT())
+ return -1;
+
+ std::unique_ptr<perf::PerfModule::HookMap> hooks(M->copyJITResult());
+ for (auto i : *hooks)
+ perf_hooks__set_hook(i.first.c_str(), i.second, NULL);
+
+ perf_hooks__invoke_test();
+ return 0;
+}
+
}
@@ -14,9 +14,14 @@
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
+#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
+#include "llvm/ExecutionEngine/Orc/LambdaResolver.h"
+#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/Option/Option.h"
+#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/TargetRegistry.h"
@@ -24,11 +29,13 @@
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#include <memory>
+#include <vector>
#include "clang.h"
#include "clang-c.h"
#include "llvm-utils.h"
#include "util-cxx.h"
+#include "perf-hooks.h"
namespace perf {
@@ -190,6 +197,66 @@ PerfModule::toBPFObject(void)
return std::move(Buffer);
}
+/*
+ * Use a global memory manager so allocated code and data won't be released
+ * when object destroy.
+ */
+static llvm::SectionMemoryManager JITMemoryManager;
+
+int PerfModule::doJIT(void)
+{
+ using namespace orc;
+
+ prepareJIT();
+
+ std::unique_ptr<TargetMachine> TM(EngineBuilder().selectTarget());
+ if (!TM) {
+ llvm::errs() << "Can't get target machine\n";
+ return -1;
+ }
+ const DataLayout DL(TM->createDataLayout());
+ Module->setDataLayout(DL);
+ Module->setTargetTriple(TM->getTargetTriple().normalize());
+
+ ObjectLinkingLayer<> ObjectLayer;
+ IRCompileLayer<decltype(ObjectLayer)> CompileLayer(ObjectLayer, SimpleCompiler(*TM));
+
+ auto Resolver = createLambdaResolver(
+ [](const std::string &Name) {
+ return RuntimeDyld::SymbolInfo(nullptr);
+ },
+ [](const std::string &Name) {
+ return RuntimeDyld::SymbolInfo(nullptr);
+ });
+
+ std::vector<llvm::Module *> Ms;
+ Ms.push_back(getModule());
+ CompileLayer.addModuleSet(std::move(Ms),
+ &JITMemoryManager,
+ std::move(Resolver));
+
+
+ for (Function *F : JITFunctions) {
+ JITSymbol sym = CompileLayer.findSymbol(F->getName().str(), true);
+
+ /*
+ * Type of F->getSection() is moving from
+ * const char * to StringRef.
+ * Convert it to std::string so we don't need
+ * consider this API change.
+ */
+ std::string sec(F->getSection());
+ std::string hook(&sec.c_str()[sizeof("perfhook:") - 1]);
+ perf_hook_func_t func = (perf_hook_func_t)(intptr_t)sym.getAddress();
+
+ if (JITResult[hook])
+ llvm::errs() << "Warning: multiple functions on hook "
+ << hook << ", only one is used\n";
+ JITResult[hook] = func;
+ }
+ return 0;
+}
+
class ClangOptions {
llvm::SmallString<PATH_MAX> FileName;
llvm::SmallString<64> KVerDef;
@@ -295,6 +362,10 @@ void perf_clang__init(void)
LLVMInitializeBPFTarget();
LLVMInitializeBPFTargetMC();
LLVMInitializeBPFAsmPrinter();
+
+ llvm::InitializeNativeTarget();
+ llvm::InitializeNativeTargetAsmPrinter();
+ llvm::InitializeNativeTargetAsmParser();
}
void perf_clang__cleanup(void)
@@ -7,18 +7,26 @@
#include "llvm/Option/Option.h"
#include <memory>
#include <set>
+#include <map>
+
+#include "util/perf-hooks.h"
namespace perf {
using namespace llvm;
class PerfModule {
+public:
+ typedef std::map<std::string, perf_hook_func_t> HookMap;
private:
std::unique_ptr<llvm::Module> Module;
std::set<llvm::GlobalVariable *> Maps;
std::set<llvm::Function *> BPFFunctions;
std::set<llvm::Function *> JITFunctions;
+
+ HookMap JITResult;
+
void prepareBPF(void);
void prepareJIT(void);
public:
@@ -26,10 +34,15 @@ class PerfModule {
{
return Module.get();
}
+ inline HookMap *copyJITResult(void)
+ {
+ return new HookMap(JITResult);
+ }
PerfModule(std::unique_ptr<llvm::Module>&& M);
std::unique_ptr<llvm::SmallVectorImpl<char>> toBPFObject(void);
+ int doJIT(void);
};
std::unique_ptr<PerfModule>