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 }