| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504 | /** * Stacktracing * * Functions to generate a stacktrace. * * Copyright: Copyright (C) 2009 Fawzi * License: Tango License * Author: Fawzi Mohamed */ module tango.core.tools.StackTrace; import tango.core.tools.Demangler; import tango.core.Runtime; import tango.core.Thread; import tango.core.Traits: ctfe_i2a; import tango.stdc.string; import tango.stdc.stringz : fromStringz; import tango.stdc.stdlib: abort; import tango.core.tools.FrameInfo; 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; import tango.stdc.stdlib; } version(linux){ import tango.core.tools.LinuxStackTrace; } version(CatchRecursiveTracing){ __gshared ThreadLocal!(int) recursiveStackTraces; shared 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; __gshared AddrBacktraceFunc addrBacktraceFnc; alias bool function(ref FrameInfo fInfo,const(TraceContext)* context,char[] buf) SymbolizeFrameInfoFnc; __gshared SymbolizeFrameInfoFnc symbolizeFrameInfoFnc; shared 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 FrameInfo fInfo,const(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 __gshared int[const(char)[]] internalFuncs; shared static this(){ /* TODO these probably are inaccurate given some parameter constness changes. Some might not even exist anymore. */ 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["_D2rt8compiler3dmd2rt6dmain24mainUiPPaZi7runMainMFZv"]=1; internalFuncs["_D2rt8compiler3dmd2rt6dmain24mainUiPPaZi6runAllMFZv"]=1; internalFuncs["_D2rt8compiler3dmd2rt6dmain24mainUiPPaZi7tryExecMFDFZvZv"]=1; internalFuncs["main"]=1; // glib specific internalFuncs["__libc_start_main"]=1; // backtrace() gets always the backtrace at the point it were called, so // ignore things we don't really want to see internalFuncs["_D5tango4core5tools10StackTrace20defaultAddrBacktraceFPS5tango4core5tools10StackTrace12TraceContextPS5tango4core5tools10StackTrace12TraceContextPkkPiZk"]=1; internalFuncs["_D5tango4core5tools10StackTrace14BasicTraceInfo5traceMFPS5tango4core5tools10StackTrace12TraceContextiZv"]=1; internalFuncs["_D5tango4core5tools10StackTrace11basicTracerFPvZC9Exception9TraceInfo"]=1; // assertion internals should not be shown to users internalFuncs["onAssertError"]=1; internalFuncs["_d_assert"]=1; internalFuncs["onAssertErrorMsg"]=1; internalFuncs["_d_assert_msg"]=1; // ignore calls when called for uncaught exceptions internalFuncs["_d_throwc"]=1; } // function to determine if a name is an internal method const(char[])[] internalMethodEnders = [ "8__assertFiZv", "9__requireMFZv" ]; bool isInternalMethod(const(char)[] name) { static bool endsWith(const(char)[] str, const(char)[] what) { if (str.length < what.length) return false; return str[$-what.length .. $] == what; } if (name[0..2] != "_D"){ return false; } foreach (end; internalMethodEnders){ if (endsWith(name, end)){ return true; } } return false; } /// 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 const(char)[] nameOfFunctionAt(void* addr, char[] buf){ 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 const(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: Throwable.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 override int opApply(scope int delegate(ref const(char[])) dg ) const { return opApply( (ref size_t, ref const(char[]) buf) { return dg( buf ); } ); } override int opApply(scope int delegate(ref size_t line, ref const(char[]) func) loopBody) const { 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.dup,buf2); int res=loopBody(fInfo.iframe, fInfo.func); if (res) return res; } return 0; } int opApply(scope int delegate( ref FrameInfo fInfo ) loopBody) const { 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); if (!fInfo.internalFunction){ auto r= (fInfo.func in internalFuncs); fInfo.internalFunction = (r !is null); if (!fInfo.internalFunction){ fInfo.internalFunction = isInternalMethod(fInfo.func); } } fInfo.func = demangler.demangle(fInfo.func,buf2); int res=loopBody(fInfo); if (res) return res; } return 0; } /// Writes out the stacktrace. void writeOut(scope void delegate(const(char[])) sink) const { int ignored = 0; foreach (ref FrameInfo fInfo; this){ if (!fInfo.internalFunction){ fInfo.iframe -= ignored; fInfo.writeOut(sink); fInfo.iframe += ignored; sink("\n"); } else ignored++; } } override immutable(char)[] toString() { immutable(char)[] ret; writeOut((str) { ret ~= str; }); return ret; } } 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,cast(int)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 FrameInfo fInfo,const(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){ version(TangoDoc) { bool elfSymbolizeFrameInfo(ref FrameInfo fInfo, const(TraceContext)* context, char[] buf); } else { bool elfSymbolizeFrameInfo(ref FrameInfo fInfo, const(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.dup; break; } } } } StaticSectionInfo.resolveLineNumber(fInfo); return true; } } } /// loads symbols for the given frame info with the methods defined in tango itself bool defaultSymbolizeFrameInfo(ref FrameInfo fInfo,const(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) { Runtime.console.stderr("hit maximum recursive tracing (tracer asserting...?)\n"); abort(); return null; } } res=new BasicTraceInfo(); res.trace(cast(TraceContext*)ptr); } catch (Throwable e){ Runtime.console.stderr("tracer got exception:\n"); Runtime.console.stderr(e.msg); Runtime.console.stderr(e.toString()); Runtime.console.stderr("\n"); } 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){ Runtime.console.stderr(fromStringz(strsignal(sn))); Runtime.console.stderr(" encountered at:\n"); 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); } 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((in char[] s) { Runtime.console.stderr(s); }); } Runtime.console.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.opApply((ref const(char[]) s) { Runtime.console.stderr(s~"\n"); return 0;}); Runtime.console.stderr("Stacktrace signal handler abort().\n"); abort(); } __gshared sigaction_t fault_action; void setupSegfaultTracer(){ //use an alternative stack; this is useful when infinite recursion // triggers a SIGSEGV void* altstack = malloc(SIGSTKSZ); if (altstack) { stack_t stack; stack.ss_sp = altstack; stack.ss_size = SIGSTKSZ; sigaltstack(&stack, null); } 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 | SA_ONSTACK; foreach (sig;[SIGSEGV,SIGFPE,SIGILL,SIGBUS,SIGINT]){ sigaction(sig, &fault_action, null); } } version(noSegfaultTrace){ } else { shared static this(){ setupSegfaultTracer(); } } }else version(Windows){ }else { pragma(msg, "[INFO] SEGFAULT trace not yet implemented for this OS"); } |