1 module lwdr.tracking; 2 3 import rtoslink; 4 5 version(LWDR_TrackMem) 6 { 7 /// Keeps track of allocations 8 struct AllocationList(T, size_t MaxElements) 9 { 10 static assert(MaxElements > 0, "MaxElements should be at least 1!"); 11 12 private T[MaxElements] payload; 13 private size_t length_ = 0; 14 15 @property bool empty() const { return length_ == 0; } 16 @property size_t length() const { return length_; } 17 18 /// Track an allocation 19 bool add(T t) nothrow @nogc 20 { 21 if(length_ + 1 >= MaxElements) 22 return false; 23 24 payload[length_++] = t; 25 return true; 26 } 27 28 /// Get last allocation 29 auto getLast() nothrow @nogc 30 { 31 return payload[length_ - 1]; 32 } 33 34 /// Remove last allocation 35 void removeLast() nothrow @nogc 36 { 37 if(empty) return; 38 39 payload[length_ - 1] = T.init; 40 length_--; 41 } 42 43 /// Find the last occurence of `t` and remove it 44 bool removeLastOccurenceOf(T t) nothrow @nogc 45 { 46 if(empty) return false; 47 48 size_t index; 49 bool found = false; 50 51 for(size_t i = length_ - 1; i > 0; i--) 52 { 53 if(payload[i] == t) 54 { 55 index = i; 56 found = true; 57 } 58 } 59 60 if(!found) 61 return false; 62 63 for(size_t i = index; i < length_ - 1; i++) 64 { 65 payload[i] = payload[i+1]; 66 } 67 payload[length_ - 1] = T.init; 68 length_--; 69 return true; 70 } 71 } 72 73 version(LWDR_TLS) 74 private AllocationList!(void*, 16) trackedAllocations; /// Track allocations per-thread 75 else 76 private __gshared AllocationList!(void*, 16) trackedAllocations; /// Track allocations across all threads 77 78 /// Stores state of allocations at specific scope 79 struct MemAlloc 80 { 81 size_t allocsAtScope; /// number of allocations when this struct was initiated 82 83 /// free allocations until `trackedAllocations.length` matches `this.allocsAtScope`. 84 void free() nothrow @nogc 85 { 86 assert(allocsAtScope >= trackedAllocations.length); 87 88 auto difference = trackedAllocations.length - allocsAtScope; 89 foreach(d; 0 .. difference) 90 { 91 auto ptr = trackedAllocations.getLast; 92 trackedAllocations.removeLast; 93 rtosbackend_heapfreealloc(ptr); 94 } 95 } 96 } 97 98 /// Get the current state of allocations 99 MemAlloc enterMemoryTracking() nothrow @nogc 100 { 101 return MemAlloc(trackedAllocations.length); 102 } 103 104 version(LWDR_TLS) 105 private bool currentlyTracking = false; /// Are we currently tracking allocations? 106 else 107 private __gshared bool currentlyTracking = false; /// Are we currently tracking allocations? 108 /// Are we currently allocations? 109 bool isCurrentTracking() nothrow @nogc { return currentlyTracking; } 110 /// Begin tracking allocations 111 void enableMemoryTracking() nothrow @nogc { currentlyTracking = true; } 112 /// Stop tracking allocations 113 void disableMemoryTracking() nothrow @nogc { currentlyTracking = false; } 114 115 /// Allocate heap memory of `sz` bytes. If tracking allocations, the resultant pointer will be added to `trackedAllocations` 116 void* lwdrInternal_alloc(size_t sz) nothrow @nogc 117 { 118 auto ptr = rtosbackend_heapalloc(sz); 119 if(currentlyTracking) 120 { 121 trackedAllocations.add(ptr); 122 } 123 return ptr; 124 } 125 126 /// Dealloc heap memory 127 void lwdrInternal_free(void* ptr) nothrow @nogc 128 { 129 trackedAllocations.removeLastOccurenceOf(ptr); 130 rtosbackend_heapfreealloc(ptr); 131 } 132 133 /// Allocate `sz` bytes heap memory represented by a slice of ubytes. If tracking allocations, the resultant slice will be added to `trackedAllocations` 134 ubyte[] lwdrInternal_allocBytes(size_t sz) nothrow @nogc 135 { 136 return cast(ubyte[])lwdrInternal_alloc(sz)[0..sz]; 137 } 138 } 139 else 140 { 141 /// Allocate heap memory of `sz` bytes 142 void* lwdrInternal_alloc(size_t sz) nothrow @nogc pure 143 { 144 return rtosbackend_heapalloc(sz); 145 } 146 147 /// Free heap memory 148 void lwdrInternal_free(void* ptr) nothrow @nogc pure 149 { 150 return rtosbackend_heapfreealloc(ptr); 151 } 152 153 /// Allocate `sz` bytes of heap memory represented by a slice of ubytes. 154 ubyte[] lwdrInternal_allocBytes(size_t sz) nothrow pure @nogc 155 { 156 return cast(ubyte[])lwdrInternal_alloc(sz)[0..sz]; 157 } 158 }