//===- GlobalHandler.cpp - Target independent global & env. var handling --===// // // 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 // //===----------------------------------------------------------------------===// // // Target independent global handler and environment manager. // //===----------------------------------------------------------------------===// #include "GlobalHandler.h" #include "PluginInterface.h" #include "Utils/ELF.h" #include "Shared/Utils.h" #include "llvm/Support/Error.h" #include #include using namespace llvm; using namespace omp; using namespace target; using namespace plugin; Expected> GenericGlobalHandlerTy::getELFObjectFile(DeviceImageTy &Image) { assert(utils::elf::isELF(Image.getMemoryBuffer().getBuffer()) && "Input is not an ELF file"); return ELFObjectFileBase::createELFObjectFile(Image.getMemoryBuffer()); } Error GenericGlobalHandlerTy::moveGlobalBetweenDeviceAndHost( GenericDeviceTy &Device, DeviceImageTy &Image, const GlobalTy &HostGlobal, bool Device2Host) { GlobalTy DeviceGlobal(HostGlobal.getName(), HostGlobal.getSize()); // Get the metadata from the global on the device. if (auto Err = getGlobalMetadataFromDevice(Device, Image, DeviceGlobal)) return Err; // Perform the actual transfer. return moveGlobalBetweenDeviceAndHost(Device, HostGlobal, DeviceGlobal, Device2Host); } /// Actually move memory between host and device. See readGlobalFromDevice and /// writeGlobalToDevice for the interface description. Error GenericGlobalHandlerTy::moveGlobalBetweenDeviceAndHost( GenericDeviceTy &Device, const GlobalTy &HostGlobal, const GlobalTy &DeviceGlobal, bool Device2Host) { // Transfer the data from the source to the destination. if (Device2Host) { if (auto Err = Device.dataRetrieve(HostGlobal.getPtr(), DeviceGlobal.getPtr(), HostGlobal.getSize(), nullptr)) return Err; } else { if (auto Err = Device.dataSubmit(DeviceGlobal.getPtr(), HostGlobal.getPtr(), HostGlobal.getSize(), nullptr)) return Err; } DP("Succesfully %s %u bytes associated with global symbol '%s' %s the " "device " "(%p -> %p).\n", Device2Host ? "read" : "write", HostGlobal.getSize(), HostGlobal.getName().data(), Device2Host ? "from" : "to", DeviceGlobal.getPtr(), HostGlobal.getPtr()); return Plugin::success(); } bool GenericGlobalHandlerTy::isSymbolInImage(GenericDeviceTy &Device, DeviceImageTy &Image, StringRef SymName) { // Get the ELF object file for the image. Notice the ELF object may already // be created in previous calls, so we can reuse it. If this is unsuccessful // just return false as we couldn't find it. auto ELFObjOrErr = getELFObjectFile(Image); if (!ELFObjOrErr) { consumeError(ELFObjOrErr.takeError()); return false; } // Search the ELF symbol using the symbol name. auto SymOrErr = utils::elf::getSymbol(**ELFObjOrErr, SymName); if (!SymOrErr) { consumeError(SymOrErr.takeError()); return false; } return SymOrErr->has_value(); } Error GenericGlobalHandlerTy::getGlobalMetadataFromImage( GenericDeviceTy &Device, DeviceImageTy &Image, GlobalTy &ImageGlobal) { // Get the ELF object file for the image. Notice the ELF object may already // be created in previous calls, so we can reuse it. auto ELFObj = getELFObjectFile(Image); if (!ELFObj) return ELFObj.takeError(); // Search the ELF symbol using the symbol name. auto SymOrErr = utils::elf::getSymbol(**ELFObj, ImageGlobal.getName()); if (!SymOrErr) return Plugin::error("Failed ELF lookup of global '%s': %s", ImageGlobal.getName().data(), toString(SymOrErr.takeError()).data()); if (!SymOrErr->has_value()) return Plugin::error("Failed to find global symbol '%s' in the ELF image", ImageGlobal.getName().data()); auto AddrOrErr = utils::elf::getSymbolAddress(**SymOrErr); // Get the section to which the symbol belongs. if (!AddrOrErr) return Plugin::error("Failed to get ELF symbol from global '%s': %s", ImageGlobal.getName().data(), toString(AddrOrErr.takeError()).data()); // Setup the global symbol's address and size. ImageGlobal.setPtr(const_cast(*AddrOrErr)); ImageGlobal.setSize((*SymOrErr)->getSize()); return Plugin::success(); } Error GenericGlobalHandlerTy::readGlobalFromImage(GenericDeviceTy &Device, DeviceImageTy &Image, const GlobalTy &HostGlobal) { GlobalTy ImageGlobal(HostGlobal.getName(), -1); if (auto Err = getGlobalMetadataFromImage(Device, Image, ImageGlobal)) return Err; if (ImageGlobal.getSize() != HostGlobal.getSize()) return Plugin::error("Transfer failed because global symbol '%s' has " "%u bytes in the ELF image but %u bytes on the host", HostGlobal.getName().data(), ImageGlobal.getSize(), HostGlobal.getSize()); DP("Global symbol '%s' was found in the ELF image and %u bytes will copied " "from %p to %p.\n", HostGlobal.getName().data(), HostGlobal.getSize(), ImageGlobal.getPtr(), HostGlobal.getPtr()); assert(Image.getStart() <= ImageGlobal.getPtr() && utils::advancePtr(ImageGlobal.getPtr(), ImageGlobal.getSize()) < utils::advancePtr(Image.getStart(), Image.getSize()) && "Attempting to read outside the image!"); // Perform the copy from the image to the host memory. std::memcpy(HostGlobal.getPtr(), ImageGlobal.getPtr(), HostGlobal.getSize()); return Plugin::success(); } bool GenericGlobalHandlerTy::hasProfilingGlobals(GenericDeviceTy &Device, DeviceImageTy &Image) { GlobalTy global(getInstrProfNamesVarName().str(), 0); if (auto Err = getGlobalMetadataFromImage(Device, Image, global)) { consumeError(std::move(Err)); return false; } return true; } Expected GenericGlobalHandlerTy::readProfilingGlobals(GenericDeviceTy &Device, DeviceImageTy &Image) { GPUProfGlobals DeviceProfileData; auto ObjFile = getELFObjectFile(Image); if (!ObjFile) return ObjFile.takeError(); std::unique_ptr ELFObj( static_cast(ObjFile->release())); DeviceProfileData.TargetTriple = ELFObj->makeTriple(); // Iterate through elf symbols for (auto &Sym : ELFObj->symbols()) { auto NameOrErr = Sym.getName(); if (!NameOrErr) return NameOrErr.takeError(); // Check if given current global is a profiling global based // on name if (*NameOrErr == getInstrProfNamesVarName()) { // Read in profiled function names DeviceProfileData.NamesData = SmallVector(Sym.getSize(), 0); GlobalTy NamesGlobal(NameOrErr->str(), Sym.getSize(), DeviceProfileData.NamesData.data()); if (auto Err = readGlobalFromDevice(Device, Image, NamesGlobal)) return Err; } else if (NameOrErr->starts_with(getInstrProfCountersVarPrefix())) { // Read global variable profiling counts SmallVector Counts(Sym.getSize() / sizeof(int64_t), 0); GlobalTy CountGlobal(NameOrErr->str(), Sym.getSize(), Counts.data()); if (auto Err = readGlobalFromDevice(Device, Image, CountGlobal)) return Err; DeviceProfileData.Counts.push_back(std::move(Counts)); } else if (NameOrErr->starts_with(getInstrProfDataVarPrefix())) { // Read profiling data for this global variable __llvm_profile_data Data{}; GlobalTy DataGlobal(NameOrErr->str(), Sym.getSize(), &Data); if (auto Err = readGlobalFromDevice(Device, Image, DataGlobal)) return Err; DeviceProfileData.Data.push_back(std::move(Data)); } } return DeviceProfileData; } void GPUProfGlobals::dump() const { outs() << "======= GPU Profile =======\nTarget: " << TargetTriple.str() << "\n"; outs() << "======== Counters =========\n"; for (const auto &Count : Counts) { outs() << "["; for (size_t i = 0; i < Count.size(); i++) { if (i == 0) outs() << " "; outs() << Count[i] << " "; } outs() << "]\n"; } outs() << "========== Data ===========\n"; for (const auto &ProfData : Data) { outs() << "{ "; // The ProfData.Name maybe array, eg: NumValueSites[IPVK_Last+1] . // If we print out it directly, we are accessing out of bound data. // Skip dumping the array for now. #define INSTR_PROF_DATA(Type, LLVMType, Name, Initializer) \ if (sizeof(#Name) > 2 && #Name[sizeof(#Name) - 2] == ']') { \ outs() << "[...] "; \ } else { \ outs() << ProfData.Name << " "; \ } #include "llvm/ProfileData/InstrProfData.inc" outs() << "}\n"; } outs() << "======== Functions ========\n"; std::string s; s.reserve(NamesData.size()); for (uint8_t Name : NamesData) { s.push_back((char)Name); } InstrProfSymtab Symtab; if (Error Err = Symtab.create(StringRef(s))) { consumeError(std::move(Err)); } Symtab.dumpNames(outs()); outs() << "===========================\n"; }