| //===-- 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()); |
| } |