1 module served.utils.trace; 2 3 import core.time; 4 import std.stdio : File; 5 6 struct TraceDataStat(T) 7 { 8 T min; 9 T max; 10 T total; 11 12 void resetTo(T value) 13 { 14 min = value; 15 max = value; 16 total = value; 17 } 18 19 void put(T value) 20 { 21 if (value < min) 22 min = value; 23 if (value > max) 24 max = value; 25 total += value; 26 } 27 28 auto map(alias fn)() 29 { 30 import std.functional : unaryFun; 31 32 alias mapFun = unaryFun!fn; 33 34 auto minV = mapFun(min); 35 auto maxV = mapFun(max); 36 auto totalV = mapFun(total); 37 38 return TraceDataStat!(typeof(minV))(minV, maxV, totalV); 39 } 40 } 41 42 struct TraceState 43 { 44 MonoTime time; 45 long gc; 46 47 static TraceState current() 48 { 49 import core.memory : GC; 50 51 TraceState ret; 52 ret.time = MonoTime.currTime; 53 static if (__traits(hasMember, typeof(GC.stats()), "allocatedInCurrentThread")) 54 ret.gc = GC.stats().allocatedInCurrentThread; 55 else 56 ret.gc = GC.stats().usedSize; 57 return ret; 58 } 59 } 60 61 struct TraceData 62 { 63 TraceDataStat!long gcUsage; 64 TraceDataStat!Duration runtime; 65 int timesCalled; 66 67 TraceState start() 68 { 69 return TraceState.current(); 70 } 71 72 void end(TraceState state) 73 { 74 auto now = TraceState.current(); 75 if (timesCalled == 0) 76 { 77 gcUsage.resetTo(now.gc - state.gc); 78 runtime.resetTo(now.time - state.time); 79 } 80 else 81 { 82 gcUsage.put(now.gc - state.gc); 83 runtime.put(now.time - state.time); 84 } 85 timesCalled++; 86 } 87 } 88 89 TraceData*[string] _traceInfos; 90 91 /// CTFE function to generate mixin code for function tracing 92 /// The trace 93 const(char)[] traceStatistics(const(char)[] name) 94 { 95 import std.ascii : isAlphaNum; 96 97 char[] id; 98 foreach (char c; name) 99 if (c.isAlphaNum) 100 id ~= c; 101 //dfmt off 102 return ` 103 static bool _trace_init_` ~ id ~ ` = false; 104 static TraceData _trace_info_` ~ id ~ `; 105 if (!_trace_init_` ~ id ~ `) 106 { 107 _traceInfos["` ~ name ~ `"] = &_trace_info_` ~ id ~ `; 108 _trace_init_` ~ id ~ ` = true; 109 } 110 auto _trace_state_` ~ id ~ ` = _trace_info_` ~ id ~ `.start(); 111 112 scope (exit) 113 { 114 _trace_info_` ~ id ~ `.end(_trace_state_` ~ id ~ `); 115 } 116 `; 117 //dfmt on 118 } 119 120 void dumpTraceInfos(File output) 121 { 122 import std.array : array; 123 import std.algorithm : sort; 124 125 output.write("trace name\tnum calls"); 126 foreach (stat; ["time", "gc"]) 127 output.writef("\tmin %s\tmax %s\ttotal %s\tavg %s", 128 stat, stat, stat, stat); 129 output.writeln(); 130 131 auto kv = _traceInfos.byKeyValue.array 132 .sort!"a.value.timesCalled > b.value.timesCalled"; 133 134 foreach (entry; kv) 135 { 136 int total = entry.value.timesCalled; 137 double dTotal = cast(double)total; 138 139 void dumpStat(T)(TraceDataStat!T stat) 140 { 141 output.write('\t', stat.min); 142 output.write('\t', stat.max); 143 output.write('\t', stat.total); 144 output.write('\t', stat.total / dTotal); 145 } 146 147 output.write(entry.key, '\t', total); 148 dumpStat(entry.value.runtime.map!"a.total!`msecs`"); 149 dumpStat(entry.value.gcUsage); 150 output.writeln(); 151 } 152 }