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