1 module memutils.debugger;
2 import memutils.allocators;
3 import memutils.hashmap;
4 
5 /**
6 * Another proxy allocator used to aggregate statistics and to enforce correct usage.
7 */
8 final class DebugAllocator(Base : Allocator) : Allocator {
9 	private {
10 		HashMap!(void*, size_t, Malloc) m_blocks;
11 		size_t m_bytes;
12 		size_t m_maxBytes;
13 	}
14 	package Base m_baseAlloc;
15 	
16 	this()
17 	{
18 		m_baseAlloc = getAllocator!Base();
19 	}
20 
21 	~this() { m_blocks.clear(); }
22 	public {
23 		@property size_t allocatedBlockCount() const { return m_blocks.length; }
24 		@property size_t bytesAllocated() const { return m_bytes; }
25 		@property size_t maxBytesAllocated() const { return m_maxBytes; }
26 		void printMap() {
27 			foreach(const ref void* ptr, const ref size_t sz; m_blocks) {
28 				logDebug(ptr, " sz ", sz);
29 			}
30 		}
31 	}
32 	void[] alloc(size_t sz)
33 	{
34 
35 		assert(sz > 0, "Cannot serve a zero-length allocation");
36 
37 		//logTrace("Bytes allocated in ", Base.stringof, ": ", bytesAllocated());
38 		auto ret = m_baseAlloc.alloc(sz);
39 		synchronized(this) {
40 			assert(ret.length == sz, "base.alloc() returned block with wrong size.");
41 			assert(m_blocks.get(cast(const)ret.ptr, size_t.max) == size_t.max, "base.alloc() returned block that is already allocated: " ~ ret.ptr.to!string);
42 			m_blocks[ret.ptr] = sz;
43 			m_bytes += sz;
44 			if( m_bytes > m_maxBytes ){
45 				m_maxBytes = m_bytes;
46 				//logTrace("New allocation maximum: %d (%d blocks)", m_maxBytes, m_blocks.length);
47 			}
48 		}
49 
50 
51 		//logDebug("Alloc ptr: ", ret.ptr, " sz: ", ret.length);
52 		
53 		return ret;
54 	}
55 	
56 	void[] realloc(void[] mem, size_t new_size)
57 	{
58 		assert(new_size > 0 && mem.length > 0, "Cannot serve a zero-length reallocation");
59 		void[] ret;
60 		size_t sz;
61 		synchronized(this) {
62 			sz = m_blocks.get(mem.ptr, size_t.max);
63 			assert(sz != size_t.max, "realloc() called with non-allocated pointer.");
64 			assert(sz == mem.length, "realloc() called with block of wrong size.");
65 		}
66 		ret = m_baseAlloc.realloc(mem, new_size);
67 		synchronized(this) {
68 			assert(ret.length == new_size, "base.realloc() returned block with wrong size.");
69 			assert(ret.ptr is mem.ptr || m_blocks.get(ret.ptr, size_t.max) == size_t.max, "base.realloc() returned block that is already allocated.");
70 			m_bytes -= sz;
71 			m_blocks.remove(mem.ptr);
72 			m_blocks[ret.ptr] = new_size;
73 			m_bytes += new_size;
74 		}
75 		return ret;
76 	}
77 	
78 	void free(void[] mem)
79 	{
80 		assert(mem.length > 0, "Cannot serve a zero-length deallocation");
81 
82 		size_t sz;
83 		synchronized(this) {
84 			sz = m_blocks.get(cast(const)mem.ptr, size_t.max);
85 			assert(sz != size_t.max, "free() called with non-allocated object. "~ mem.ptr.to!string~ " m_blocks len: "~ m_blocks.length.to!string);
86 			assert(sz == mem.length, "free() called with block of wrong size.");
87 		}
88 
89 		//logDebug("free ptr: ", mem.ptr, " sz: ", mem.length);
90 		m_baseAlloc.free(mem);
91 		
92 		synchronized(this) {
93 			m_bytes -= sz;
94 			m_blocks.remove(mem.ptr);
95 		}
96 	}
97 
98 	package void ignore(void* ptr) {
99 		synchronized(this) {
100 			size_t sz = m_blocks.get(cast(const) ptr, size_t.max);
101 			m_bytes -= sz;
102 			m_blocks.remove(ptr);
103 		}
104 	}
105 }