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, core.stdc.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() nothrow { stopTrackingAllocs(); } 54 this(this) @disable; 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 void* function(size_t, uint, const TypeInfo) gc_malloc; 77 BlkInfo function(size_t, uint, const TypeInfo) gc_qalloc; 78 void* function(size_t, uint, const TypeInfo) gc_calloc; 79 void* function(void*, size_t, uint ba, const TypeInfo) gc_realloc; 80 size_t function(void*, size_t, size_t, const TypeInfo) gc_extend; 81 size_t function(size_t) gc_reserve; 82 void function(void*) gc_free; 83 84 void* function(void*) gc_addrOf; 85 size_t function(void*) gc_sizeOf; 86 87 BlkInfo function(void*) gc_query; 88 89 void function(void*) gc_addRoot; 90 void function(void*, size_t, const TypeInfo) gc_addRange; 91 92 void function(void*) gc_removeRoot; 93 void function(void*) gc_removeRange; 94 void function(in void[]) gc_runFinalizers; 95 } 96 } 97 98 __gshared Proxy myProxy; 99 __gshared Proxy *pOrg; // pointer to original Proxy of runtime 100 __gshared Proxy** pproxy; // should point to proxy var in runtime (proxy.d) 101 __gshared File allocs_file; // where to write messages 102 103 template FunArgsTypes(string funname) { 104 alias FunType = typeof(*__traits(getMember, myProxy, funname)); 105 alias FunArgsTypes = ParameterTypeTuple!FunType; 106 } 107 108 extern(C) { 109 Proxy* gc_getProxy(); 110 111 auto genCall(string funname)(FunArgsTypes!funname args) { 112 *pproxy = null; 113 scope(exit) *pproxy = &myProxy; 114 return __traits(getMember, pOrg, funname)(args); 115 } 116 117 auto genAlloc(string letter)(size_t sz, uint ba, const TypeInfo ti) { 118 *pproxy = null; 119 scope(exit) *pproxy = &myProxy; 120 121 mixin("alias cc = allocs_" ~ letter ~ "c;"); 122 mixin("alias asz = allocs_" ~ letter ~ "sz;"); 123 allocs_file.writefln("gc_"~letter~"alloc(%d, %d) :%d %s", sz, ba, cc, ti is null ? "" : ti.toString()); 124 asz += sz; cc++; 125 return __traits(getMember, pOrg, "gc_"~letter~"alloc")(sz, ba, ti); 126 } 127 128 auto genNop(string funname)(FunArgsTypes!funname args) { } 129 } 130 131 void initGcProxy() { 132 pOrg = gc_getProxy(); 133 pproxy = cast(Proxy**) (cast(byte*)pOrg + Proxy.sizeof); 134 foreach(funname; __traits(allMembers, Proxy)) 135 __traits(getMember, myProxy, funname) = &genCall!funname; 136 } 137 138 139 version (unittest) { 140 extern(C) { 141 void gc_setProxy( Proxy* p ); 142 void gc_clrProxy(); 143 } 144 } 145 146 unittest { 147 //make sure our way to get pproxy works 148 initGcProxy(); 149 myProxy.gc_addRoot = &genNop!"gc_addRoot"; 150 myProxy.gc_addRange = &genNop!"gc_addRange"; 151 myProxy.gc_removeRoot = &genNop!"gc_removeRoot"; 152 myProxy.gc_removeRange = &genNop!"gc_removeRange"; 153 assert(*pproxy == null); 154 gc_setProxy(&myProxy); 155 assert(*pproxy == &myProxy); 156 gc_clrProxy(); 157 assert(*pproxy == null); 158 writeln("pproxy test OK"); 159 memset(&myProxy, 0, Proxy.sizeof); 160 } 161 162 unittest { 163 clearAllocStats(); 164 scope atr = allocsTracker(); 165 assert(allocs_qsz == 0 && allocs_qc == 0); 166 scope arr = new int[32]; 167 assert(allocs_qsz == 129 && allocs_qc == 1); 168 writeln("allocsTracker() and new int[] test OK"); 169 } 170 171 unittest { 172 clearAllocStats(); 173 startTrackingAllocs(stderr); 174 assert(allocs_qsz == 0 && allocs_qc == 0); 175 scope arr = new int[32]; 176 assert(allocs_qsz == 129 && allocs_qc == 1); 177 stopTrackingAllocs(); 178 179 scope arr2 = new int[42]; 180 assert(allocs_qsz == 129 && allocs_qc == 1); 181 182 startTrackingAllocs(stderr); 183 scope arr3 = new byte[10]; 184 assert(allocs_qsz != 129 && allocs_qc != 1); 185 stopTrackingAllocs(); 186 187 writeln("startTrackingAllocs() and new int[] test OK"); 188 }