1 
2 /**
3  *   Stacktracing
4  *
5  *   Functions to generate a stacktrace.
6  *
7  *  Copyright: Copyright (C) 2009 Fawzi
8  *  License:   Tango License
9  *  Author:    Fawzi Mohamed
10  */
11 module tango.core.tools.StackTrace;
12 import tango.core.tools.Demangler;
13 import tango.core.Runtime;
14 import tango.core.Thread;
15 import tango.core.Traits: ctfe_i2a;
16 import tango.stdc.string;
17 import tango.stdc.stringz : fromStringz;
18 import tango.stdc.stdlib: abort;
19 import tango.core.tools.FrameInfo;
20 
21 version(Windows){
22     import tango.core.tools.WinStackTrace;
23 } else {
24     import tango.stdc.posix.ucontext;
25     import tango.stdc.posix.sys.types: pid_t,pthread_t;
26     import tango.stdc.signal;
27     import tango.stdc.stdlib;
28 }
29 version(linux){
30     import tango.core.tools.LinuxStackTrace;
31 }
32 
33 version(CatchRecursiveTracing){
34     __gshared ThreadLocal!(int) recursiveStackTraces;
35 
36     shared static this(){
37         recursiveStackTraces=new ThreadLocal!(int)(0);
38     }
39 }
40 
41 version(Windows){
42 } else {
43    struct TraceContext{
44        bool hasContext;
45        ucontext_t context;
46        pid_t hProcess;
47        pthread_t hThread;
48    }
49 }
50 
51 alias size_t function(TraceContext* context,TraceContext* contextOut,size_t*traceBuf,size_t bufLength,int *flags) AddrBacktraceFunc;
52 __gshared AddrBacktraceFunc addrBacktraceFnc;
53 alias bool function(ref FrameInfo fInfo,const(TraceContext)* context,char[] buf) SymbolizeFrameInfoFnc;
54 __gshared SymbolizeFrameInfoFnc symbolizeFrameInfoFnc;
55 
56 shared static this(){
57     addrBacktraceFnc=&defaultAddrBacktrace;
58     symbolizeFrameInfoFnc=&defaultSymbolizeFrameInfo;
59 }
60 
61 /// sets the function used for address stacktraces
62 extern(C) void rt_setAddrBacktraceFnc(AddrBacktraceFunc f){
63     addrBacktraceFnc=f;
64 }
65 /// sets the function used to symbolize a FrameInfo
66 extern(C) void rt_setSymbolizeFrameInfoFnc(SymbolizeFrameInfoFnc f){
67     symbolizeFrameInfoFnc=f;
68 }
69 /// creates a stack trace (defined in the runtime)
70 extern(C) Exception.TraceInfo rt_createTraceContext( void* ptr );
71 
72 alias Exception.TraceInfo function( void* ptr = null ) TraceHandler;
73 
74 /// builds a backtrace of addresses, the addresses are addresses of the *next* instruction,
75 /// *return* addresses, the most likely the calling instruction is the one before them
76 /// (stack top excluded)
77 extern(C) size_t rt_addrBacktrace(TraceContext* context, TraceContext *contextOut,size_t*traceBuf,size_t bufLength,int *flags){
78     if (addrBacktraceFnc !is null){
79         return addrBacktraceFnc(context,contextOut,traceBuf,bufLength,flags);
80     } else {
81         return 0;
82     }
83 }
84 
85 /// tries to sybolize a frame information, this should try to build the best
86 /// backtrace information, if possible finding the calling context, thus
87 /// if fInfo.exactAddress is false the address might be changed to the one preceding it
88 /// returns true if it managed to at least find the function name
89 extern(C) bool rt_symbolizeFrameInfo(ref FrameInfo fInfo,const(TraceContext)* context,char[] buf){
90     if (symbolizeFrameInfoFnc !is null){
91         return symbolizeFrameInfoFnc(fInfo,context,buf);
92     } else {
93         return false;
94     }
95 }
96 
97 // names of the functions that should be ignored for the backtrace
98 __gshared int[const(char)[]] internalFuncs;
99 shared static this(){
100     /* TODO these probably are inaccurate given some parameter constness changes. Some might not even exist anymore. */
101     internalFuncs["D5tango4core10stacktrace10StackTrace20defaultAddrBacktraceFPS5tango4core10stacktrace10StackTrace12TraceContextPS5tango4core10stacktrace10StackTrace12TraceContextPkkPiZk"]=1;
102     internalFuncs["_D5tango4core10stacktrace10StackTrace20defaultAddrBacktraceFPS5tango4core10stacktrace10StackTrace12TraceContextPS5tango4core10stacktrace10StackTrace12TraceContextPmmPiZm"]=1;
103     internalFuncs["rt_addrBacktrace"]=1;
104     internalFuncs["D5tango4core10stacktrace10StackTrace14BasicTraceInfo5traceMFPS5tango4core10stacktrace10StackTrace12TraceContextiZv"]=1;
105     internalFuncs["D5tango4core10stacktrace10StackTrace11basicTracerFPvZC9Exception9TraceInfo"]=1;
106     internalFuncs["rt_createTraceContext"]=1;
107     internalFuncs["D2rt6dmain24mainUiPPaZi7runMainMFZv"]=1;
108     internalFuncs["D2rt6dmain24mainUiPPaZi6runAllMFZv"]=1;
109     internalFuncs["D2rt6dmain24mainUiPPaZi7tryExecMFDFZvZv"]=1;
110     internalFuncs["_D5tango4core10stacktrace10StackTrace20defaultAddrBacktraceFPS5tango4core10stacktrace10StackTrace12TraceContextPS5tango4core10stacktrace10StackTrace12TraceContextPkkPiZk"]=1;
111     internalFuncs["_rt_addrBacktrace"]=1;
112     internalFuncs["_D5tango4core10stacktrace10StackTrace14BasicTraceInfo5traceMFPS5tango4core10stacktrace10StackTrace12TraceContextiZv"]=1;
113     internalFuncs["_D5tango4core10stacktrace10StackTrace11basicTracerFPvZC9Exception9TraceInfo"]=1;
114     internalFuncs["_rt_createTraceContext"]=1;
115     internalFuncs["_D2rt8compiler3dmd2rt6dmain24mainUiPPaZi7runMainMFZv"]=1;
116     internalFuncs["_D2rt8compiler3dmd2rt6dmain24mainUiPPaZi6runAllMFZv"]=1;
117     internalFuncs["_D2rt8compiler3dmd2rt6dmain24mainUiPPaZi7tryExecMFDFZvZv"]=1;
118     internalFuncs["main"]=1;
119     // glib specific
120     internalFuncs["__libc_start_main"]=1;
121     // backtrace() gets always the backtrace at the point it were called, so
122     // ignore things we don't really want to see
123     internalFuncs["_D5tango4core5tools10StackTrace20defaultAddrBacktraceFPS5tango4core5tools10StackTrace12TraceContextPS5tango4core5tools10StackTrace12TraceContextPkkPiZk"]=1;
124     internalFuncs["_D5tango4core5tools10StackTrace14BasicTraceInfo5traceMFPS5tango4core5tools10StackTrace12TraceContextiZv"]=1;
125     internalFuncs["_D5tango4core5tools10StackTrace11basicTracerFPvZC9Exception9TraceInfo"]=1;
126     // assertion internals should not be shown to users
127     internalFuncs["onAssertError"]=1;
128     internalFuncs["_d_assert"]=1;
129     internalFuncs["onAssertErrorMsg"]=1;
130     internalFuncs["_d_assert_msg"]=1;
131     // ignore calls when called for uncaught exceptions
132     internalFuncs["_d_throwc"]=1;
133 }
134 
135 // function to determine if a name is an internal method
136 const(char[])[] internalMethodEnders = [
137     "8__assertFiZv",
138     "9__requireMFZv"
139 ];
140 bool isInternalMethod(const(char)[] name)
141 {
142     static bool endsWith(const(char)[] str, const(char)[] what)
143     {
144         if (str.length < what.length)
145             return false;
146         return str[$-what.length .. $] == what;
147     }
148     if (name[0..2] != "_D"){
149         return false;
150     }
151     foreach (end; internalMethodEnders){
152         if (endsWith(name, end)){
153             return true;
154         }
155     }
156     return false;
157 }
158 
159 /// returns the name of the function at the given adress (if possible)
160 /// function@ and then the address. For delegates you can use .funcptr
161 /// does not demangle
162 const(char)[] nameOfFunctionAt(void* addr, char[] buf){
163     FrameInfo fInfo;
164     fInfo.clear();
165     fInfo.address=cast(size_t)addr;
166     if (rt_symbolizeFrameInfo(fInfo,null,buf) && fInfo.func.length){
167         return fInfo.func;
168     } else {
169         return "function@"~ctfe_i2a(cast(size_t)addr);
170     }
171 }
172 /// ditto
173 const(char)[] nameOfFunctionAt(void * addr){
174     char[1024] buf;
175     return nameOfFunctionAt(addr,buf).dup;
176 }
177 
178 /// precision of the addresses given by the backtrace function
179 enum AddrPrecision{
180     AllReturn=0,
181     TopExact=1,
182     AllExact=3
183 }
184 
185 /// basic class that represents a stacktrace
186 class BasicTraceInfo: Throwable.TraceInfo{
187     size_t[] traceAddresses;
188     size_t[128] traceBuf;
189     AddrPrecision addrPrecision;
190     TraceContext context;
191     /// cretes an empty stacktrace
192     this(){}
193     /// creates a stacktrace with the given traceAddresses
194     this(size_t[] traceAddresses,AddrPrecision addrPrecision){
195         this.traceAddresses[]=traceAddresses[];
196         if (traceAddresses.length<=traceBuf.length){
197             // change to either always copy (and truncate) or never copy?
198             traceBuf[0..traceAddresses.length]=traceAddresses[];
199             this.traceAddresses=traceBuf[0..traceAddresses.length];
200         }
201         this.addrPrecision=addrPrecision;
202     }
203     /// takes a stacktrace
204     void trace(TraceContext *contextIn=null,int skipFrames=0){
205         int flags;
206         size_t nFrames=rt_addrBacktrace(contextIn,&context,traceBuf.ptr,traceBuf.length,&flags);
207         traceAddresses=traceBuf[skipFrames..nFrames];
208         addrPrecision=cast(AddrPrecision)flags;
209         if (flags==AddrPrecision.TopExact && skipFrames!=0)
210             addrPrecision=AddrPrecision.AllReturn;
211     }
212     /// loops on the stacktrace
213     override int opApply(scope int delegate(ref const(char[])) dg ) const
214     {
215         return opApply( (ref size_t, ref const(char[]) buf)
216                         {
217                             return dg( buf );
218                         } );
219     }
220 
221     override int opApply(scope int delegate(ref size_t line, ref const(char[]) func) loopBody) const
222     {
223             FrameInfo fInfo;
224             for (size_t iframe=0;iframe<traceAddresses.length;++iframe){
225                     char[2048] buf;
226                     char[1024] buf2;
227                     fInfo.clear();
228                     fInfo.address=cast(size_t)traceAddresses[iframe];
229                     fInfo.iframe=cast(ptrdiff_t)iframe;
230                     fInfo.exactAddress=(addrPrecision & 2) || (iframe==0 && (addrPrecision & 1));
231                     rt_symbolizeFrameInfo(fInfo,&context,buf);
232 
233                     auto r= fInfo.func in internalFuncs;
234                     fInfo.internalFunction |= (r !is null);
235                     fInfo.func = demangler.demangle(fInfo.func.dup,buf2);
236                     int res=loopBody(fInfo.iframe, fInfo.func);
237                     if (res) return res;
238             }
239             return 0;
240     }
241     int opApply(scope int delegate( ref FrameInfo fInfo ) loopBody) const {
242         FrameInfo fInfo;
243         for (size_t iframe=0;iframe<traceAddresses.length;++iframe){
244             char[2048] buf;
245             char[1024] buf2;
246             fInfo.clear();
247             fInfo.address=cast(size_t)traceAddresses[iframe];
248             fInfo.iframe=cast(ptrdiff_t)iframe;
249             fInfo.exactAddress=(addrPrecision & 2) || (iframe==0 && (addrPrecision & 1));
250             rt_symbolizeFrameInfo(fInfo,&context,buf);
251 
252             if (!fInfo.internalFunction){
253                 auto r= (fInfo.func in internalFuncs);
254                 fInfo.internalFunction = (r !is null);
255                 if (!fInfo.internalFunction){
256                     fInfo.internalFunction = isInternalMethod(fInfo.func);
257                 }
258             }
259             fInfo.func = demangler.demangle(fInfo.func,buf2);
260             int res=loopBody(fInfo);
261             if (res) return res;
262         }
263         return 0;
264     }
265 
266     /// Writes out the stacktrace.
267     void writeOut(scope void delegate(const(char[])) sink) const {
268         int ignored = 0;
269         foreach (ref FrameInfo fInfo; this){
270             if (!fInfo.internalFunction){
271                 fInfo.iframe -= ignored;
272                 fInfo.writeOut(sink);
273                 fInfo.iframe += ignored;
274                 sink("\n");
275             }
276             else ignored++;
277         }
278     }
279     
280     override immutable(char)[] toString() const
281     {
282         immutable(char)[] ret;
283         writeOut((str) { ret ~= str; });
284         return ret;
285     }
286 }
287 
288 version(linux){
289     version=LibCBacktrace;
290     version=DladdrSymbolification;
291     version=ElfSymbolification;
292 }
293 version(OSX){
294     version=LibCBacktrace;
295     version=DladdrSymbolification;
296 }
297 version(LibCBacktrace){
298     extern(C)int backtrace(void**,int);
299 }
300 
301 /// default (tango given) backtrace function
302 size_t defaultAddrBacktrace(TraceContext* context,TraceContext*contextOut,
303     size_t*traceBuf,size_t length,int*flags){
304     version(LibCBacktrace){
305         //if (context!is null) return 0; // now it just gives a local trace, uncomment & skip?
306         *flags=AddrPrecision.TopExact;
307         return cast(size_t)backtrace(cast(void**)traceBuf,cast(int)length);
308     } else version (Windows){
309         return winAddrBacktrace(context,contextOut,traceBuf,length,flags);
310     } else {
311         return 0;
312     }
313 }
314 
315 version(DladdrSymbolification){
316     extern(C) struct Dl_info {
317       char *dli_fname;      /* Filename of defining object */
318       void *dli_fbase;      /* Load address of that object */
319       char *dli_sname;      /* Name of nearest lower symbol */
320       void *dli_saddr;      /* Exact value of nearest symbol */
321     }
322 
323     extern(C)int dladdr(void* addr, Dl_info* info);
324 
325     /// poor symbolication, uses dladdr, gives no line info, limited info on statically linked files
326     bool dladdrSymbolizeFrameInfo(ref FrameInfo fInfo,const(TraceContext)*context,char[] buf){
327         Dl_info dli;
328         void *ip=cast(void*)(fInfo.address);
329         if (!fInfo.exactAddress) --ip;
330         if (dladdr(ip, &dli))
331         {
332             if (dli.dli_fname && dli.dli_fbase){
333                 fInfo.offsetImg = cast(ptrdiff_t)ip - cast(ptrdiff_t)dli.dli_fbase;
334                 fInfo.baseImg = cast(size_t)dli.dli_fbase;
335                 fInfo.file=dli.dli_fname[0..strlen(dli.dli_fname)];
336             }
337             if (dli.dli_sname && dli.dli_saddr){
338                 fInfo.offsetSymb = cast(ptrdiff_t)ip - cast(ptrdiff_t)dli.dli_saddr;
339                 fInfo.baseSymb = cast(size_t)dli.dli_saddr;
340                 fInfo.func = dli.dli_sname[0..strlen(dli.dli_sname)];
341             }
342         }
343         return true;
344     }
345 }
346 
347 version(ElfSymbolification){
348 version(TangoDoc)
349 {
350     bool elfSymbolizeFrameInfo(ref FrameInfo fInfo,
351         const(TraceContext)* context, char[] buf);
352 }
353 else
354 {
355     bool elfSymbolizeFrameInfo(ref FrameInfo fInfo,
356         const(TraceContext)* context, char[] buf)
357     {
358         Dl_info dli;
359         void *ip=cast(void*)(fInfo.address);
360         if (!fInfo.exactAddress) --ip;
361         if (dladdr(ip, &dli))
362         {
363             if (dli.dli_fname && dli.dli_fbase){
364                 fInfo.offsetImg = cast(ptrdiff_t)ip - cast(ptrdiff_t)dli.dli_fbase;
365                 fInfo.baseImg = cast(size_t)dli.dli_fbase;
366                 fInfo.file=dli.dli_fname[0..strlen(dli.dli_fname)];
367             }
368             if (dli.dli_sname && dli.dli_saddr){
369                 fInfo.offsetSymb = cast(ptrdiff_t)ip - cast(ptrdiff_t)dli.dli_saddr;
370                 fInfo.baseSymb = cast(size_t)dli.dli_saddr;
371                 fInfo.func = dli.dli_sname[0..strlen(dli.dli_sname)];
372             } else {
373                 // try static symbols
374                 foreach(symName,symAddr,symEnd,pub;StaticSectionInfo) {
375                     if (cast(size_t)ip>=symAddr && cast(size_t)ip<symEnd) {
376                         fInfo.offsetSymb = cast(ptrdiff_t)ip - cast(ptrdiff_t)symAddr;
377                         fInfo.baseSymb = cast(size_t)symAddr;
378                         fInfo.func = symName.dup;
379                         break;
380                     }
381                 }
382             }
383         }
384         StaticSectionInfo.resolveLineNumber(fInfo);
385         return true;
386     }
387 
388 }
389 }
390 
391 /// loads symbols for the given frame info with the methods defined in tango itself
392 bool defaultSymbolizeFrameInfo(ref FrameInfo fInfo,const(TraceContext) *context, char[] buf){
393     version(ElfSymbolification) {
394         return elfSymbolizeFrameInfo(fInfo,context,buf);
395     } else version(DladdrSymbolification){
396         return dladdrSymbolizeFrameInfo(fInfo,context,buf);
397     } else version(Windows) {
398         return winSymbolizeFrameInfo(fInfo,context,buf);
399     } else {
400         return false;
401     }
402 }
403 
404 /// function that generates a trace (handler compatible with old TraceInfo)
405 Exception.TraceInfo basicTracer( void* ptr = null ){
406     BasicTraceInfo res;
407     try{
408         version(CatchRecursiveTracing){
409             recursiveStackTraces.val=recursiveStackTraces.val+1;
410             scope(exit) recursiveStackTraces.val=recursiveStackTraces.val-1;
411             // printf("tracer %d\n",recursiveStackTraces.val);
412             if (recursiveStackTraces.val>10) {
413                 Runtime.console.stderr("hit maximum recursive tracing (tracer asserting...?)\n");
414                 abort();
415                 return null;
416             }
417         }
418         res=new BasicTraceInfo();
419         res.trace(cast(TraceContext*)ptr);
420     } catch (Throwable e){
421         Runtime.console.stderr("tracer got exception:\n");
422         Runtime.console.stderr(e.msg);
423         Runtime.console.stderr(e.toString());
424         Runtime.console.stderr("\n");
425     }
426     return res;
427 }
428 
429 // signal handling
430 version(Posix){
431     version(linux){
432         version(X86){
433             version = haveSegfaultTrace;
434         }else version(X86_64){
435             version = haveSegfaultTrace;
436         }
437     }
438 
439     extern(C) void tango_stacktrace_fault_handler (int sn, siginfo_t * si, void *ctx){
440         Runtime.console.stderr(fromStringz(strsignal(sn)));
441         Runtime.console.stderr(" encountered at:\n");
442         ucontext_t * context = cast(ucontext_t *) ctx;
443         version(haveSegfaultTrace){
444             void* stack;
445             void* code;
446             version(X86){
447                 code = cast(void*) context.uc_mcontext.gregs[14];
448             }else version(X86_64){
449                 code = cast(void*) context.uc_mcontext.gregs[0x10];
450             }else{
451                 static assert(0);
452             }
453 
454             FrameInfo fInfo;
455             char[1024] buf1,buf2;
456             fInfo.clear();
457             fInfo.address=cast(size_t)code;
458             rt_symbolizeFrameInfo(fInfo,null,buf1);
459             fInfo.func = demangler.demangle(fInfo.func,buf2);
460             fInfo.writeOut((in char[] s) { Runtime.console.stderr(s); });
461         }
462         Runtime.console.stderr("\n Stacktrace:\n");
463         TraceContext tc;
464         tc.hasContext=ctx is null;
465         if (tc.hasContext) tc.context=*(cast(ucontext_t*)ctx);
466         Exception.TraceInfo info=basicTracer(&tc);
467 
468         info.opApply((ref const(char[]) s) { Runtime.console.stderr(s~"\n"); return 0;});
469 
470         Runtime.console.stderr("Stacktrace signal handler abort().\n");
471         abort();
472     }
473 
474     __gshared sigaction_t fault_action;
475 
476     void setupSegfaultTracer(){
477         //use an alternative stack; this is useful when infinite recursion
478         //  triggers a SIGSEGV
479         void* altstack = malloc(SIGSTKSZ);
480         if (altstack) {
481             stack_t stack;
482             stack.ss_sp = altstack;
483             stack.ss_size = SIGSTKSZ;
484             sigaltstack(&stack, null);
485         }
486         fault_action.sa_handler = cast(typeof(fault_action.sa_handler)) &tango_stacktrace_fault_handler;
487         sigemptyset(&fault_action.sa_mask);
488         fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
489         foreach (sig;[SIGSEGV,SIGFPE,SIGILL,SIGBUS,SIGINT]){
490             sigaction(sig, &fault_action, null);
491         }
492     }
493 
494     version(noSegfaultTrace){
495     } else {
496         shared static this(){
497             setupSegfaultTracer();
498         }
499     }
500 }else version(Windows){
501 }else {
502     pragma(msg, "[INFO] SEGFAULT trace not yet implemented for this OS");
503 }