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 }