| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401 | /** * Stacktracing * * Functions to generate a stacktrace * * Copyright: 2009 Fawzi * License: tango license * Authors: Fawzi Mohamed */ module tango.core.tools.StackTrace; import tango.core.tools.Demangler; import tango.core.Thread; import tango.core.Traits: ctfe_i2a; import tango.stdc.string; import tango.stdc.stdio:printf,fprintf,stderr,fflush; import tango.stdc.stdlib: abort; version(Windows){ import tango.core.tools.WinStackTrace; } else { import tango.stdc.posix.ucontext; import tango.stdc.posix.sys.types: pid_t,pthread_t; import tango.stdc.signal; } version(linux){ import tango.core.tools.LinuxStackTrace; } version(CatchRecursiveTracing){ ThreadLocal!(int) recursiveStackTraces; static this(){ recursiveStackTraces=new ThreadLocal!(int)(0); } } version(Windows){ } else { struct TraceContext{ bool hasContext; ucontext_t context; pid_t hProcess; pthread_t hThread; } } alias size_t function(TraceContext* context,TraceContext* contextOut,size_t*traceBuf,size_t bufLength,int *flags) AddrBacktraceFunc; AddrBacktraceFunc addrBacktraceFnc; alias bool function(ref Exception.FrameInfo fInfo,TraceContext* context,char[]buf) SymbolizeFrameInfoFnc; SymbolizeFrameInfoFnc symbolizeFrameInfoFnc; static this(){ addrBacktraceFnc=&defaultAddrBacktrace; symbolizeFrameInfoFnc=&defaultSymbolizeFrameInfo; } /// sets the function used for address stacktraces extern(C) void rt_setAddrBacktraceFnc(AddrBacktraceFunc f){ addrBacktraceFnc=f; } /// sets the function used to symbolize a FrameInfo extern(C) void rt_setSymbolizeFrameInfoFnc(SymbolizeFrameInfoFnc f){ symbolizeFrameInfoFnc=f; } /// creates a stack trace (defined in the runtime) extern(C) Exception.TraceInfo rt_createTraceContext( void* ptr ); alias Exception.TraceInfo function( void* ptr = null ) TraceHandler; /// builds a backtrace of addresses, the addresses are addresses of the *next* instruction, /// *return* addresses, the most likely the calling instruction is the one before them /// (stack top excluded) extern(C) size_t rt_addrBacktrace(TraceContext* context, TraceContext *contextOut,size_t*traceBuf,size_t bufLength,int *flags){ if (addrBacktraceFnc !is null){ return addrBacktraceFnc(context,contextOut,traceBuf,bufLength,flags); } else { return 0; } } /// tries to sybolize a frame information, this should try to build the best /// backtrace information, if possible finding the calling context, thus /// if fInfo.exactAddress is false the address might be changed to the one preceding it /// returns true if it managed to at least find the function name extern(C) bool rt_symbolizeFrameInfo(ref Exception.FrameInfo fInfo,TraceContext* context,char[]buf){ if (symbolizeFrameInfoFnc !is null){ return symbolizeFrameInfoFnc(fInfo,context,buf); } else { return false; } } // names of the functions that should be ignored for the backtrace int[char[]] internalFuncs; static this(){ internalFuncs["D5tango4core10stacktrace10StackTrace20defaultAddrBacktraceFPS5tango4core10stacktrace10StackTrace12TraceContextPS5tango4core10stacktrace10StackTrace12TraceContextPkkPiZk"]=1; internalFuncs["_D5tango4core10stacktrace10StackTrace20defaultAddrBacktraceFPS5tango4core10stacktrace10StackTrace12TraceContextPS5tango4core10stacktrace10StackTrace12TraceContextPmmPiZm"]=1; internalFuncs["rt_addrBacktrace"]=1; internalFuncs["D5tango4core10stacktrace10StackTrace14BasicTraceInfo5traceMFPS5tango4core10stacktrace10StackTrace12TraceContextiZv"]=1; internalFuncs["D5tango4core10stacktrace10StackTrace11basicTracerFPvZC9Exception9TraceInfo"]=1; internalFuncs["rt_createTraceContext"]=1; internalFuncs["D2rt6dmain24mainUiPPaZi7runMainMFZv"]=1; internalFuncs["D2rt6dmain24mainUiPPaZi6runAllMFZv"]=1; internalFuncs["D2rt6dmain24mainUiPPaZi7tryExecMFDFZvZv"]=1; internalFuncs["_D5tango4core10stacktrace10StackTrace20defaultAddrBacktraceFPS5tango4core10stacktrace10StackTrace12TraceContextPS5tango4core10stacktrace10StackTrace12TraceContextPkkPiZk"]=1; internalFuncs["_rt_addrBacktrace"]=1; internalFuncs["_D5tango4core10stacktrace10StackTrace14BasicTraceInfo5traceMFPS5tango4core10stacktrace10StackTrace12TraceContextiZv"]=1; internalFuncs["_D5tango4core10stacktrace10StackTrace11basicTracerFPvZC9Exception9TraceInfo"]=1; internalFuncs["_rt_createTraceContext"]=1; internalFuncs["_D2rt6dmain24mainUiPPaZi7runMainMFZv"]=1; internalFuncs["_D2rt6dmain24mainUiPPaZi6runAllMFZv"]=1; internalFuncs["_D2rt6dmain24mainUiPPaZi7tryExecMFDFZvZv"]=1; } /// returns the name of the function at the given adress (if possible) /// function@ and then the address. For delegates you can use .funcptr /// does not demangle char[] nameOfFunctionAt(void* addr, char[] buf){ Exception.FrameInfo fInfo; fInfo.clear(); fInfo.address=cast(size_t)addr; if (rt_symbolizeFrameInfo(fInfo,null,buf) && fInfo.func.length){ return fInfo.func; } else { return "function@"~ctfe_i2a(cast(size_t)addr); } } /// ditto char[] nameOfFunctionAt(void * addr){ char[1024] buf; return nameOfFunctionAt(addr,buf).dup; } /// precision of the addresses given by the backtrace function enum AddrPrecision{ AllReturn=0, TopExact=1, AllExact=3 } /// basic class that represents a stacktrace class BasicTraceInfo: Exception.TraceInfo{ size_t[] traceAddresses; size_t[128] traceBuf; AddrPrecision addrPrecision; TraceContext context; /// cretes an empty stacktrace this(){} /// creates a stacktrace with the given traceAddresses this(size_t[] traceAddresses,AddrPrecision addrPrecision){ this.traceAddresses[]=traceAddresses; if (traceAddresses.length<=traceBuf.length){ // change to either always copy (and truncate) or never copy? traceBuf[0..traceAddresses.length]=traceAddresses; this.traceAddresses=traceBuf[0..traceAddresses.length]; } this.addrPrecision=addrPrecision; } /// takes a stacktrace void trace(TraceContext *contextIn=null,int skipFrames=0){ int flags; size_t nFrames=rt_addrBacktrace(contextIn,&context,traceBuf.ptr,traceBuf.length,&flags); traceAddresses=traceBuf[skipFrames..nFrames]; addrPrecision=cast(AddrPrecision)flags; if (flags==AddrPrecision.TopExact && skipFrames!=0) addrPrecision=AddrPrecision.AllReturn; } /// loops on the stacktrace int opApply( int delegate( ref Exception.FrameInfo fInfo ) loopBody){ Exception.FrameInfo fInfo; for (size_t iframe=0;iframe<traceAddresses.length;++iframe){ char[2048] buf; char[1024] buf2; fInfo.clear(); fInfo.address=cast(size_t)traceAddresses[iframe]; fInfo.iframe=cast(ptrdiff_t)iframe; fInfo.exactAddress=(addrPrecision & 2) || (iframe==0 && (addrPrecision & 1)); rt_symbolizeFrameInfo(fInfo,&context,buf); auto r= fInfo.func in internalFuncs; fInfo.internalFunction |= (r !is null); fInfo.func = demangler.demangle(fInfo.func,buf2); int res=loopBody(fInfo); if (res) return res; } return 0; } /// writes out the stacktrace void writeOut(void delegate(char[]) sink){ foreach (ref fInfo; this){ if (!fInfo.internalFunction){ fInfo.writeOut(sink); sink("\n"); } } } } version(linux){ version=LibCBacktrace; version=DladdrSymbolification; version=ElfSymbolification; } version(darwin){ version=LibCBacktrace; version=DladdrSymbolification; } version(LibCBacktrace){ extern(C)int backtrace(void**,int); } /// default (tango given) backtrace function size_t defaultAddrBacktrace(TraceContext* context,TraceContext*contextOut, size_t*traceBuf,size_t length,int*flags){ version(LibCBacktrace){ //if (context!is null) return 0; // now it just gives a local trace, uncomment & skip? *flags=AddrPrecision.TopExact; return cast(size_t)backtrace(cast(void**)traceBuf,length); } else version (Windows){ return winAddrBacktrace(context,contextOut,traceBuf,length,flags); } else { return 0; } } version(DladdrSymbolification){ extern(C) struct Dl_info { char *dli_fname; /* Filename of defining object */ void *dli_fbase; /* Load address of that object */ char *dli_sname; /* Name of nearest lower symbol */ void *dli_saddr; /* Exact value of nearest symbol */ } extern(C)int dladdr(void* addr, Dl_info* info); /// poor symbolication, uses dladdr, gives no line info, limited info on statically linked files bool dladdrSymbolizeFrameInfo(ref Exception.FrameInfo fInfo,TraceContext*context,char[]buf){ Dl_info dli; void *ip=cast(void*)(fInfo.address); if (!fInfo.exactAddress) --ip; if (dladdr(ip, &dli)) { if (dli.dli_fname && dli.dli_fbase){ fInfo.offsetImg = cast(ptrdiff_t)ip - cast(ptrdiff_t)dli.dli_fbase; fInfo.baseImg = cast(size_t)dli.dli_fbase; fInfo.file=dli.dli_fname[0..strlen(dli.dli_fname)]; } if (dli.dli_sname && dli.dli_saddr){ fInfo.offsetSymb = cast(ptrdiff_t)ip - cast(ptrdiff_t)dli.dli_saddr; fInfo.baseSymb = cast(size_t)dli.dli_saddr; fInfo.func = dli.dli_sname[0..strlen(dli.dli_sname)]; } } return true; } } version(ElfSymbolification){ bool elfSymbolizeFrameInfo(ref Exception.FrameInfo fInfo, TraceContext* context, char[] buf) { Dl_info dli; void *ip=cast(void*)(fInfo.address); if (!fInfo.exactAddress) --ip; if (dladdr(ip, &dli)) { if (dli.dli_fname && dli.dli_fbase){ fInfo.offsetImg = cast(ptrdiff_t)ip - cast(ptrdiff_t)dli.dli_fbase; fInfo.baseImg = cast(size_t)dli.dli_fbase; fInfo.file=dli.dli_fname[0..strlen(dli.dli_fname)]; } if (dli.dli_sname && dli.dli_saddr){ fInfo.offsetSymb = cast(ptrdiff_t)ip - cast(ptrdiff_t)dli.dli_saddr; fInfo.baseSymb = cast(size_t)dli.dli_saddr; fInfo.func = dli.dli_sname[0..strlen(dli.dli_sname)]; } else { // try static symbols foreach(symName,symAddr,symEnd,pub;StaticSectionInfo) { if (cast(size_t)ip>=symAddr && cast(size_t)ip<symEnd) { fInfo.offsetSymb = cast(ptrdiff_t)ip - cast(ptrdiff_t)symAddr; fInfo.baseSymb = cast(size_t)symAddr; fInfo.func = symName; return true; } } } } return true; } } /// loads symbols for the given frame info with the methods defined in tango itself bool defaultSymbolizeFrameInfo(ref Exception.FrameInfo fInfo,TraceContext *context,char[]buf){ version(ElfSymbolification) { return elfSymbolizeFrameInfo(fInfo,context,buf); } else version(DladdrSymbolification){ return dladdrSymbolizeFrameInfo(fInfo,context,buf); } else version(Windows) { return winSymbolizeFrameInfo(fInfo,context,buf); } else { return false; } } /// function that generates a trace (handler compatible with old TraceInfo) Exception.TraceInfo basicTracer( void* ptr = null ){ BasicTraceInfo res; try{ version(CatchRecursiveTracing){ recursiveStackTraces.val=recursiveStackTraces.val+1; scope(exit) recursiveStackTraces.val=recursiveStackTraces.val-1; // printf("tracer %d\n",recursiveStackTraces.val); if (recursiveStackTraces.val>10) { printf("hit maximum recursive tracing (tracer asserting...?)\n"); abort(); return null; } } res=new BasicTraceInfo(); res.trace(cast(TraceContext*)ptr); } catch (Exception e){ printf("tracer got exception:\n"); printf((e.msg~"\n\0").ptr); e.writeOut((char[]s){ printf((s~"\0").ptr); }); printf("\n"); } catch (Object o){ printf("tracer got object exception:\n"); printf((o.toString~"\n\0").ptr); } return res; } // signal handling version(Posix){ version(linux){ version(X86){ version = haveSegfaultTrace; }else version(X86_64){ version = haveSegfaultTrace; } } extern(C) void tango_stacktrace_fault_handler (int sn, siginfo_t * si, void *ctx){ fprintf(stderr, "%s encountered at:\n", strsignal(sn)); fflush(stderr); ucontext_t * context = cast(ucontext_t *) ctx; version(haveSegfaultTrace){ void* stack; void* code; version(X86){ code = cast(void*) context.uc_mcontext.gregs[14]; }else version(X86_64){ code = cast(void*) context.uc_mcontext.gregs[0x10]; }else{ static assert(0); } Exception.FrameInfo fInfo; char[1024] buf1,buf2; fInfo.clear(); fInfo.address=cast(size_t)code; rt_symbolizeFrameInfo(fInfo,null,buf1); fInfo.func = demangler.demangle(fInfo.func,buf2); fInfo.writeOut((char[] s) { fprintf(stderr, "%.*s", s.length,s.ptr); }); fflush(stderr); } fprintf(stderr, "\n Stacktrace:\n"); TraceContext tc; tc.hasContext=ctx is null; if (tc.hasContext) tc.context=*(cast(ucontext_t*)ctx); Exception.TraceInfo info=basicTracer(&tc); info.writeOut((char[] s) { fprintf(stderr, "%.*s", s.length,s.ptr); fflush(stderr); }); fprintf(stderr, "Stacktrace signal handler abort().\n"); abort(); } sigaction_t fault_action; void setupSegfaultTracer(){ fault_action.sa_handler = cast(typeof(fault_action.sa_handler)) &tango_stacktrace_fault_handler; sigemptyset(&fault_action.sa_mask); fault_action.sa_flags = SA_SIGINFO; foreach (sig;[SIGSEGV,SIGFPE,SIGILL,SIGBUS,SIGKILL,SIGINT]){ sigaction(sig, &fault_action, null); } } version(noSegfaultTrace){ } else { static this(){ setupSegfaultTracer(); } } }else version(Windows){ }else { pragma(msg, "[INFO] SEGFAULT trace not yet implemented for this OS"); } |