File indexing completed on 2024-04-28 05:41:20

0001 /*
0002     SPDX-FileCopyrightText: 2015-2017 Milian Wolff <mail@milianw.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-or-later
0005 */
0006 
0007 #ifndef POINTERMAP_H
0008 #define POINTERMAP_H
0009 
0010 #include <algorithm>
0011 #include <iostream>
0012 #include <limits>
0013 #include <vector>
0014 
0015 #include <tsl/robin_map.h>
0016 #include <tsl/robin_set.h>
0017 
0018 #include <boost/functional/hash.hpp>
0019 
0020 #include "indices.h"
0021 
0022 /**
0023  * Information for a single call to an allocation function for big allocations.
0024  */
0025 struct IndexedAllocationInfo
0026 {
0027     uint64_t size;
0028     TraceIndex traceIndex;
0029     AllocationInfoIndex allocationIndex;
0030     bool operator==(const IndexedAllocationInfo& rhs) const
0031     {
0032         return rhs.traceIndex == traceIndex && rhs.size == size;
0033         // allocationInfoIndex not compared to allow to look it up
0034     }
0035 };
0036 
0037 namespace std {
0038 template <>
0039 struct hash<IndexedAllocationInfo>
0040 {
0041     size_t operator()(const IndexedAllocationInfo& info) const
0042     {
0043         std::size_t seed = 0;
0044         boost::hash_combine(seed, info.size);
0045         boost::hash_combine(seed, info.traceIndex.index);
0046         // allocationInfoIndex not hashed to allow to look it up
0047         return seed;
0048     }
0049 };
0050 }
0051 
0052 struct AllocationInfoSet
0053 {
0054     AllocationInfoSet()
0055     {
0056         set.reserve(625000);
0057     }
0058 
0059     bool add(uint64_t size, TraceIndex traceIndex, AllocationInfoIndex* allocationIndex)
0060     {
0061         allocationIndex->index = set.size();
0062         IndexedAllocationInfo info = {size, traceIndex, *allocationIndex};
0063         auto it = set.find(info);
0064         if (it != set.end()) {
0065             *allocationIndex = it->allocationIndex;
0066             return false;
0067         } else {
0068             set.insert(it, info);
0069             return true;
0070         }
0071     }
0072     tsl::robin_set<IndexedAllocationInfo> set;
0073 };
0074 
0075 /**
0076  * A low-memory-overhead map of 64bit pointer addresses to 32bit allocation
0077  * indices.
0078  *
0079  * We leverage the fact that pointers are allocated in pages, i.e. close to each
0080  * other. We split the 64bit address into a common large part and an individual
0081  * 16bit small part by dividing the address by some number (PageSize below) and
0082  * keeping the result as the big part and the residue as the small part.
0083  *
0084  * The big part of the address is used for a hash map to lookup the Indices
0085  * structure where we aggregate common pointers in two memory-efficient vectors,
0086  * one for the 16bit small pointer pairs, and one for the 32bit allocation
0087  * indices.
0088  */
0089 class PointerMap
0090 {
0091     struct SplitPointer
0092     {
0093         enum
0094         {
0095             PageSize = std::numeric_limits<uint16_t>::max() / 4
0096         };
0097         SplitPointer(uint64_t ptr)
0098             : big(ptr / PageSize)
0099             , small(ptr % PageSize)
0100         {
0101         }
0102         uint64_t big;
0103         uint16_t small;
0104     };
0105 
0106 public:
0107     PointerMap()
0108     {
0109         map.reserve(1024);
0110     }
0111 
0112     void addPointer(const uint64_t ptr, const AllocationInfoIndex allocationIndex)
0113     {
0114         const SplitPointer pointer(ptr);
0115 
0116         auto mapIt = map.find(pointer.big);
0117         if (mapIt == map.end()) {
0118             mapIt = map.insert(mapIt, std::make_pair(pointer.big, Indices()));
0119         }
0120         auto& indices = mapIt.value();
0121         auto pageIt = std::lower_bound(indices.smallPtrParts.begin(), indices.smallPtrParts.end(), pointer.small);
0122         auto allocationIt = indices.allocationIndices.begin() + distance(indices.smallPtrParts.begin(), pageIt);
0123         if (pageIt == indices.smallPtrParts.end() || *pageIt != pointer.small) {
0124             indices.smallPtrParts.insert(pageIt, pointer.small);
0125             indices.allocationIndices.insert(allocationIt, allocationIndex);
0126         } else {
0127             *allocationIt = allocationIndex;
0128         }
0129     }
0130 
0131     std::pair<AllocationInfoIndex, bool> takePointer(const uint64_t ptr)
0132     {
0133         const SplitPointer pointer(ptr);
0134 
0135         auto mapIt = map.find(pointer.big);
0136         if (mapIt == map.end()) {
0137             return {{}, false};
0138         }
0139         auto& indices = mapIt.value();
0140         auto pageIt = std::lower_bound(indices.smallPtrParts.begin(), indices.smallPtrParts.end(), pointer.small);
0141         if (pageIt == indices.smallPtrParts.end() || *pageIt != pointer.small) {
0142             return {{}, false};
0143         }
0144         auto allocationIt = indices.allocationIndices.begin() + distance(indices.smallPtrParts.begin(), pageIt);
0145         auto index = *allocationIt;
0146         indices.allocationIndices.erase(allocationIt);
0147         indices.smallPtrParts.erase(pageIt);
0148         if (indices.allocationIndices.empty()) {
0149             map.erase(mapIt);
0150         }
0151         return {index, true};
0152     }
0153 
0154 private:
0155     struct Indices
0156     {
0157         std::vector<uint16_t> smallPtrParts;
0158         std::vector<AllocationInfoIndex> allocationIndices;
0159     };
0160     tsl::robin_map<uint64_t, Indices> map;
0161 };
0162 
0163 #endif // POINTERMAP_H