1 /* 2 A tiny hack to install a GC proxy and track all allocations, gathering some 3 stats and logging to given File (or stdout, by default). 4 5 Use it like this: 6 void myCode() { 7 auto tracker = allocsTracker(); 8 ... // do some work 9 // while `tracker` is alive all allocations will be shown 10 } 11 12 Alternatively use pair of functions 13 startTrackingAllocs() and stopTrackingAllocs(). 14 15 */ 16 17 module trackallocs; 18 import std.stdio, core.memory, std.c.string, std.traits, std.range, std.algorithm; 19 20 //stats 21 __gshared ulong allocs_mc = 0; // numbers of calls for malloc, qalloc and calloc 22 __gshared ulong allocs_qc = 0; 23 __gshared ulong allocs_cc = 0; 24 __gshared ulong allocs_msz = 0; // total numbers of bytes requested 25 __gshared ulong allocs_qsz = 0; 26 __gshared ulong allocs_csz = 0; 27 28 void clearAllocStats() { 29 allocs_mc = 0; allocs_qc = 0; allocs_cc = 0; 30 allocs_msz = 0; allocs_qsz = 0; allocs_csz = 0; 31 } 32 33 void startTrackingAllocs(File logFile = stdout) { 34 allocs_file = logFile; 35 if (myProxy.gc_calloc is null) { 36 initGcProxy(); 37 myProxy.gc_malloc = &genAlloc!("m"); 38 myProxy.gc_qalloc = &genAlloc!("q"); 39 myProxy.gc_calloc = &genAlloc!("c"); 40 } 41 *pproxy = &myProxy; 42 logFile.writeln("allocs tracking started"); 43 } 44 45 void stopTrackingAllocs() { 46 *pproxy = null; 47 allocs_file.writeln("allocs tracking ended"); 48 } 49 50 auto allocsTracker(File logFile = stdout) // RAII version: starts tracking now, ends it when returned value dies 51 { 52 struct S { 53 ~this() { stopTrackingAllocs(); } 54 @disable this(this); 55 } 56 startTrackingAllocs(logFile); 57 return S(); 58 } 59 60 private: ////////////////////////////////////////////////////////////////////// 61 alias BlkInfo = GC.BlkInfo; 62 63 struct Proxy // taken from proxy.d (d runtime) 64 { 65 extern (C) 66 { 67 void function() gc_enable; 68 void function() gc_disable; 69 void function() gc_collect; 70 void function() gc_minimize; 71 72 uint function(void*) gc_getAttr; 73 uint function(void*, uint) gc_setAttr; 74 uint function(void*, uint) gc_clrAttr; 75 76 static if (__VERSION__ >= 2066) { 77 void* function(size_t, uint, const TypeInfo) gc_malloc; 78 BlkInfo function(size_t, uint, const TypeInfo) gc_qalloc; 79 void* function(size_t, uint, const TypeInfo) gc_calloc; 80 void* function(void*, size_t, uint ba, const TypeInfo) gc_realloc; 81 size_t function(void*, size_t, size_t, const TypeInfo) gc_extend; 82 } else { 83 void* function(size_t, uint) gc_malloc; 84 BlkInfo function(size_t, uint) gc_qalloc; 85 void* function(size_t, uint) gc_calloc; 86 void* function(void*, size_t, uint ba) gc_realloc; 87 size_t function(void*, size_t, size_t) gc_extend; 88 } 89 size_t function(size_t) gc_reserve; 90 void function(void*) gc_free; 91 92 void* function(void*) gc_addrOf; 93 size_t function(void*) gc_sizeOf; 94 95 BlkInfo function(void*) gc_query; 96 97 void function(void*) gc_addRoot; 98 static if (__VERSION__ >= 2066) { 99 void function(void*, size_t, const TypeInfo) gc_addRange; 100 } else { 101 void function(void*, size_t) gc_addRange; 102 } 103 104 void function(void*) gc_removeRoot; 105 void function(void*) gc_removeRange; 106 static if (__VERSION__ >= 2066) { 107 void function(in void[]) gc_runFinalizers; 108 } 109 } 110 } 111 112 __gshared Proxy myProxy; 113 __gshared Proxy *pOrg; // pointer to original Proxy of runtime 114 __gshared Proxy** pproxy; // should point to proxy var in runtime (proxy.d) 115 __gshared File allocs_file; // where to write messages 116 117 template FunArgsTypes(string funname) { 118 alias FunType = typeof(*__traits(getMember, myProxy, funname)); 119 alias FunArgsTypes = ParameterTypeTuple!FunType; 120 } 121 122 extern(C) { 123 Proxy* gc_getProxy(); 124 125 auto genCall(string funname)(FunArgsTypes!funname args) { 126 *pproxy = null; 127 scope(exit) *pproxy = &myProxy; 128 return __traits(getMember, pOrg, funname)(args); 129 } 130 131 static if (__VERSION__ >= 2066) { 132 auto genAlloc(string letter)(size_t sz, uint ba, const TypeInfo ti) { 133 *pproxy = null; 134 scope(exit) *pproxy = &myProxy; 135 136 mixin("alias cc = allocs_" ~ letter ~ "c;"); 137 mixin("alias asz = allocs_" ~ letter ~ "sz;"); 138 allocs_file.writefln("gc_"~letter~"alloc(%d, %d) :%d %s", sz, ba, cc, ti is null ? "" : ti.toString()); 139 asz += sz; cc++; 140 return __traits(getMember, pOrg, "gc_"~letter~"alloc")(sz, ba, ti); 141 } 142 } else { 143 auto genAlloc(string letter)(size_t sz, uint ba) { 144 *pproxy = null; 145 scope(exit) *pproxy = &myProxy; 146 147 mixin("alias cc = allocs_" ~ letter ~ "c;"); 148 mixin("alias asz = allocs_" ~ letter ~ "sz;"); 149 allocs_file.writefln("gc_"~letter~"alloc(%d, %d) :%d", sz, ba, cc); 150 asz += sz; cc++; 151 return __traits(getMember, pOrg, "gc_"~letter~"alloc")(sz, ba); 152 } 153 } 154 155 auto genNop(string funname)(FunArgsTypes!funname args) { } 156 } 157 158 void initGcProxy() { 159 pOrg = gc_getProxy(); 160 pproxy = cast(Proxy**) (cast(byte*)pOrg + Proxy.sizeof); 161 foreach(funname; __traits(allMembers, Proxy)) 162 __traits(getMember, myProxy, funname) = &genCall!funname; 163 } 164 165 166 version(unittest) { 167 extern(C) { 168 void gc_setProxy( Proxy* p ); 169 void gc_clrProxy(); 170 } 171 } 172 173 unittest { 174 //make sure our way to get pproxy works 175 initGcProxy(); 176 myProxy.gc_addRoot = &genNop!"gc_addRoot"; 177 myProxy.gc_addRange = &genNop!"gc_addRange"; 178 myProxy.gc_removeRoot = &genNop!"gc_removeRoot"; 179 myProxy.gc_removeRange = &genNop!"gc_removeRange"; 180 assert(*pproxy == null); 181 gc_setProxy(&myProxy); 182 assert(*pproxy == &myProxy); 183 gc_clrProxy(); 184 assert(*pproxy == null); 185 writeln("pproxy test OK"); 186 memset(&myProxy, 0, Proxy.sizeof); 187 } 188 189 unittest { 190 clearAllocStats(); 191 scope atr = allocsTracker(); 192 assert(allocs_qsz == 0 && allocs_qc == 0); 193 scope arr = new int[32]; 194 assert(allocs_qsz == 129 && allocs_qc == 1); 195 writeln("allocsTracker() and new int[] test OK"); 196 } 197 198 unittest { 199 clearAllocStats(); 200 startTrackingAllocs(stderr); 201 assert(allocs_qsz == 0 && allocs_qc == 0); 202 scope arr = new int[32]; 203 assert(allocs_qsz == 129 && allocs_qc == 1); 204 stopTrackingAllocs(); 205 206 scope arr2 = new int[42]; 207 assert(allocs_qsz == 129 && allocs_qc == 1); 208 209 startTrackingAllocs(stderr); 210 scope arr3 = new byte[10]; 211 assert(allocs_qsz != 129 && allocs_qc != 1); 212 stopTrackingAllocs(); 213 214 writeln("startTrackingAllocs() and new int[] test OK"); 215 }