1 module upromised.backtrace; 2 import core.runtime : Runtime, TraceHandler; 3 4 struct BaseStack { 5 Throwable.TraceInfo base; 6 size_t delegate() skip; 7 } 8 9 struct PromiseTraceHandler { 10 TraceHandler original; 11 BaseStack base; 12 } 13 14 Throwable.TraceInfo dgTraceInfo(void delegate(void delegate(const(char[]) value, int* stop)) impl) nothrow { 15 return new class Throwable.TraceInfo { 16 override int opApply(scope int delegate(ref const(char[])) dg) const { 17 return opApply((ref size_t, ref const(char[]) buf) { 18 return dg(buf); 19 }); 20 } 21 22 override int opApply(scope int delegate(ref size_t, ref const(char[])) dg) const { 23 ulong count; 24 impl((value, stop) { 25 *stop = dg(count, value); 26 ++count; 27 }); 28 return 0; 29 } 30 31 override string toString() const { 32 string bt; 33 this.opApply((ref size_t a, ref const(char[]) b) { 34 bt ~= b; 35 bt ~= "\n"; 36 return 0; 37 }); 38 return bt; 39 } 40 }; 41 } 42 Throwable.TraceInfo traceinfo(string[] list) nothrow { 43 return dgTraceInfo((cb) { 44 int r; 45 auto listIt = list; 46 while (r == 0) { 47 if (listIt.length == 0) return; 48 cb(listIt[0], &r); 49 listIt = listIt[1..$]; 50 } 51 }); 52 } 53 Throwable.TraceInfo concat(Throwable.TraceInfo a, Throwable.TraceInfo b) nothrow { 54 import core.stdc.stdlib : abort; 55 if (!(a !is null)) abort(); 56 if (!(b !is null)) abort(); 57 return dgTraceInfo((cb) { 58 int r; 59 a.opApply((ref const(char[]) v) { 60 cb(v, &r); 61 return r; 62 }); 63 if (r == 0) b.opApply((ref const(char[]) v) { 64 cb(v, &r); 65 return r; 66 }); 67 }); 68 } 69 ulong length(Throwable.TraceInfo info) { 70 import core.stdc.stdlib : abort; 71 72 if (info is null) abort(); 73 74 size_t total; 75 foreach(_; info) { 76 total++; 77 } 78 return total; 79 } 80 81 Throwable.TraceInfo trimTrail(Throwable.TraceInfo info, ulong amount) nothrow { 82 return trimTrailDg(info, () => amount); 83 } 84 Throwable.TraceInfo trimTrailDg(Throwable.TraceInfo info, ulong delegate() amountDg) nothrow { 85 import core.stdc.stdlib : abort; 86 if (info is null) abort(); 87 if (amountDg.funcptr is null) abort(); 88 89 return dgTraceInfo((cb) { 90 size_t total = info.length; 91 size_t amount = amountDg(); 92 size_t limit = total >= amount ? total - amount : 0; 93 int r; 94 info.opApply((ref const(char[]) v) { 95 if (limit-- == 0) return 1; 96 cb(v, &r); 97 return r; 98 }); 99 }); 100 } 101 Throwable.TraceInfo skipMagic(Throwable.TraceInfo a, string magic) nothrow { 102 import core.stdc.stdlib : abort; 103 import std.algorithm : countUntil; 104 105 if (a is null) abort(); 106 107 magic = magic[1..$]; 108 return dgTraceInfo((cb) { 109 bool skipping = true; 110 int r; 111 a.opApply((ref const(char[]) v) { 112 if (skipping) { 113 if (v.countUntil(magic) >= 0) { 114 skipping = false; 115 } 116 return 0; 117 } else { 118 cb(v, &r); 119 return r; 120 } 121 }); 122 if (skipping) { 123 a.opApply((ref const(char[]) v) { 124 cb(v, &r); 125 return r; 126 }); 127 } 128 }); 129 } 130 131 private __gshared PromiseTraceHandler handler; 132 enum handlerFuncMagic = "___98371_realBacktraceMagic_31810"; 133 pragma(mangle, handlerFuncMagic) Throwable.TraceInfo handlerFunc(void* pos) { 134 import core.runtime : defaultTraceHandler; 135 136 return realBacktrace(pos, handlerFuncMagic).trimTrailDg(handler.base.skip).concat(handler.base.base); 137 } 138 139 static this() { 140 import core.runtime : defaultTraceHandler; 141 142 handler.base.base = [].traceinfo; 143 handler.base.skip = () => 0; 144 handler.original = &defaultTraceHandler; 145 Runtime.traceHandler(&handlerFunc); 146 } 147 148 enum realBacktraceMagic = "___38912_realBacktrace_32191"; 149 pragma(mangle, realBacktraceMagic) pragma(inline, false) Throwable.TraceInfo realBacktrace(void* pos, string skipMagicString = realBacktraceMagic) nothrow { 150 return wa1(pos).skipMagic(skipMagicString); 151 } 152 // Dlang skip a few frames to remove internal function from stack traces. But sometimes this does not work. 153 // Using a magic name to remove the internal function always works. 154 pragma(inline, false) Throwable.TraceInfo wa1(void* pos) nothrow { 155 return wa2(pos); 156 } 157 pragma(inline, false) Throwable.TraceInfo wa2(void* pos) nothrow { 158 return wa3(pos); 159 } 160 pragma(inline, false) Throwable.TraceInfo wa3(void* pos) nothrow { 161 return wa4(pos); 162 } 163 pragma(inline, false) Throwable.TraceInfo wa4(void* pos) nothrow { 164 try { 165 return handler.original(pos); 166 } catch(Exception e) { 167 assert(false); 168 } 169 } 170 171 BaseStack setBasestack(Throwable.TraceInfo backBt) nothrow { 172 import std.array : array; 173 174 auto prev = handler.base; 175 handler.base.base = backBt; 176 auto skip = realBacktrace(null); 177 try { 178 handler.base.skip = () { 179 auto l = skip.length; 180 if (l == 0) return 0; 181 return skip.length - 1; 182 }; 183 } catch(Exception) { 184 assert(false); 185 } 186 return prev; 187 } 188 189 void recoverBasestack(BaseStack a) nothrow { 190 handler.base = a; 191 } 192 193 enum backtrace_magic = "892179_backtrace_381243"; 194 pragma(mangle, backtrace_magic) Throwable.TraceInfo backtrace(void* pos = null, string skipMagicValue = backtrace_magic) nothrow { 195 try { 196 return Runtime.traceHandler()(pos).skipMagic(skipMagicValue); 197 } catch(Exception) { 198 assert(false); 199 } 200 }