Lumiera 0.pre.04
»edit your freedom«
Loading...
Searching...
No Matches
tracking-allocator.cpp
Go to the documentation of this file.
1/*
2 TrackingAllocator - custom allocator for memory management diagnostics
3
4 Copyright (C)
5 2024, Hermann Vosseler <Ichthyostega@web.de>
6
7  **Lumiera** is free software; you can redistribute it and/or modify it
8  under the terms of the GNU General Public License as published by the
9  Free Software Foundation; either version 2 of the License, or (at your
10  option) any later version. See the file COPYING for further details.
11
12* *****************************************************************/
13
14
35#include "lib/iter-explorer.hpp"
36#include "lib/depend.hpp"
37#include "lib/util.hpp"
38
39
40#include <string>
41#include <unordered_map>
42
43using std::string;
44using std::make_shared;
45using std::make_pair;
46using util::contains;
47using util::joinDash;
48using util::showAdr;
49
50
51namespace lib {
52namespace test{
53
54
60 {
61 // using the allocated memory location as key
62 using Location = void*;
64 {
65 HashVal operator() (Location const& loc) const { return HashVal(loc); }
66 };
67
75
76 using AllocTab = std::unordered_map<const Location, Allocation, LocationHash>;
77
78
82 size_t entryNr_;
83
84 public:
86 : poolID_{id}
87 , allocs_{}
88 , checksum_{0}
90 { }
91
92 Allocation& addAlloc (size_t bytes);
93 void discardAlloc (void* loc, size_t);
94
95 Allocation const* findAlloc (Location) const;
96
97 HashVal getChecksum() const;
98 size_t getAllocationCnt() const;
99 size_t calcAllocSize() const;
100
101 Literal getPoolID() const { return poolID_; }
102 };
103
104
105 namespace { // implementation details for common memory management
106
107 /* === Logging primitives === */
108
109 inline EventLog& log() { return TrackingAllocator::log; }
110
111 template<typename...XS>
112 inline void
113 logAlarm (XS const& ...xs)
114 {
115 log().error (joinDash(xs...));
116 }
117
118 template<typename...ARGS>
119 inline void
120 logAlloc (Literal pool, string fun, ARGS const& ...args)
121 {
122 log().call (pool,fun,args...);
123 }
124
125
129 { // note: obsolete entries never discarded
130 using PoolTab = std::unordered_map<Literal, std::weak_ptr<MemoryPool>>;
131
132 PoolTab pools_{};
133
134 public:
135 static PoolHandle locate (Literal poolID);
136
137 private:
138 PoolHandle fetch_or_create (Literal poolID);
139 };
140
144
145
148 PoolRegistry::locate (Literal poolID)
149 {
150 return poolReg().fetch_or_create (poolID);
151 }
152
153
155 PoolRegistry::fetch_or_create (Literal poolID)
156 {
157 auto& entry = pools_[poolID];
158 auto handle = entry.lock();
159 if (handle) return handle;
160
161 // create a new pool and enrol it for given ID
162 PoolHandle newPool = make_shared<MemoryPool> (poolID);
163 entry = newPool;
164 return newPool;
165 }
166 }//(End)Implementation memory pools and registration
167
168
169
170
171
173 : mem_{PoolRegistry::locate(GLOBAL)}
174 { }
175
177 : mem_{PoolRegistry::locate(id)}
178 { }
179
180
189 {
190 ENSURE (mem_);
191 return mem_->addAlloc (bytes)
192 .buff.front();
193 }
194
200 void
201 TrackingAllocator::deallocate (Location loc, size_t bytes) noexcept
202 {
203 ENSURE (mem_);
204 mem_->discardAlloc (loc, bytes);
205 }
206
208 MemoryPool::addAlloc (size_t bytes)
209 {
210 Allocation newAlloc;
211 newAlloc.buff.allocate (bytes);
212 Location loc = newAlloc.buff.front();
213 ASSERT (not contains (allocs_, loc));
214 newAlloc.entryID = ++entryNr_;
215 logAlloc (poolID_, "allocate", bytes, newAlloc.entryID, showAdr(loc));
216 checksum_ += newAlloc.entryID * bytes;
217 return allocs_.emplace (loc, move(newAlloc))
218 .first->second;
219 }
220
221 void
223 {
224 if (contains (allocs_, loc))
225 {
226 auto& entry = allocs_[loc];
227 ASSERT (entry.buff);
228 ASSERT (entry.buff.front() == loc);
229 if (entry.buff.size() != bytes) // *deliberately* tolerating wrong data, but log incident to support diagnostics
230 logAlarm ("SizeMismatch", bytes, "≠", entry.buff.size(), entry.entryID, showAdr(loc));
231
232 logAlloc (poolID_, "deallocate", bytes, entry.entryID, bytes, showAdr(loc));
233 checksum_ -= entry.entryID * bytes; // Note: using the given size (if wrong ⟿ checksum mismatch)
234 allocs_.erase(loc);
235 }
236 else // deliberately no exception here (for better diagnostics)
237 logAlarm ("FreeUnknown", bytes, showAdr(loc));
238 }
239
240
243 {
244 return contains (allocs_, loc)? & allocs_.at(loc)
245 : nullptr;
246 }
247
248
249 HashVal
251 {
252 return checksum_;
253 }
254
255 size_t
257 {
258 return allocs_.size();
259 }
260
261 size_t
263 {
264 return explore(allocs_)
265 .transform ([](auto& it){ return it->second.buff.size(); })
266 .resultSum();
267 }
268
269
270
271 /* ===== Diagnostics ===== */
272
273 EventLog TrackingAllocator::log{"test::TrackingAllocator"};
274
275
277 HashVal
279 {
280 PoolHandle pool = PoolRegistry::locate (poolID);
281 return pool->getChecksum();
282 }
283
285 size_t
287 {
288 PoolHandle pool = PoolRegistry::locate (poolID);
289 return pool.use_count() - 1;
290 }
291
293 size_t
295 {
296 PoolHandle pool = PoolRegistry::locate (poolID);
297 return pool->getAllocationCnt();
298 }
299
301 size_t
303 {
304 PoolHandle pool = PoolRegistry::locate (poolID);
305 return pool->calcAllocSize();
306 }
307
308
310 bool
312 {
313 ENSURE (mem_);
314 return bool(mem_->findAlloc (memLoc));
315 }
316
318 size_t
320 {
321 ENSURE (mem_);
322 auto* entry = mem_->findAlloc (memLoc);
323 return entry? entry->buff.size()
324 : 0;
325 }
326
328 HashVal
330 {
331 ENSURE (mem_);
332 auto* entry = mem_->findAlloc (memLoc);
333 return entry? entry->entryID
334 : 0;
335 }
336
337 Literal
339 {
340 ENSURE (mem_);
341 return mem_->getPoolID();
342 }
343
344
345
346}} // namespace lib::test
Access point to singletons and other kinds of dependencies designated by type.
Definition depend.hpp:281
Inline string literal.
Definition symbol.hpp:78
Managed uninitialised Heap-allocated storage with array like access.
Helper to log and verify the occurrence of events.
EventLog & error(string text)
Log an error note.
EventLog & call(string target, string function)
Log occurrence of a function call with no arguments.
Allocation & addAlloc(size_t bytes)
Allocation const * findAlloc(Location) const
std::unordered_map< const Location, Allocation, LocationHash > AllocTab
void discardAlloc(void *loc, size_t)
void deallocate(Location, size_t=0) noexcept
Discard and forget an allocation created through this allocator.
static size_t numAlloc(Literal pool=GLOBAL)
get active allocation count for mem-pool
Location allocate(size_t n)
Allot a memory block of given size bytes.
bool manages(Location) const
probe if this allocator pool did allocate the given memory location
TrackingAllocator()
can be default created to attach to a common pool
HashVal getID(Location) const
retrieve the internal registration ID for this allocation.
static size_t numBytes(Literal pool=GLOBAL)
calculate currently allotted Bytes for mem-pool
static size_t use_count(Literal pool=GLOBAL)
determine number of active front-end handles
size_t getSize(Location) const
retrieve the registered size of this allocation, if known.
static HashVal checksum(Literal pool=GLOBAL)
get Checksum for specific mem-pool
std::unordered_map< Literal, std::weak_ptr< MemoryPool > > PoolTab
Types marked with this mix-in may be moved but not copied.
Definition nocopy.hpp:50
Any copy and copy construction prohibited.
Definition nocopy.hpp:38
Singleton services and Dependency Injection.
Building tree expanding and backtracking evaluations within hierarchical scopes.
Depend< MemoryPool > globalPool
singleton for default pool
void logAlloc(Literal pool, string fun, ARGS const &...args)
std::shared_ptr< MemoryPool > PoolHandle
Implementation namespace for support and library code.
auto explore(IT &&srcSeq)
start building a IterExplorer by suitably wrapping the given iterable source.
size_t HashVal
a STL compatible hash value
Definition hash-value.h:52
Test runner and basic definitions for tests.
ostream & showAdr(ostream &stream, void const *addr)
preconfigured format for pretty-printing of addresses
bool contains(MAP &map, typename MAP::key_type const &key)
shortcut for containment test on a map
Definition util.hpp:230
string joinDash(ARGS const &...args)
shortcut: join directly with dashes
registration entry to maintain a single allocation
UninitialisedDynBlock< byte > buff
HashVal operator()(Location const &loc) const
Unittest helper code: a custom allocator to track memory usage.
A raw memory block with proper alignment and array access.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...