[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: backtrace of all stack frames, without debugger
On Fri, Sep 14, 2001, guy keren wrote about "Re: backtrace of all stack frames, without debugger":
>
> On Thu, 13 Sep 2001, Nadav Har'El wrote:
>
> > Ok, so that's a nice and useful trick. But I want to improve it to show the
> > code addresses of all function calls on the stack frames, not just the last
> > call.
>
> the answer is in the question. look at /usr/include/execinfo.h . the
Ok. So thanks to Guy's suggestion of using backtrace() from glibc (wow, this
glibc is a goldmine of interesting and completely non-standard functions ;)),
I got my SIGSEGV signal handler to work like I wanted it: when the program
gets a segmentaion violation (possibily in one of many threads), it prints
out a backtrace - a list of the exact functions and line numbers running
at the time (actually, it prints a "addr2line ..." command which the user
then runs to get that backtrace). Using backtrace() isn't completely
straighforward in this case, because a signal handler is NOT a normal function
call, and backtrace() doesn't, for some reason, take a stack address on
which to start its search... But if you don't specifically ask for this
signal handler to get a seperate stack, using a somewhat-ugly combination
of sigcontext and backtrace() seems to do the job well in Linux.
The code is included below, for everyone's enjoyment (if you can call debugging
enjoyment ;)). Call debug_signal(SIGSEGV) to use it.
For example, I wrote an example program with multiple threads, one of which
overflows a buffer and gets a segmentation violation. Running this program
gives me:
/home/nyh/c/multithread-debug$ a.out
Signal 11 received. To find where, run
addr2line -e /home/nyh/c/multithread-debug/a.out -f -C 0x8048883 0x80488ad 0x80488e4 0x40030bfd 0x4012577a
And when I now run the addr2line command it tells me, I get
funcb
/home/nyh/c/multithread-debug/try5.c:63
funca
/home/nyh/c/multithread-debug/try5.c:68
failer
/home/nyh/c/multithread-debug/try5.c:74
??
??:0
??
??:0
Which is indeed exactly what happens: failer() is the thread function that
calls funca() which calls funcb() which is the one overflowing the buffer,
with line 63 indeed being exactly the bad line looking like
s[10000]='h';
--------------------------------------------------------------------------
#include <signal.h>
#include <unistd.h>
#include <execinfo.h>
/* returns >=0 on success and buf filled and null-terminated. */
static int
find_current_executable(char *buf,int bufsize){
int i;
i=readlink("/proc/self/exe",buf,bufsize);
if(i>0 && i<bufsize){
buf[i]='\0'; /* readlink doesn't finish string with null */
return i+1;
}
return -1;
}
static void
debugging_sighandler(int sig, struct sigcontext sc)
{
/*** the following trick finds the executable's filename in progname */
char progname[128];
void *pointers[30];
size_t size;
int i;
if(find_current_executable(progname,sizeof(progname)) < 0)
sprintf(progname,"?unknown-executable?"); /*where is the executable?*/
fprintf(stderr, "\nSignal %d received. To find where, run\n ",sig);
size=backtrace(pointers, sizeof(pointers)/sizeof(*pointers));
/* it appears that in Linux, the first 3 functions are crap. I'm not
sure why (the first one is, obviously, this function), or why the
function interrupted doesn't get showed (we show it explicitly on
the first printf line) */
fprintf(stderr,"addr2line -e %s -f -C %p ",progname,(void*)sc.eip);
for(i=3;i<size;i++){
fprintf(stderr, "%p ",pointers[i]);
}
fprintf(stderr,"\n");
exit(1);
}
void
debug_signal(int sig)
{
signal(sig, (__sighandler_t) debugging_sighandler);
}
--
Nadav Har'El | Monday, Sep 17 2001, 29 Elul 5761
nyh@math.technion.ac.il |-----------------------------------------
Phone: +972-53-245868, ICQ 13349191 |"A mathematician is a device for turning
http://nadav.harel.org.il |coffee into theorems" -- P. Erdos
=================================================================
To unsubscribe, send mail to linux-il-request@linux.org.il with
the word "unsubscribe" in the message body, e.g., run the command
echo unsubscribe | mail linux-il-request@linux.org.il