diff options
Diffstat (limited to 'llvm/lib/ExecutionEngine/OProfileJIT')
-rw-r--r-- | llvm/lib/ExecutionEngine/OProfileJIT/OProfileJITEventListener.cpp | 188 | ||||
-rw-r--r-- | llvm/lib/ExecutionEngine/OProfileJIT/OProfileWrapper.cpp | 267 |
2 files changed, 455 insertions, 0 deletions
diff --git a/llvm/lib/ExecutionEngine/OProfileJIT/OProfileJITEventListener.cpp b/llvm/lib/ExecutionEngine/OProfileJIT/OProfileJITEventListener.cpp new file mode 100644 index 000000000000..bb5d96051da9 --- /dev/null +++ b/llvm/lib/ExecutionEngine/OProfileJIT/OProfileJITEventListener.cpp @@ -0,0 +1,188 @@ +//===-- OProfileJITEventListener.cpp - Tell OProfile about JITted code ----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a JITEventListener object that uses OProfileWrapper to tell +// oprofile about JITted functions, including source line information. +// +//===----------------------------------------------------------------------===// + +#include "llvm-c/ExecutionEngine.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/Config/config.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/ExecutionEngine/JITEventListener.h" +#include "llvm/ExecutionEngine/OProfileWrapper.h" +#include "llvm/ExecutionEngine/RuntimeDyld.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/Function.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/SymbolSize.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Errno.h" +#include "llvm/Support/raw_ostream.h" +#include <dirent.h> +#include <fcntl.h> + +using namespace llvm; +using namespace llvm::object; + +#define DEBUG_TYPE "oprofile-jit-event-listener" + +namespace { + +class OProfileJITEventListener : public JITEventListener { + std::unique_ptr<OProfileWrapper> Wrapper; + + void initialize(); + std::map<ObjectKey, OwningBinary<ObjectFile>> DebugObjects; + +public: + OProfileJITEventListener(std::unique_ptr<OProfileWrapper> LibraryWrapper) + : Wrapper(std::move(LibraryWrapper)) { + initialize(); + } + + ~OProfileJITEventListener(); + + void notifyObjectLoaded(ObjectKey Key, const ObjectFile &Obj, + const RuntimeDyld::LoadedObjectInfo &L) override; + + void notifyFreeingObject(ObjectKey Key) override; +}; + +void OProfileJITEventListener::initialize() { + if (!Wrapper->op_open_agent()) { + const std::string err_str = sys::StrError(); + LLVM_DEBUG(dbgs() << "Failed to connect to OProfile agent: " << err_str + << "\n"); + } else { + LLVM_DEBUG(dbgs() << "Connected to OProfile agent.\n"); + } +} + +OProfileJITEventListener::~OProfileJITEventListener() { + if (Wrapper->isAgentAvailable()) { + if (Wrapper->op_close_agent() == -1) { + const std::string err_str = sys::StrError(); + LLVM_DEBUG(dbgs() << "Failed to disconnect from OProfile agent: " + << err_str << "\n"); + } else { + LLVM_DEBUG(dbgs() << "Disconnected from OProfile agent.\n"); + } + } +} + +void OProfileJITEventListener::notifyObjectLoaded( + ObjectKey Key, const ObjectFile &Obj, + const RuntimeDyld::LoadedObjectInfo &L) { + if (!Wrapper->isAgentAvailable()) { + return; + } + + OwningBinary<ObjectFile> DebugObjOwner = L.getObjectForDebug(Obj); + const ObjectFile &DebugObj = *DebugObjOwner.getBinary(); + std::unique_ptr<DIContext> Context = DWARFContext::create(DebugObj); + + // Use symbol info to iterate functions in the object. + for (const std::pair<SymbolRef, uint64_t> &P : computeSymbolSizes(DebugObj)) { + SymbolRef Sym = P.first; + if (!Sym.getType() || *Sym.getType() != SymbolRef::ST_Function) + continue; + + Expected<StringRef> NameOrErr = Sym.getName(); + if (!NameOrErr) + continue; + StringRef Name = *NameOrErr; + Expected<uint64_t> AddrOrErr = Sym.getAddress(); + if (!AddrOrErr) + continue; + uint64_t Addr = *AddrOrErr; + uint64_t Size = P.second; + + if (Wrapper->op_write_native_code(Name.data(), Addr, (void *)Addr, Size) == + -1) { + LLVM_DEBUG(dbgs() << "Failed to tell OProfile about native function " + << Name << " at [" << (void *)Addr << "-" + << ((char *)Addr + Size) << "]\n"); + continue; + } + + DILineInfoTable Lines = Context->getLineInfoForAddressRange(Addr, Size); + size_t i = 0; + size_t num_entries = Lines.size(); + struct debug_line_info *debug_line; + debug_line = (struct debug_line_info *)calloc( + num_entries, sizeof(struct debug_line_info)); + + for (auto& It : Lines) { + debug_line[i].vma = (unsigned long)It.first; + debug_line[i].lineno = It.second.Line; + debug_line[i].filename = + const_cast<char *>(Lines.front().second.FileName.c_str()); + ++i; + } + + if (Wrapper->op_write_debug_line_info((void *)Addr, num_entries, + debug_line) == -1) { + LLVM_DEBUG(dbgs() << "Failed to tell OProfiler about debug object at [" + << (void *)Addr << "-" << ((char *)Addr + Size) + << "]\n"); + continue; + } + } + + DebugObjects[Key] = std::move(DebugObjOwner); +} + +void OProfileJITEventListener::notifyFreeingObject(ObjectKey Key) { + if (Wrapper->isAgentAvailable()) { + + // If there was no agent registered when the original object was loaded then + // we won't have created a debug object for it, so bail out. + if (DebugObjects.find(Key) == DebugObjects.end()) + return; + + const ObjectFile &DebugObj = *DebugObjects[Key].getBinary(); + + // Use symbol info to iterate functions in the object. + for (symbol_iterator I = DebugObj.symbol_begin(), + E = DebugObj.symbol_end(); + I != E; ++I) { + if (I->getType() && *I->getType() == SymbolRef::ST_Function) { + Expected<uint64_t> AddrOrErr = I->getAddress(); + if (!AddrOrErr) + continue; + uint64_t Addr = *AddrOrErr; + + if (Wrapper->op_unload_native_code(Addr) == -1) { + LLVM_DEBUG( + dbgs() + << "Failed to tell OProfile about unload of native function at " + << (void *)Addr << "\n"); + continue; + } + } + } + } + + DebugObjects.erase(Key); +} + +} // anonymous namespace. + +namespace llvm { +JITEventListener *JITEventListener::createOProfileJITEventListener() { + return new OProfileJITEventListener(std::make_unique<OProfileWrapper>()); +} + +} // namespace llvm + +LLVMJITEventListenerRef LLVMCreateOProfileJITEventListener(void) +{ + return wrap(JITEventListener::createOProfileJITEventListener()); +} diff --git a/llvm/lib/ExecutionEngine/OProfileJIT/OProfileWrapper.cpp b/llvm/lib/ExecutionEngine/OProfileJIT/OProfileWrapper.cpp new file mode 100644 index 000000000000..b78d2531382d --- /dev/null +++ b/llvm/lib/ExecutionEngine/OProfileJIT/OProfileWrapper.cpp @@ -0,0 +1,267 @@ +//===-- OProfileWrapper.cpp - OProfile JIT API Wrapper implementation -----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the interface in OProfileWrapper.h. It is responsible +// for loading the opagent dynamic library when the first call to an op_ +// function occurs. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/OProfileWrapper.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/Mutex.h" +#include "llvm/Support/raw_ostream.h" +#include <cstring> +#include <dirent.h> +#include <fcntl.h> +#include <mutex> +#include <stddef.h> +#include <sys/stat.h> +#include <unistd.h> + +#define DEBUG_TYPE "oprofile-wrapper" + +namespace { + +// Global mutex to ensure a single thread initializes oprofile agent. +llvm::sys::Mutex OProfileInitializationMutex; + +} // anonymous namespace + +namespace llvm { + +OProfileWrapper::OProfileWrapper() +: Agent(0), + OpenAgentFunc(0), + CloseAgentFunc(0), + WriteNativeCodeFunc(0), + WriteDebugLineInfoFunc(0), + UnloadNativeCodeFunc(0), + MajorVersionFunc(0), + MinorVersionFunc(0), + IsOProfileRunningFunc(0), + Initialized(false) { +} + +bool OProfileWrapper::initialize() { + using namespace llvm; + using namespace llvm::sys; + + std::lock_guard<sys::Mutex> Guard(OProfileInitializationMutex); + + if (Initialized) + return OpenAgentFunc != 0; + + Initialized = true; + + // If the oprofile daemon is not running, don't load the opagent library + if (!isOProfileRunning()) { + LLVM_DEBUG(dbgs() << "OProfile daemon is not detected.\n"); + return false; + } + + std::string error; + if(!DynamicLibrary::LoadLibraryPermanently("libopagent.so", &error)) { + LLVM_DEBUG( + dbgs() + << "OProfile connector library libopagent.so could not be loaded: " + << error << "\n"); + } + + // Get the addresses of the opagent functions + OpenAgentFunc = (op_open_agent_ptr_t)(intptr_t) + DynamicLibrary::SearchForAddressOfSymbol("op_open_agent"); + CloseAgentFunc = (op_close_agent_ptr_t)(intptr_t) + DynamicLibrary::SearchForAddressOfSymbol("op_close_agent"); + WriteNativeCodeFunc = (op_write_native_code_ptr_t)(intptr_t) + DynamicLibrary::SearchForAddressOfSymbol("op_write_native_code"); + WriteDebugLineInfoFunc = (op_write_debug_line_info_ptr_t)(intptr_t) + DynamicLibrary::SearchForAddressOfSymbol("op_write_debug_line_info"); + UnloadNativeCodeFunc = (op_unload_native_code_ptr_t)(intptr_t) + DynamicLibrary::SearchForAddressOfSymbol("op_unload_native_code"); + MajorVersionFunc = (op_major_version_ptr_t)(intptr_t) + DynamicLibrary::SearchForAddressOfSymbol("op_major_version"); + MinorVersionFunc = (op_major_version_ptr_t)(intptr_t) + DynamicLibrary::SearchForAddressOfSymbol("op_minor_version"); + + // With missing functions, we can do nothing + if (!OpenAgentFunc + || !CloseAgentFunc + || !WriteNativeCodeFunc + || !WriteDebugLineInfoFunc + || !UnloadNativeCodeFunc) { + OpenAgentFunc = 0; + CloseAgentFunc = 0; + WriteNativeCodeFunc = 0; + WriteDebugLineInfoFunc = 0; + UnloadNativeCodeFunc = 0; + return false; + } + + return true; +} + +bool OProfileWrapper::isOProfileRunning() { + if (IsOProfileRunningFunc != 0) + return IsOProfileRunningFunc(); + return checkForOProfileProcEntry(); +} + +bool OProfileWrapper::checkForOProfileProcEntry() { + DIR* ProcDir; + + ProcDir = opendir("/proc"); + if (!ProcDir) + return false; + + // Walk the /proc tree looking for the oprofile daemon + struct dirent* Entry; + while (0 != (Entry = readdir(ProcDir))) { + if (Entry->d_type == DT_DIR) { + // Build a path from the current entry name + SmallString<256> CmdLineFName; + raw_svector_ostream(CmdLineFName) << "/proc/" << Entry->d_name + << "/cmdline"; + + // Open the cmdline file + int CmdLineFD = open(CmdLineFName.c_str(), S_IRUSR); + if (CmdLineFD != -1) { + char ExeName[PATH_MAX+1]; + char* BaseName = 0; + + // Read the cmdline file + ssize_t NumRead = read(CmdLineFD, ExeName, PATH_MAX+1); + close(CmdLineFD); + ssize_t Idx = 0; + + if (ExeName[0] != '/') { + BaseName = ExeName; + } + + // Find the terminator for the first string + while (Idx < NumRead-1 && ExeName[Idx] != 0) { + Idx++; + } + + // Go back to the last non-null character + Idx--; + + // Find the last path separator in the first string + while (Idx > 0) { + if (ExeName[Idx] == '/') { + BaseName = ExeName + Idx + 1; + break; + } + Idx--; + } + + // Test this to see if it is the oprofile daemon + if (BaseName != 0 && (!strcmp("oprofiled", BaseName) || + !strcmp("operf", BaseName))) { + // If it is, we're done + closedir(ProcDir); + return true; + } + } + } + } + + // We've looked through all the files and didn't find the daemon + closedir(ProcDir); + return false; +} + +bool OProfileWrapper::op_open_agent() { + if (!Initialized) + initialize(); + + if (OpenAgentFunc != 0) { + Agent = OpenAgentFunc(); + return Agent != 0; + } + + return false; +} + +int OProfileWrapper::op_close_agent() { + if (!Initialized) + initialize(); + + int ret = -1; + if (Agent && CloseAgentFunc) { + ret = CloseAgentFunc(Agent); + if (ret == 0) { + Agent = 0; + } + } + return ret; +} + +bool OProfileWrapper::isAgentAvailable() { + return Agent != 0; +} + +int OProfileWrapper::op_write_native_code(const char* Name, + uint64_t Addr, + void const* Code, + const unsigned int Size) { + if (!Initialized) + initialize(); + + if (Agent && WriteNativeCodeFunc) + return WriteNativeCodeFunc(Agent, Name, Addr, Code, Size); + + return -1; +} + +int OProfileWrapper::op_write_debug_line_info( + void const* Code, + size_t NumEntries, + struct debug_line_info const* Info) { + if (!Initialized) + initialize(); + + if (Agent && WriteDebugLineInfoFunc) + return WriteDebugLineInfoFunc(Agent, Code, NumEntries, Info); + + return -1; +} + +int OProfileWrapper::op_major_version() { + if (!Initialized) + initialize(); + + if (Agent && MajorVersionFunc) + return MajorVersionFunc(); + + return -1; +} + +int OProfileWrapper::op_minor_version() { + if (!Initialized) + initialize(); + + if (Agent && MinorVersionFunc) + return MinorVersionFunc(); + + return -1; +} + +int OProfileWrapper::op_unload_native_code(uint64_t Addr) { + if (!Initialized) + initialize(); + + if (Agent && UnloadNativeCodeFunc) + return UnloadNativeCodeFunc(Agent, Addr); + + return -1; +} + +} // namespace llvm |