summaryrefslogtreecommitdiff
path: root/llvm/lib/ExecutionEngine/OProfileJIT
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/ExecutionEngine/OProfileJIT')
-rw-r--r--llvm/lib/ExecutionEngine/OProfileJIT/OProfileJITEventListener.cpp188
-rw-r--r--llvm/lib/ExecutionEngine/OProfileJIT/OProfileWrapper.cpp267
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