Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

StackTrace.cc

Go to the documentation of this file.
00001 #if !defined(__APPLE__) && !defined(_GNU_SOURCE)
00002 #  define _GNU_SOURCE // enable dladdr and getline
00003 #endif
00004 
00005 #include "StackTrace.h"
00006 #include <unistd.h>
00007 
00008 #if defined(__APPLE__) // need atos
00009 #  if defined(STACKTRACE_USE_BACKTRACE)
00010 #    include <execinfo.h> // to record the backtrace addresses
00011 #  endif
00012 // for display:
00013 #  include </usr/include/util.h> // forkpty to make a pseudo-terminal for atos
00014 #  include <termios.h> // set pseudo-terminal to raw mode
00015 #  include <sys/select.h> // test whether atos has responded yet
00016 
00017 #elif defined(STACKTRACE_USE_BACKTRACE)
00018 #  include <execinfo.h> // to record the backtrace addresses
00019 // for display:
00020 #  include <dlfcn.h> // dladdr()
00021 #  include <sys/param.h> // realpath()
00022 #  include <errno.h>
00023 #  if defined(__GNUC__) && defined(__cplusplus)
00024 #    include <cxxabi.h> // demangling
00025 #    include <string>
00026 #  endif
00027 #  if defined(__linux__)
00028 #    include <sys/stat.h>
00029 #  endif
00030 #endif
00031 
00032 #ifdef __cplusplus
00033 #  include <cstdio>
00034 #  include <cstdlib>
00035 #  include <cstring>
00036 #else
00037 #  include <stdio.h>
00038 #  include <stdlib.h>
00039 #  include <string.h>
00040 #endif
00041 
00042 #ifdef ST_UNUSED 
00043 #elif defined(__GNUC__) && __GNUC__>3
00044 //! portable access to compiler hint not to warn if a function argument is ignored (goes in argument list)
00045 # define ST_UNUSED(x) UNUSED_##x __attribute__((unused)) 
00046 //! portable access to compiler hint not to warn if a function argument is ignored (goes at beginning of function body)
00047 # define ST_BODY_UNUSED(x) /*no body necessary*/
00048 
00049 #elif defined(__LCLINT__) 
00050 //! portable access to compiler hint not to warn if a function argument is ignored (goes in argument list)
00051 # define ST_UNUSED(x) /*@unused@*/ x 
00052 //! portable access to compiler hint not to warn if a function argument is ignored (goes at beginning of function body)
00053 # define ST_BODY_UNUSED(x) /*no body necessary*/
00054 
00055 #else 
00056 //! portable access to compiler hint not to warn if a function argument is ignored (goes in argument list)
00057 # define ST_UNUSED(x) UNUSED_##x 
00058 //! portable access to compiler hint not to warn if a function argument is ignored (goes at beginning of function body)
00059 # define ST_BODY_UNUSED(x) (void)UNUSED_##x /* ugly hack to avoid warning */
00060 #endif
00061 
00062 #ifdef __cplusplus
00063 namespace stacktrace {
00064 #endif /* __cplusplus */
00065 
00066 int unrollStackFrame(struct StackFrame* curFrame, struct StackFrame* nextFrame) {
00067   if(curFrame==NULL)
00068     return 0;
00069   curFrame->caller=NULL;
00070   if(nextFrame==NULL)
00071     return 0;
00072   
00073 
00074 #ifdef STACKTRACE_USE_BACKTRACE
00075   
00076   if(curFrame->packedRA==NULL)
00077     return 0; // don't have current frame
00078   if(*curFrame->packedRAUsed<=curFrame->depth+1) {
00079     // last element
00080     if(curFrame!=nextFrame) {
00081       nextFrame->packedRA = NULL;
00082       nextFrame->packedRAUsed = nextFrame->packedRACap = NULL;
00083     }
00084   return 0;
00085   }
00086   if(curFrame!=nextFrame) {
00087     nextFrame->packedRA = curFrame->packedRA;
00088     nextFrame->packedRAUsed = curFrame->packedRAUsed;
00089     nextFrame->packedRACap = curFrame->packedRACap;
00090     nextFrame->depth = curFrame->depth; // will be incremented below
00091   }
00092   nextFrame->ra = (*nextFrame->packedRA)[++(nextFrame->depth)];
00093   curFrame->caller=nextFrame;
00094   return 1;
00095   
00096 #else
00097 
00098   void* nsp=NULL;
00099   machineInstruction * nra=NULL;
00100 
00101 #if defined(__i386__) || defined(__x86_64__) || defined(__amd64__)
00102   if(curFrame->sp==NULL)
00103     return 0;
00104   if(((void**)curFrame->sp)-1==NULL)
00105     return 0;
00106   nsp=((void***)curFrame->sp)[-1];
00107   if(nsp==NULL)
00108     return 0;
00109   nsp=(void**)nsp+1; //move from frame pointer to stack pointer of previous frame
00110   nra=*((machineInstruction**)curFrame->sp);
00111   if(nsp<=curFrame->sp) {
00112     fprintf(stderr,"stacktrace::unrollStackFrame(sp=%p,ra=%p) directed to invalid next frame: (sp=%p,ra=%p)\n",curFrame->sp,curFrame->ra,nsp,nra);
00113     return 0;
00114   }
00115 #  ifdef DEBUG_STACKTRACE
00116   if(curFrame->debug)
00117     printf("( %p %p ) -> { %p %p }\n",curFrame->sp,curFrame->ra,nsp,nra);
00118   nextFrame->debug=curFrame->debug;
00119 #  endif
00120   nextFrame->sp=nsp;
00121   nextFrame->ra=nra;
00122   curFrame->caller=nextFrame;
00123   return 1;
00124 #endif
00125 #ifdef __POWERPC__
00126   if(curFrame->sp==NULL)
00127     return 0;
00128   if(*(void**)curFrame->sp==NULL)
00129     return 0;
00130   nsp=*(void**)curFrame->sp;
00131   nra=((machineInstruction**)nsp)[2];
00132   if(nsp<=curFrame->sp) {
00133     fprintf(stderr,"stacktrace::unrollStackFrame(sp=%p,ra=%p) directed to invalid next frame: (sp=%p,ra=%p)\n",curFrame->sp,curFrame->ra,nsp,nra);
00134     return 0;
00135   }
00136 #  ifdef DEBUG_STACKTRACE
00137   if(curFrame->debug)
00138     printf("( %p %p ) -> { %p %p }\n",curFrame->sp,curFrame->ra,nsp,nra);
00139   nextFrame->debug=curFrame->debug;
00140 #  endif
00141   nextFrame->sp=nsp;
00142   nextFrame->ra=nra;
00143   curFrame->caller=nextFrame;
00144   return 1;
00145 #endif
00146 #if defined(__MIPSEL__) || defined(__MIPS__) /* we're running on PLATFORM_APERIOS */
00147   if(curFrame->sp==NULL)
00148     return 0;
00149   /* Have to scan through intructions being executed because stack pointer is not stored directly on the stack */
00150   machineInstruction * ins;
00151   const machineInstruction * INS_BASE=(const machineInstruction *)0x2000; // lowest valid memory address?
00152   
00153 #ifdef __PIC__
00154   ins = reinterpret_cast<machineInstruction*>(curFrame->gp-curFrame->ra);
00155 #else
00156   ins = curFrame->ra;
00157 #endif
00158   // find previous return address
00159   for(; ins>=INS_BASE; ins--) {
00160     // gcc will always save the return address with the instruction
00161     //     sw ra, offset(sp)
00162     // 
00163     // the high word in this case is sw sp ra
00164     if ( ( *ins & 0xffff0000 ) == 0xafbf0000 )
00165     {
00166       // the low word is the offset from sp
00167       int offset = *ins & 0x000ffff;
00168       
00169       // in case things went horribly awry, don't deref the non-aligned ptr
00170       if (offset & 0x3)
00171         return 0;
00172       
00173       nra = *reinterpret_cast<machineInstruction**>((char*)curFrame->sp + offset);
00174       break; // now search for stack pointer
00175     }
00176     
00177     //it appears the aperios stub entry functions always begin with "ori  t0,ra,0x0"
00178     //if we hit one of these, return 0, because we can't unroll any more
00179     //(or at least, I don't know how it returns from these... there's no return statements!)
00180     if ( *ins  == 0x37e80000 ) {
00181 #  ifdef DEBUG_STACKTRACE
00182       if(curFrame->debug)
00183         printf("( %p %p %p ) -> { kernel? }\n",curFrame->sp,curFrame->ra,curFrame->gp);
00184 #  endif
00185       return 0;
00186     }
00187   }
00188   // find previous stack pointer
00189   for(; ins>=INS_BASE; ins--) {
00190     // gcc will always change the stack frame with the instruction
00191     //     addiu sp,sp,offset
00192     //
00193     // at the beginning of the function the offset will be negative since the stack grows 
00194     // from high to low addresses
00195     //
00196     // first check the high word which will be instruction + regs in this case (I-type)
00197     if ( ( *ins & 0xffff0000 ) == 0x27bd0000 ) {
00198       // the offset is in the low word. since we're finding occurrence at the start of the function,
00199       // it will be negative (increase stack size), so sign extend it
00200       int offset = ( *ins & 0x0000ffff ) | 0xffff0000;
00201 
00202       // in case things went horribly awry, don't deref the non-aligned ptr
00203       if (offset & 0x3)
00204         return 0;
00205       
00206       nsp = (char*)curFrame->sp - offset;
00207       break;
00208     }
00209   }
00210   
00211   
00212   if(ins>=INS_BASE) {
00213     if(nsp<=curFrame->sp) {
00214 #ifdef __PIC__
00215       fprintf(stderr,"stacktrace::unrollStackFrame(sp=%p,ra=%p,gp=%p) directed to invalid next frame: (sp=%p,ra=%p,gp=%p)\n",curFrame->sp,(void*)curFrame->ra,(void*)curFrame->gp,nsp,nra,(void*)(reinterpret_cast<size_t*>(nsp)[4]));
00216 #else
00217       fprintf(stderr,"stacktrace::unrollStackFrame(sp=%p,ra=%p) directed to invalid next frame: (sp=%p,ra=%p)\n",curFrame->sp,(void*)curFrame->ra,nsp,nra);
00218 #endif
00219       return 0;
00220     }
00221 
00222 #ifdef __PIC__
00223 #  ifdef DEBUG_STACKTRACE
00224     if(curFrame->debug)
00225       printf("( %p %p %p ) -> { %p %p %p }\n",curFrame->sp,curFrame->ra,curFrame->gp,nsp,nra,reinterpret_cast<size_t*>(nsp)[4]);
00226     nextFrame->debug=curFrame->debug;
00227 #  endif
00228     // I'm not actually sure this is a valid stop criteria, but in testing,
00229     // after this it seems to cross into some kind of kernel code.
00230     // (We get a really low gp (0x106), although a fairly normal nra, and then go bouncing
00231     // around in memory until we hit sp=0x80808080, ra=0x2700, which seems to be the 'real' last frame)
00232     //if(reinterpret_cast<size_t>(nra)>reinterpret_cast<size_t*>(nsp)[4])
00233     //return 0;
00234     //instead of this however, now we check for the ori t0,ra,0 statement, and reuse previous gp below
00235     
00236     nextFrame->sp=nsp;
00237     //not sure how valid this is either:
00238     if(reinterpret_cast<size_t>(nra)>reinterpret_cast<size_t*>(nsp)[4]) {
00239       nextFrame->gp = curFrame->gp;
00240     } else {
00241       nextFrame->gp = reinterpret_cast<size_t*>(nsp)[4]; // gp is stored 4 words from stack pointer
00242     }
00243     nextFrame->ra = nextFrame->gp-reinterpret_cast<size_t>(nra);
00244 #else
00245 #  ifdef DEBUG_STACKTRACE
00246     if(curFrame->debug)
00247       printf("( %p %p ) -> { %p %p }\n",curFrame->sp,curFrame->ra,nsp,nra);
00248     nextFrame->debug=curFrame->debug;
00249 #  endif
00250     nextFrame->sp=nsp;
00251     nextFrame->ra=nra;
00252 #endif /* __PIC__ */
00253     curFrame->caller=nextFrame;
00254     return 1;
00255   }
00256 #ifdef __PIC__
00257 #  ifdef DEBUG_STACKTRACE
00258   if(curFrame->debug)
00259     printf("( %p %p %p ) -> { %p %p --- }\n",curFrame->sp,curFrame->ra,curFrame->gp,nsp,nra);
00260 #  endif
00261 #else
00262 #  ifdef DEBUG_STACKTRACE
00263   if(curFrame->debug)
00264     printf("( %p %p ) -> { %p %p }\n",curFrame->sp,curFrame->ra,nsp,nra);
00265 #  endif
00266 #endif
00267   return 0;
00268 #endif
00269 #endif /* backtrace */
00270 }
00271 
00272 #ifdef STACKTRACE_USE_BACKTRACE
00273 static int growAlloc(struct StackFrame* frame) {
00274   void** r = (void**)realloc(*frame->packedRA, *frame->packedRACap * sizeof(void*) * 2);
00275   if(r==NULL)
00276     return 0;
00277   *frame->packedRACap *= 2;
00278   *frame->packedRA = r;
00279   return 1;
00280 }
00281 #endif
00282 
00283 #ifdef STACKTRACE_USE_BACKTRACE
00284 const size_t MIN_CAP=50;
00285 static void allocBacktraceRegion(struct StackFrame* frame, size_t cap) {
00286   if(cap < MIN_CAP)
00287     cap=MIN_CAP;
00288   frame->packedRACap = (size_t*)malloc(sizeof(size_t));
00289   *frame->packedRACap = MIN_CAP;
00290   frame->packedRAUsed = (size_t*)malloc(sizeof(size_t));
00291   *frame->packedRAUsed = 0;
00292   frame->packedRA = (void***)malloc(sizeof(void**));
00293   *frame->packedRA = (void**)malloc(*frame->packedRACap * sizeof(void*));
00294   //printf("Allocated %p\n",*frame->packedRA);
00295 }
00296   
00297 static void freeBacktraceRegion(struct StackFrame* frame) {
00298   //printf("Freeing %p\n",*frame->packedRA);
00299   free(frame->packedRACap);
00300   frame->packedRACap = NULL;
00301   free(frame->packedRAUsed);
00302   frame->packedRAUsed = NULL;
00303   free(*frame->packedRA);
00304   *frame->packedRA=NULL;
00305   free(frame->packedRA);
00306   frame->packedRA=NULL;
00307 }
00308 #endif
00309 
00310 void getCurrentStackFrame(struct StackFrame* frame) {
00311 #ifdef STACKTRACE_USE_BACKTRACE
00312 
00313   // first call?  allocate the storage area
00314   if(frame->packedRA==NULL)
00315     allocBacktraceRegion(frame,MIN_CAP);
00316   
00317   // call backtrace, if we hit the capacity, grow the buffer and try again
00318   do {
00319     *frame->packedRAUsed = backtrace(*frame->packedRA, *frame->packedRACap);
00320   } while(*frame->packedRAUsed==*frame->packedRACap && growAlloc(frame));
00321   
00322   // if we used an oversized buffer, shrink it back down
00323   if(*frame->packedRACap > *frame->packedRAUsed*2 && *frame->packedRACap>MIN_CAP) {
00324     unsigned int newsize = *frame->packedRAUsed * 3 / 2;
00325     if(newsize < MIN_CAP)
00326       newsize = MIN_CAP;
00327     void** r = (void**)realloc(*frame->packedRA, newsize * sizeof(void*) * 2);
00328     if(r!=NULL) {
00329       *frame->packedRACap = newsize;
00330       *frame->packedRA = r;
00331     }
00332   }
00333   frame->depth=1;
00334   
00335 #else
00336   
00337   void** csp=NULL;
00338   machineInstruction* cra=NULL;
00339 
00340 #ifdef __POWERPC__
00341   __asm __volatile__ ("mr %0,r1" : "=r"(csp) ); // get the current stack pointer
00342   __asm __volatile__ ("mflr %0" : "=r"(cra) );  // get the current return address
00343 #endif /* __POWERPC__ */
00344   
00345 #if defined(__MIPSEL__) || defined(__MIPS__)
00346 #ifdef __PIC__
00347   size_t cgp=0;
00348   __asm __volatile__ ("move %0,$gp" : "=r"(cgp) ); //get the gp register so we can compute link addresses
00349 #endif /* __PIC__ */
00350   __asm __volatile__ ("move %0,$sp" : "=r"(csp) ); // get the current stack pointer
00351   __asm __volatile__ ("jal readepc; nop; readepc: move %0,$ra" : "=r"(cra) ); // get the current return address
00352 #endif /* __MIPSEL__ */
00353   
00354 #if defined(__i386__)
00355   __asm __volatile__ ("movl %%ebp,%0" : "=m"(csp) ); // get the caller's stack pointer
00356   csp++; //go back one to really be a stack pointer
00357   //__asm __volatile__ ("movl (%%esp),%0" : "=r"(cra) ); // get the caller's address
00358   cra=*((machineInstruction**)csp);
00359   csp=((void***)csp)[-1]+1;
00360 #endif /* __i386__ */
00361 
00362 // basically the same as i386, but movq instead of movl, and rbp instead of ebp
00363 #if defined(__x86_64__) || defined(__amd64__)
00364   __asm __volatile__ ("movq %%rbp,%0" : "=m"(csp) ); // get the caller's stack pointer
00365   csp++; //go back one to really be a stack pointer
00366   //__asm __volatile__ ("movq (%%rsp),%0" : "=r"(cra) ); // get the caller's address
00367   cra=*((machineInstruction**)csp);
00368   csp=((void***)csp)[-1]+1;
00369 #endif /* amd64/x86_64 */
00370 
00371   frame->sp=csp;
00372 #if defined(__PIC__) && (defined(__MIPSEL__) || defined(__MIPS__))
00373   frame->ra=cgp-reinterpret_cast<size_t>(cra);
00374   frame->gp=cgp;
00375 #else
00376   frame->ra=cra;
00377 #endif /* __PIC__ */
00378 
00379 #if !defined(__i386__) && !defined(__x86_64__) && !defined(__amd64__)
00380   //with ia-32 it was more convenient to directly provide caller, so don't need to unroll
00381   //otherwise we actually want to return *caller's* frame, so unroll once
00382   unrollStackFrame(frame,frame);
00383 #endif /* not __i386__ */
00384 #endif /* backtrace */
00385 }
00386 
00387 void freeStackTrace(struct StackFrame* frame) {
00388 #ifdef STACKTRACE_USE_BACKTRACE
00389   if(frame!=NULL && frame->packedRA!=NULL) {
00390     freeBacktraceRegion(frame);
00391   }
00392 #endif
00393   while(frame!=NULL) {
00394     struct StackFrame * next=frame->caller;
00395     free(frame);
00396     if(frame==next)
00397       return;
00398     frame=next;
00399   }
00400 }
00401 
00402 struct StackFrame* allocateStackTrace(unsigned int size) {
00403   struct StackFrame * frame=NULL;
00404   while(size--!=0) {
00405     struct StackFrame * prev = (struct StackFrame *)malloc(sizeof(struct StackFrame));
00406     memset(prev, 0, sizeof(*prev));
00407 #ifdef STACKTRACE_USE_BACKTRACE
00408     if(frame==NULL) {
00409       allocBacktraceRegion(prev,size);
00410     } else {
00411       prev->packedRA = frame->packedRA;
00412       prev->packedRAUsed = frame->packedRAUsed;
00413       prev->packedRACap = frame->packedRACap;
00414     }
00415     prev->depth = size-1;
00416 #endif
00417     prev->caller=frame;
00418     frame=prev;
00419   }
00420   return frame;
00421 }
00422 
00423 
00424 struct StackFrame * recordStackTrace(unsigned int limit/*=-1U*/, unsigned int skip/*=0*/) {
00425   if(limit==0)
00426     return NULL;
00427   struct StackFrame * cur = allocateStackTrace(1);
00428 #ifdef DEBUG_STACKTRACE
00429   cur->debug=0;
00430 #endif
00431   getCurrentStackFrame(cur);
00432   for(; skip!=0; skip--)
00433     if(!unrollStackFrame(cur,cur)) {
00434       freeStackTrace(cur);
00435       return NULL;
00436     }
00437   struct StackFrame * prev = (struct StackFrame *)malloc(sizeof(struct StackFrame));
00438   memset(prev, 0, sizeof(*prev));
00439 #ifdef DEBUG_STACKTRACE
00440   prev->debug=0;
00441 #endif
00442   
00443   //unroll once more for the current frame
00444   if(!unrollStackFrame(cur,prev)) {
00445     freeStackTrace(cur);
00446     return NULL;
00447   }
00448 #ifdef STACKTRACE_USE_BACKTRACE
00449   memset(cur,0,sizeof(*cur)); // clear cur, prev is now responsible for packedRA allocation
00450 #else
00451   cur->caller=NULL;
00452 #endif
00453   freeStackTrace(cur);
00454   cur=prev;
00455   
00456   for(--limit; limit!=0; limit--) {
00457     struct StackFrame * next = (struct StackFrame *)malloc(sizeof(struct StackFrame));
00458     memset(next, 0, sizeof(*next));
00459 #ifdef DEBUG_STACKTRACE
00460     next->debug=0;
00461 #endif
00462     if(!unrollStackFrame(prev,next)) {
00463       // reached end of trace
00464       free(next);
00465       prev->caller=NULL; //denotes end was reached
00466       return cur;
00467     }
00468     prev=next;
00469   }
00470   // reaching here implies limit was reached
00471   prev->caller=prev; //denotes limit was reached
00472   return cur;
00473 }
00474 
00475 struct StackFrame * recordOverStackTrace(struct StackFrame* frame, unsigned int skip) {
00476   struct StackFrame * cur = allocateStackTrace(1);
00477 #ifdef DEBUG_STACKTRACE
00478   cur->debug=0;
00479 #endif
00480   if(frame==NULL)
00481     return frame;
00482   getCurrentStackFrame(cur);
00483   for(; skip!=0; skip--)
00484     if(!unrollStackFrame(cur,cur)) {
00485       freeStackTrace(cur);
00486       return frame;
00487   }
00488 #ifdef STACKTRACE_USE_BACKTRACE
00489   if(frame->packedRA!=NULL)
00490     freeBacktraceRegion(frame);
00491 #endif
00492 
00493   //unroll once more for the current frame
00494   if(!unrollStackFrame(cur,frame)) {
00495     freeStackTrace(cur);
00496     return NULL;
00497   }
00498 #ifdef STACKTRACE_USE_BACKTRACE
00499   memset(cur,0,sizeof(*cur)); // clear cur, frame is now responsible for packedRA allocation
00500 #else
00501   cur->caller=NULL;
00502 #endif
00503   freeStackTrace(cur);
00504 
00505   struct StackFrame * ans;
00506   for(; frame->caller!=NULL && frame->caller!=frame; frame=frame->caller) {
00507     ans=frame->caller; //don't lose remainder of free list if we hit the end
00508     if(!unrollStackFrame(frame,frame->caller)) {
00509       return ans; // reached end of trace
00510     }
00511   }
00512   // reaching here implies limit was reached
00513   frame->caller=frame; //denotes limit was reached
00514   return NULL;
00515 }
00516     
00517     
00518 #ifdef __APPLE__
00519 // use atos to do symbol lookup, can lookup non-dynamic symbols and also line numbers
00520 /*! This function is more complicated than you'd expect because atos doesn't flush after each line,
00521  *  so plain pipe() or socketpair() won't work until we close the write side.  But the whole point is
00522  *  we want to keep atos around so we don't have to reprocess the symbol table over and over.
00523  *  What we wind up doing is using forkpty() to make a new pseudoterminal for atos to run in,
00524  *  and thus will use line-buffering for stdout, and then we can get each line. */
00525 static void atosLookup(unsigned int depth, void* ra) {
00526   static int fd=-1;
00527   int isfirst=0;
00528   
00529   if(fd==-1) {
00530     struct termios opts;
00531     cfmakeraw(&opts); // have to set this first, otherwise queries echo until child kicks in
00532     pid_t child = forkpty(&fd,NULL,&opts,NULL);
00533     if(child<0) {
00534       perror("Could not forkpty for atos call");
00535       return;
00536     }
00537     if(child==0) {
00538       //sleep(3);
00539       char pidstr[50];
00540       snprintf(pidstr,50,"%d",getppid());
00541       execlp("atos","atos","-p",pidstr,(char*)0);
00542       //snprintf(pidstr,50,"atos -p %d",getppid());
00543       //execlp("sh","sh","-i","-c",pidstr,(char*)0);
00544       fprintf(stderr,"Could not exec atos for stack trace!\n");
00545       _exit(1);
00546     }
00547     isfirst=1;
00548     }
00549 
00550     {
00551     char q[50];
00552     size_t qlen = snprintf(q,50,"%p\n",ra);
00553     //printf("query: %.*s",50,q);
00554     write(fd,q,qlen);
00555   }
00556   
00557   if(isfirst) {
00558     // atos can take a while to parse symbol table on first request, which is why we leave it running
00559     // if we see a delay, explain what's going on...
00560     int err;
00561     struct timeval tv = {3,0};
00562     fd_set fds;
00563     FD_ZERO(&fds);
00564     FD_SET(fd,&fds);
00565     err = select(fd+1,&fds,NULL,NULL,&tv);
00566     if(err<0)
00567       perror("select for atos output");
00568     if(err==0) // timeout
00569       printf("Generating... first call takes some time for 'atos' to cache the symbol table.\n");
00570     }
00571 
00572         {
00573     const unsigned int MAXLINE=1024;
00574     char line[MAXLINE];
00575     size_t nread=0;
00576     char c='x';
00577     while(c!='\n' && nread<MAXLINE) {
00578       if(read(fd,&c,1)<=0) {
00579         fprintf(stderr,"Lost atos connection for stacktrace\n");
00580         close(fd);
00581         fd=-1;
00582         break;
00583         }
00584       //printf("Read %d\n",c);
00585       if(c!='\r')
00586         line[nread++]=c;
00587     }
00588     if(nread<MAXLINE)
00589       line[nread++]='\0';
00590     fprintf(stderr,"%4d %.*s",depth,MAXLINE,line);
00591     }
00592 }
00593   
00594 #elif defined(STACKTRACE_USE_BACKTRACE)
00595   
00596 #  if defined(__GNUC__) && defined(__cplusplus)
00597   static std::string demangle(const std::string& name)
00598   {
00599     int status = 0;
00600     char *d = 0;
00601     std::string ret = name;
00602     try { if ((d = abi::__cxa_demangle(name.c_str(), 0, 0, &status))) ret = d; }
00603     catch(...) {  }
00604     std::free(d);
00605     return ret;
00606   }
00607 #  endif
00608   
00609 #  ifndef __linux__
00610 // on bsd based systems, we have fgetln() instead of getline()
00611 static int getline(char** s, size_t* len, FILE* f) {
00612   size_t _len;
00613   char * _s = fgetln(f,&_len);
00614   if(_s==NULL)
00615     return -1;
00616   if(*len<_len+1) {
00617     char * ns = (char*)realloc(*s,_len+1);
00618     if(ns==NULL)
00619       return -1;
00620     *s=ns;
00621   }
00622   memcpy(*s,_s,_len);
00623   (*s)[_len]='\0';
00624   return _len;
00625 }
00626 #  endif
00627 
00628 static int addr2lineLookup(const char* const ex, const void* const off, char** srcfile, size_t* srcfilelen, char** func, size_t* funclen) {
00629   const char * cmdfmt = "addr2line -fe '%s' %p";
00630   const size_t cmdlen = snprintf(NULL,0,cmdfmt,ex,off)+1;
00631   char * cmd = (char*)malloc(cmdlen*sizeof(char));
00632   if(cmd==NULL) {
00633     fprintf(stderr,"[ERR Could not malloc addr2line command]\n");
00634     return -1;
00635   }
00636   const int cmdused = snprintf(cmd,cmdlen,cmdfmt,ex,off);
00637   if(cmdused<0) {
00638     perror("snprintf for addr2line command");
00639     free(cmd);
00640     return -1;
00641   }
00642   if((size_t)cmdused>=cmdlen) {
00643     fprintf(stderr, "[ERR addr2line command grew? %d vs %lu]\n",cmdused,(unsigned long)cmdlen);
00644     free(cmd);
00645     return -1;
00646   }
00647   FILE* look=popen(cmd,"r");
00648   free(cmd);
00649   if(look==NULL) {
00650     fprintf(stderr, "[Missing addr2line]\n");
00651     return -1;
00652   }
00653   if(getline(func,funclen,look)<=0) {
00654     pclose(look);
00655     return -1;
00656   }
00657   if(getline(srcfile,srcfilelen,look)<=0) {
00658     pclose(look);
00659     return -1;
00660   }
00661   pclose(look);
00662   char* nl = strrchr(*func,'\n');
00663   if(nl!=NULL)
00664     *nl='\0';
00665   nl = strrchr(*srcfile,'\n');
00666   if(nl!=NULL)
00667     *nl='\0';
00668   return 0;
00669 }
00670 
00671 static void displayRelPath(FILE* os, const char * wd, const char* path) {
00672   unsigned int same=0,i=0;
00673   for(i=0; path[i]!='\0'; ++i) {
00674     if(wd[i]=='/')
00675       same=i+1;
00676     else if(wd[i]=='\0') {
00677       same=i;
00678       break;
00679     } else if(wd[i]!=path[i])
00680       break;
00681   }
00682   if(wd[same]=='\0')
00683     ++same;
00684   else if(same>1) {
00685     // really want to be relative to source tree root, don't bother with ..'s
00686     /*for(i=same; wd[i]!='\0'; ++i)
00687       if(wd[i]=='/')
00688         fprintf(os,"../");
00689     fprintf(os,"../");*/
00690   }
00691   fprintf(os,"%s",&path[same]);
00692 }
00693 #endif
00694 
00695 //! attempts to read symbol information and displays stack trace header
00696 static void beginDisplay() {
00697 #ifdef STACKTRACE_USE_BACKTRACE
00698   //fprintf(stderr,"Stack Trace:\n");
00699 #elif defined(PLATFORM_APERIOS)
00700   fprintf(stderr,"Run trace_lookup:");
00701 #elif defined(__APPLE__)
00702   fprintf(stderr,"backtrace_symbols() unavailable, try 'atos' to make human-readable backtrace (-p %d):",getpid());
00703 #else
00704   fprintf(stderr,"backtrace_symbols() unavailable, try addr2line or tools/trace_lookup to make human-readable backtrace:");
00705 #endif
00706 }
00707 
00708 #ifdef __APPLE__
00709 static void displayStackFrame(unsigned int depth, const struct StackFrame* frame) {
00710   atosLookup(depth,(void*)frame->ra);
00711 }
00712 #elif defined(STACKTRACE_USE_BACKTRACE)
00713 static void displayStackFrame(unsigned int depth, const struct StackFrame* frame) {
00714   void* ra = (void*)frame->ra;
00715   Dl_info sym;
00716   memset(&sym,0,sizeof(Dl_info));
00717   int dlres = dladdr(frame->ra, &sym);
00718   
00719   int isExe = (sym.dli_fname==NULL); // if lib unknown, assume static linkage, implies executable
00720 #  ifdef __linux__
00721   // detect if /proc/self/exe points to sym.dli_fname
00722   if(!isExe && sym.dli_fname[0]!='\0') {
00723     struct stat exeStat;
00724     struct stat libStat;
00725     if(stat("/proc/self/exe",&exeStat)!=0) {
00726       perror(" stat /proc/self/exe");
00727     } else if(stat(sym.dli_fname,&libStat)!=0) {
00728       perror(" stat lib");
00729     } else {
00730       isExe = (exeStat.st_dev==libStat.st_dev && exeStat.st_ino==libStat.st_ino);
00731     }
00732   }
00733 #  endif
00734 
00735   if(dlres==0 || sym.dli_sname==NULL) {
00736     if(sym.dli_fname==NULL || sym.dli_fname[0]=='\0') {
00737       fprintf(stderr,"%4d  [non-dynamic symbol @ %p]",depth,ra);
00738       fprintf(stderr," (has offset %p in unknown library)\n",sym.dli_fbase);
00739     } else {
00740       // use addr2line for static function lookup
00741       const void* const off = (isExe) ? ra : (void*)((size_t)ra-(size_t)sym.dli_fbase);
00742       char* srcfile=NULL, *func=NULL;
00743       size_t srcfilelen=0, funclen=0;
00744       if(addr2lineLookup(sym.dli_fname,off,&srcfile,&srcfilelen,&func,&funclen)==0) {
00745         fprintf(stderr,"%4d  %s",depth,(strlen(func)==0) ? "[unknown symbol]" : func);
00746         if(!isExe) {
00747           const char * base = strrchr(sym.dli_fname,'/');
00748           fprintf(stderr," (%s)",(base==NULL)?sym.dli_fname:base+1);
00749         }
00750         if(strcmp(srcfile,"??:0")!=0) {
00751           fprintf(stderr," ");
00752           char * wd = getcwd(NULL,0);
00753           if(wd==NULL) {
00754             perror("getcwd");
00755             return;
00756           }
00757           fprintf(stderr,"(");
00758           displayRelPath(stderr,wd,srcfile);
00759           fprintf(stderr,")");
00760           free(wd);
00761         }
00762         fprintf(stderr,"\n");
00763       }
00764       free(srcfile);
00765       free(func);
00766     }
00767     return;
00768   } else {
00769     const char * dispFmt="%4d  %s +%#lx";
00770 #  ifdef __cplusplus
00771     fprintf(stderr,dispFmt,depth,demangle(sym.dli_sname).c_str(),(size_t)ra-(size_t)sym.dli_saddr);
00772 #  else
00773     fprintf(stderr,dispFmt,depth,sym.dli_sname,(size_t)ra-(size_t)sym.dli_saddr);
00774 #  endif
00775   }
00776 
00777   if(sym.dli_fname==NULL || sym.dli_fname[0]=='\0') {
00778     fprintf(stderr," (%p, offset %p in unknown lib)\n",ra,sym.dli_fbase);
00779     return;
00780   } else if(isExe) {
00781     // don't bother listing executable name, just show return address
00782     // ...or since the symbol name and offset imply the return address, just skip it
00783     //fprintf(stderr," (%p)",ra);
00784   } else {
00785     // ra is meaningless in dynamically loaded libraries... address space layout randomization (ASLR)
00786     // just show library name
00787     const char * base = strrchr(sym.dli_fname,'/');
00788     fprintf(stderr," (%s)",(base==NULL)?sym.dli_fname:base+1);
00789   }
00790   
00791   // now do file and line number lookup of function via addr2line
00792   const void* const off = (isExe) ? ra : (void*)((size_t)ra-(size_t)sym.dli_fbase);
00793   char* srcfile=NULL, *func=NULL;
00794   size_t srcfilelen=0, funclen=0;
00795   if(addr2lineLookup(sym.dli_fname,off,&srcfile,&srcfilelen,&func,&funclen)==0 && strcmp(srcfile,"??:0")!=0) {
00796     fprintf(stderr," ");
00797     char * wd = getcwd(NULL,0);
00798     if(wd==NULL) {
00799       perror("getcwd");
00800       return;
00801     }
00802     fprintf(stderr,"(");
00803     displayRelPath(stderr,wd,srcfile);
00804     fprintf(stderr,")");
00805     free(wd);
00806   }
00807   fprintf(stderr,"\n");
00808   free(srcfile);
00809   free(func);
00810 }
00811 #else
00812 static void displayStackFrame(unsigned int ST_UNUSED(depth), const struct StackFrame* frame) {
00813   ST_BODY_UNUSED(depth);
00814   fprintf(stderr," %p",(void*)frame->ra);
00815 }
00816 #endif
00817 
00818 //! releases symbol information used during display
00819 static void completeDisplay(int isend) {
00820 #if defined(STACKTRACE_USE_BACKTRACE)
00821 #endif
00822   if(!isend)
00823     fprintf(stderr," ...\n");
00824 }
00825 
00826 void displayCurrentStackTrace(unsigned int limit/*=-1U*/, unsigned int skip/*=0*/) {
00827   struct StackFrame * cur = allocateStackTrace(1);
00828 #ifdef DEBUG_STACKTRACE
00829   cur->debug=0;
00830 #endif
00831   unsigned int i;
00832   int more;
00833   if(limit==0)
00834     return;
00835   getCurrentStackFrame(cur);
00836   //printf(" initial (%p\t%p\t%p)\n",cur.ra,cur.sp,*(void**)cur.sp);
00837   beginDisplay();
00838   for(; skip!=0; skip--) {
00839     if(!unrollStackFrame(cur,cur)) {
00840       completeDisplay(1);
00841       return;
00842     }
00843     //printf(" skip (%p\t%p\t%p)\n",cur.ra,cur.sp,*(void**)cur.sp);
00844   }
00845   for(i=0; (more=unrollStackFrame(cur,cur)) && i<limit; i++) {
00846     //printf(" out (%p\t%p\t%p)\n",cur.ra,cur.sp,*(void**)cur.sp);
00847     displayStackFrame(i,cur);
00848   }
00849   completeDisplay(!more);
00850   freeStackTrace(cur);
00851 }
00852 
00853 void displayStackTrace(const struct StackFrame* frame) {
00854   int i;
00855   beginDisplay();
00856   for(i=0; frame!=NULL && frame->caller!=frame; i++) {
00857     displayStackFrame(i,frame);
00858     frame=frame->caller;
00859   }
00860   if(frame!=NULL)
00861     displayStackFrame(i+1,frame);
00862   completeDisplay(frame==NULL);
00863 }
00864 
00865 
00866 #ifdef __cplusplus
00867 }
00868 #endif /* __cplusplus */
00869 
00870 /*! @file
00871  * @brief Implements functionality for performing stack traces
00872  * @author ejt (Generalized and implementation for non-MIPS platforms)
00873  * @author Stuart Scandrett (original inspiration, Aperios/MIPS stack operations)
00874  */

Tekkotsu v5.1CVS
Generated Mon May 9 04:58:51 2016 by Doxygen 1.6.3