/* Memory managment. * * Author: Daniel Krebs * SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include using namespace villas; using namespace villas::utils; namespace villas { MemoryManager *MemoryManager::instance = nullptr; MemoryManager &MemoryManager::get() { if (instance == nullptr) { instance = new MemoryManager; if (!instance) throw MemoryAllocationError(); } return *instance; } MemoryManager::AddressSpaceId MemoryManager::getOrCreateAddressSpace(std::string name) { try { // Try fast lookup return addrSpaceLookup.at(name); } catch (const std::out_of_range &) { // Does not yet exist, create std::shared_ptr addrSpace(new AddressSpace); addrSpace->name = name; // Cache it for the next access addrSpaceLookup[name] = memoryGraph.addVertex(addrSpace); return addrSpaceLookup[name]; } } MemoryManager::MappingId MemoryManager::createMapping(uintptr_t src, uintptr_t dest, size_t size, const std::string &name, MemoryManager::AddressSpaceId fromAddrSpace, MemoryManager::AddressSpaceId toAddrSpace) { std::shared_ptr mapping(new Mapping); mapping->name = name; mapping->src = src; mapping->dest = dest; mapping->size = size; return addMapping(mapping, fromAddrSpace, toAddrSpace); } MemoryManager::MappingId MemoryManager::addMapping(std::shared_ptr mapping, MemoryManager::AddressSpaceId fromAddrSpace, MemoryManager::AddressSpaceId toAddrSpace) { return memoryGraph.addEdge(mapping, fromAddrSpace, toAddrSpace); } MemoryManager::AddressSpaceId MemoryManager::findAddressSpace(const std::string &name) { return memoryGraph.findVertex( [&](const std::shared_ptr &v) { return v->name == name; }); } std::list MemoryManager::findPath(const MemoryManager::AddressSpaceId &fromAddrSpaceId, const MemoryManager::AddressSpaceId &toAddrSpaceId) { std::list path; auto fromAddrSpace = memoryGraph.getVertex(fromAddrSpaceId); auto toAddrSpace = memoryGraph.getVertex(toAddrSpaceId); // Find a path through the memory graph MemoryGraph::Path pathGraph; if (not memoryGraph.getPath(fromAddrSpaceId, toAddrSpaceId, pathGraph, pathCheckFunc)) { logger->debug("No translation found from ({}) to ({})", fromAddrSpace->toString(), toAddrSpace->toString()); throw std::out_of_range("no translation found"); } for (auto &mappingId : pathGraph) { auto mapping = memoryGraph.getEdge(mappingId); path.push_back(mapping->getVertexTo()); } return path; } MemoryTranslation MemoryManager::getTranslation( const MemoryManager::AddressSpaceId &fromAddrSpaceId, const MemoryManager::AddressSpaceId &toAddrSpaceId) { // Find a path through the memory graph MemoryGraph::Path path; if (not memoryGraph.getPath(fromAddrSpaceId, toAddrSpaceId, path, pathCheckFunc)) { auto fromAddrSpace = memoryGraph.getVertex(fromAddrSpaceId); auto toAddrSpace = memoryGraph.getVertex(toAddrSpaceId); logger->debug("No translation found from ({}) to ({})", fromAddrSpace->toString(), toAddrSpace->toString()); throw std::out_of_range("no translation found"); } // Start with an identity mapping MemoryTranslation translation(0, 0, SIZE_MAX); // Iterate through path and merge all mappings into a single translation for (auto &mappingId : path) { auto mapping = memoryGraph.getEdge(mappingId); translation += getTranslationFromMapping(*mapping); } return translation; } bool MemoryManager::pathCheck(const MemoryGraph::Path &path) { // Start with an identity mapping MemoryTranslation translation(0, 0, SIZE_MAX); // Try to add all mappings together to a common translation. If this fails // there is a non-overlapping window. for (auto &mappingId : path) { auto mapping = memoryGraph.getEdge(mappingId); try { translation += getTranslationFromMapping(*mapping); } catch (const InvalidTranslation &) { return false; } } return true; } uintptr_t MemoryTranslation::getLocalAddr(uintptr_t addrInForeignAddrSpace) const { assert(addrInForeignAddrSpace >= dst); assert(addrInForeignAddrSpace < (dst + size)); return src + addrInForeignAddrSpace - dst; } uintptr_t MemoryTranslation::getForeignAddr(uintptr_t addrInLocalAddrSpace) const { assert(addrInLocalAddrSpace >= src); assert(addrInLocalAddrSpace < (src + size)); return dst + addrInLocalAddrSpace - src; } MemoryTranslation & MemoryTranslation::operator+=(const MemoryTranslation &other) { Logger logger = Log::get("MemoryTranslation"); // Set level to debug to enable debug output logger->set_level(spdlog::level::info); const uintptr_t this_dst_high = this->dst + this->size; const uintptr_t other_src_high = other.src + other.size; logger->debug("this->src: {:#x}", this->src); logger->debug("this->dst: {:#x}", this->dst); logger->debug("this->size: {:#x}", this->size); logger->debug("other.src: {:#x}", other.src); logger->debug("other.dst: {:#x}", other.dst); logger->debug("other.size: {:#x}", other.size); logger->debug("this_dst_high: {:#x}", this_dst_high); logger->debug("other_src_high: {:#x}", other_src_high); // Make sure there is a common memory area assertExcept(other.src < this_dst_high, MemoryManager::InvalidTranslation()); assertExcept(this->dst < other_src_high, MemoryManager::InvalidTranslation()); const uintptr_t hi = std::max(this_dst_high, other_src_high); const uintptr_t lo = std::min(this->dst, other.src); const uintptr_t diff_hi = (this_dst_high > other_src_high) ? (this_dst_high - other_src_high) : (other_src_high - this_dst_high); const bool otherSrcIsSmaller = this->dst > other.src; const uintptr_t diff_lo = (otherSrcIsSmaller) ? (this->dst - other.src) : (other.src - this->dst); logger->debug("hi: {:#x}", hi); logger->debug("lo: {:#x}", lo); logger->debug("diff_hi: {:#x}", diff_hi); logger->debug("diff_lo: {:#x}", diff_lo); // New size of aperture, can only stay or shrink this->size = (hi - lo) - diff_hi - diff_lo; // New translation will come out other's destination (by default) this->dst = other.dst; // The source stays the same and can only increase with merged translations //this->src = this->src; if (otherSrcIsSmaller) // Other mapping starts at lower addresses, so we actually arrive at // higher addresses this->dst += diff_lo; else // Other mapping starts at higher addresses than this, so we have to // increase the start // NOTE: for addresses equality, this just adds 0 this->src += diff_lo; logger->debug("result src: {:#x}", this->src); logger->debug("result dst: {:#x}", this->dst); logger->debug("result size: {:#x}", this->size); return *this; } void MemoryManager::printGraph() { auto loggerStatic = logger; loggerStatic->info("####### Vertices ########"); for (unsigned int i = 0; i < memoryGraph.getVertexCount(); i++) { auto vertex = memoryGraph.getVertex(i); loggerStatic->info(std::to_string(i) + ": " + vertex->name); } loggerStatic->info("####### Edges ########"); for (unsigned int i = 0; i < memoryGraph.getEdgeCount(); i++) { auto mapping = memoryGraph.getEdge(i); loggerStatic->info("From: " + std::to_string(mapping->getVertexFrom()) + " To: " + std::to_string(mapping->getVertexTo()) + " ID: " + mapping->toString()); } } } // namespace villas