diff --git a/interpreter/cling/lib/Interpreter/CIFactory.cpp b/interpreter/cling/lib/Interpreter/CIFactory.cpp index 7ad289eafa091..4ce0d75c92ed4 100644 --- a/interpreter/cling/lib/Interpreter/CIFactory.cpp +++ b/interpreter/cling/lib/Interpreter/CIFactory.cpp @@ -49,6 +49,7 @@ #include "llvm/Target/TargetOptions.h" #include +#include #include #include #include @@ -1322,6 +1323,12 @@ namespace { if(COpts.CUDAHost) argvCompile.push_back("--cuda-host-only"); +#ifdef __linux__ + // Keep frame pointer to make JIT stack unwinding reliable for profiling + if (std::getenv("CLING_PROFILE")) + argvCompile.push_back("-fno-omit-frame-pointer"); +#endif + // argv[0] already inserted, get the rest argvCompile.insert(argvCompile.end(), argv+1, argv + argc); @@ -1660,7 +1667,10 @@ namespace { // adjusted per transaction in IncrementalParser::codeGenTransaction(). CGOpts.setInlining(CodeGenOptions::NormalInlining); - // CGOpts.setDebugInfo(clang::CodeGenOptions::FullDebugInfo); + // Add debugging info when debugging or profiling + if (std::getenv("CLING_DEBUG") || std::getenv("CLING_PROFILE")) + CGOpts.setDebugInfo(clang::codegenoptions::FullDebugInfo); + // CGOpts.EmitDeclMetadata = 1; // For unloading, for later // aliasing the complete ctor to the base ctor causes the JIT to crash CGOpts.CXXCtorDtorAliases = 0; diff --git a/interpreter/cling/lib/Interpreter/CMakeLists.txt b/interpreter/cling/lib/Interpreter/CMakeLists.txt index 921c773c15fe2..1f15fa122ef3a 100644 --- a/interpreter/cling/lib/Interpreter/CMakeLists.txt +++ b/interpreter/cling/lib/Interpreter/CMakeLists.txt @@ -87,6 +87,7 @@ add_cling_library(clingInterpreter OBJECT InvocationOptions.cpp LookupHelper.cpp NullDerefProtectionTransformer.cpp + PerfJITEventListener.cpp RequiredSymbols.cpp Transaction.cpp TransactionUnloader.cpp diff --git a/interpreter/cling/lib/Interpreter/IncrementalJIT.cpp b/interpreter/cling/lib/Interpreter/IncrementalJIT.cpp index 611e21c36efb6..f0130e5e4fc07 100644 --- a/interpreter/cling/lib/Interpreter/IncrementalJIT.cpp +++ b/interpreter/cling/lib/Interpreter/IncrementalJIT.cpp @@ -55,6 +55,9 @@ class ClingMemoryManager: public SectionMemoryManager { namespace cling { +///\brief Creates JIT event listener to allow profiling of JITted code with perf +llvm::JITEventListener* createPerfJITEventListener(); + ///\brief Memory manager for the OrcJIT layers to resolve symbols from the /// common IncrementalJIT. I.e. the master of the Orcs. /// Each ObjectLayer instance has one Azog object. @@ -355,8 +358,15 @@ IncrementalJIT::IncrementalJIT(IncrementalExecutor& exe, // Enable JIT symbol resolution from the binary. llvm::sys::DynamicLibrary::LoadLibraryPermanently(0, 0); - // Make debug symbols available. - m_GDBListener = 0; // JITEventListener::createGDBRegistrationListener(); + // Enable GDB JIT listener for debugging if CLING_DEBUG is set + if (std::getenv("CLING_DEBUG")) + m_Listeners.push_back(JITEventListener::createGDBRegistrationListener()); + +#ifdef __linux__ + // Enable perf profiling of JITted code on Linux if CLING_PROFILE is set + if (std::getenv("CLING_PROFILE")) + m_Listeners.push_back(cling::createPerfJITEventListener()); +#endif // #if MCJIT // llvm::EngineBuilder builder(std::move(m)); diff --git a/interpreter/cling/lib/Interpreter/IncrementalJIT.h b/interpreter/cling/lib/Interpreter/IncrementalJIT.h index 158bd1afd013b..a60f8b9ae2a29 100644 --- a/interpreter/cling/lib/Interpreter/IncrementalJIT.h +++ b/interpreter/cling/lib/Interpreter/IncrementalJIT.h @@ -45,7 +45,8 @@ class IncrementalJIT { private: friend class Azog; - llvm::JITEventListener* m_GDBListener; // owned by llvm::ManagedStaticBase + // owned by llvm::ManagedStaticBase + std::vector m_Listeners; SymbolMapT m_SymbolMap; @@ -53,21 +54,14 @@ class IncrementalJIT { public: NotifyObjectLoadedT(IncrementalJIT &jit) : m_JIT(jit) {} void operator()(llvm::orc::VModuleKey K, - const llvm::object::ObjectFile &Object, - const llvm::LoadedObjectInfo &/*Info*/) const { + const llvm::object::ObjectFile& Object, + const llvm::RuntimeDyld::LoadedObjectInfo& Info) const { m_JIT.m_UnfinalizedSections[K] = std::move(m_JIT.m_SectionsAllocatedSinceLastLoad); m_JIT.m_SectionsAllocatedSinceLastLoad = SectionAddrSet(); - // FIXME: NotifyObjectEmitted requires a RuntimeDyld::LoadedObjectInfo - // object. In order to get it one should call - // RTDyld.loadObject(*ObjToLoad->getBinary()) according to r306058. - // Moreover this should be done in the finalizer. Currently we are - // disabling this since we have globally disabled this functionality in - // IncrementalJIT.cpp (m_GDBListener = 0). - // - // if (auto GDBListener = m_JIT.m_GDBListener) - // GDBListener->NotifyObjectEmitted(*Object->getBinary(), Info); + for (auto listener : m_JIT.m_Listeners) + listener->notifyObjectLoaded(K, Object, Info); for (const auto &Symbol: Object.symbols()) { auto Flags = Symbol.getFlags(); diff --git a/interpreter/cling/lib/Interpreter/PerfJITEventListener.cpp b/interpreter/cling/lib/Interpreter/PerfJITEventListener.cpp new file mode 100644 index 0000000000000..138b5bc76968c --- /dev/null +++ b/interpreter/cling/lib/Interpreter/PerfJITEventListener.cpp @@ -0,0 +1,129 @@ +//--------------------------------------------------------------------*- C++ -*- +// CLING - the C++ LLVM-based InterpreterG :) +// author: Guilherme Amadio +// +// This file is dual-licensed: you can choose to license it under the University +// of Illinois Open Source License or the GNU Lesser General Public License. See +// LICENSE.TXT for details. +//------------------------------------------------------------------------------ +// +// This file implements a JITEventListener object that tells perf about JITted +// symbols using perf map files (/tmp/perf-%d.map, where %d = pid of process). +// +// Documentation for this perf jit interface is available at: +// https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/tools/perf/Documentation/jit-interface.txt +// +//------------------------------------------------------------------------------ + +#ifdef __linux__ + +#include "llvm/Config/config.h" +#include "llvm/ExecutionEngine/JITEventListener.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/SymbolSize.h" +#include "llvm/Support/ManagedStatic.h" + +#include +#include +#include + +#include + +using namespace llvm; +using namespace llvm::object; + +namespace { + + class PerfJITEventListener : public JITEventListener { + public: + PerfJITEventListener(); + ~PerfJITEventListener() { + if (m_Perfmap) + fclose(m_Perfmap); + } + + void notifyObjectLoaded(ObjectKey K, const ObjectFile& Obj, + const RuntimeDyld::LoadedObjectInfo& L) override; + void notifyFreeingObject(ObjectKey K) override; + + private: + std::mutex m_Mutex; + FILE* m_Perfmap; + }; + + PerfJITEventListener::PerfJITEventListener() { + char filename[64]; + snprintf(filename, 64, "/tmp/perf-%d.map", getpid()); + m_Perfmap = fopen(filename, "a"); + } + + void PerfJITEventListener::notifyObjectLoaded( + ObjectKey K, const ObjectFile& Obj, + const RuntimeDyld::LoadedObjectInfo& L) { + + if (!m_Perfmap) + return; + + OwningBinary DebugObjOwner = L.getObjectForDebug(Obj); + const ObjectFile& DebugObj = *DebugObjOwner.getBinary(); + + // For each symbol, we want to check its address and size + // if it's a function and write the information to the perf + // map file, otherwise we just ignore the symbol and any + // related errors. This implementation is adapted from LLVM: + // llvm/src/lib/ExecutionEngine/PerfJITEvents/PerfJITEventListener.cpp + + for (const std::pair& P : + computeSymbolSizes(DebugObj)) { + SymbolRef Sym = P.first; + + Expected SymTypeOrErr = Sym.getType(); + if (!SymTypeOrErr) { + consumeError(SymTypeOrErr.takeError()); + continue; + } + + SymbolRef::Type SymType = *SymTypeOrErr; + if (SymType != SymbolRef::ST_Function) + continue; + + Expected Name = Sym.getName(); + if (!Name) { + consumeError(Name.takeError()); + continue; + } + + Expected AddrOrErr = Sym.getAddress(); + if (!AddrOrErr) { + consumeError(AddrOrErr.takeError()); + continue; + } + + uint64_t address = *AddrOrErr; + uint64_t size = P.second; + + if (size == 0) + continue; + + std::lock_guard lock(m_Mutex); + fprintf(m_Perfmap, "%" PRIx64 " %" PRIx64 " %s\n", address, size, Name->data()); + } + + fflush(m_Perfmap); + } + + void PerfJITEventListener::notifyFreeingObject(ObjectKey K) { + // nothing to be done + } + + llvm::ManagedStatic PerfListener; + +} // end anonymous namespace + +namespace cling { + + JITEventListener* createPerfJITEventListener() { return &*PerfListener; } + +} // namespace cling + +#endif