Reputation: 1445
I have done some code in C that happily send the full backtrace with function name and line number to a log file.
This was done using a mixture of backtrace, backtrace_symbols and dladdr and ADDR2LINE on LINUX. Also using the "execinfo.h" on Linux....
So essentially the following:
Backtrace: Line Locations:
signalErrorHandler
/home/lynton/Desktop/TestThreadLeak/TestThreadLeak/./main.c:211
??
??:0
*__GI_raise
/build/buildd/eglibc-2.12.1/signal/../nptl/sysdeps/unix/sysv/linux/raise.c:64
*__GI_abort
/build/buildd/eglibc-2.12.1/stdlib/abort.c:94
__libc_message
/build/buildd/eglibc-2.12.1/libio/../sysdeps/unix/sysv/linux/libc_fatal.c:168
malloc_printerr
/build/buildd/eglibc-2.12.1/malloc/malloc.c:6283
*__GI___libc_free
/build/buildd/eglibc-2.12.1/malloc/malloc.c:3739
threadMainLoop
/home/lynton/Desktop/TestThreadLeak/TestThreadLeak/./main.c:260
start_thread
/build/buildd/eglibc-2.12.1/nptl/pthread_create.c:304
??
/build/buildd/eglibc-2.12.1/misc/../sysdeps/unix/sysv/linux/x86_64/clone.S:114
Now that I have taken the code to Solaris I see it is not supported ;-(
I have tried the pstack approach on Solaris and get something like:
15871: ./exit_test
----------------- lwp# 1 / thread# 1 --------------------
ffffffff7efdaf48 lwp_wait (2, ffffffff7ffffb9c)
ffffffff7efd34ac _thrp_join (2, 0, 0, 1, 0, ffffffff7ffffb9c) + 38
00000001000012f0 main (1, ffffffff7ffffd28, ffffffff7ffffd38, 100101f68, 100000000, ffffffff7f500200) + 204
0000000100000ba4 _start (0, 0, 0, 0, 0, 0) + 7c
----------------- lwp# 2 / thread# 2 --------------------
ffffffff7efdb210 waitid (0, 3e01, ffffffff7eaf8c30, 3)
ffffffff7efc9cbc waitpid (3e01, ffffffff7eaf8eb0, 0, 0, ffffffff7f100300, 0) + 64
ffffffff7efbcc08 system (ffffffff7eaf9ff0, 1ad8, 1800, 0, ffffffff7f13c000, ffffffff7eaf8f18) + 394
0000000100000fec signalErrorHandler (b, 0, ffffffff7eafbba0, 40000000, 0, 0) + 2bc
ffffffff7efd6fdc __sighndlr (b, 0, ffffffff7eafbba0, 100000d30, 0, 0) + c
ffffffff7efcab70 call_user_handler (ffffffff7f500a00, ffffffff7f500a00, ffffffff7eafbba0, 12, 0, 0) + 3e0
ffffffff7efcad7c sigacthandler (0, 0, ffffffff7eafbba0, ffffffff7f500a00, 0, ffffffff7f13c000) + 68
--- called from signal handler with signal 0 (SIGEXIT) ---
ffffffff7ee0052c memcpy (ffffffff7ffffd28, 1fc000, 0, 0, 100001040, 0) + 30
ffffffff7efd6eb0 _lwp_start (0, 0, 0, 0, 0, 0)
How can I use the above somehow to PROGRAMATICALLY get the LINE NUMBERS and function names as well? I have see something about "walkcontext" or "walkstack"....does anyone have any sample code for me to get line numbers etc?
Also, I have used ADDR2LINE on Linux and it works great.....can someone tell me how to use it on Solaris from the DUMP above? I cannot get it to work ;-(
Any advise will be hugely appreciated
Thanks
Lynton
Upvotes: 4
Views: 5855
Reputation: 5951
I'll start out by saying that C might not be the best way to do this in 2011 (depending on what your larger aims are). Check out this other question: Analizing MIPS binaries: is there a Python library for parsing binary data? which references (eg) pydevtools.
That said, please find below an example using gaddr2line
(which is how Solaris spells addr2line
).
This short program just calls function foo()
, which in turn calls pstack(1)
(on line 9, via system(3C)
). In the output from the program, pstack(1)
tells us that the address in function foo()
when system()
is invoked is 0x00010724. Finally, running gaddr2line(1)
on that address tells us that this corresponds to line 9 of foo.c
, and we have come full circle.
/tmp $ cat -n foo.c
1
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5
6 int foo() {
7 char buf[64];
8 snprintf(buf, 64, "/bin/pstack %i", getpid());
9 return system(buf);
10 }
11
12 int main(int argc, char *argv[]) {
13 return foo();
14 }
15
/tmp $ gcc -g -o foo foo.c
/tmp $
/tmp $ ./foo
15954: ./foo
ff2cd4d8 waitid (0, 3e53, ffbff668, 3)
ff2bce94 waitpid (3e53, ffbff7bc, 0, 0, ffbff814, ff390140) + 60
ff2afe20 system (ffbff910, ff339bd0, 20000, 1, ff3303d8, ffbff814) + 2ec
00010724 foo (209b8, 1c00, ff335900, 4, ff392a00, ff2b6d6c) + 38
00010748 main (1, ffbffa34, ffbffa3c, 209dc, ff3900c0, 0) + c
00010584 _start (0, 0, 0, 0, 0, 0) + 5c
/tmp $
/tmp $ gaddr2line -e foo 00010724
/tmp/foo.c:9
/tmp $
Following on from that, here is a short example of using walkcontext(3C)
to walk the stack. To get debug line number information however you will need to query the appropriate sections of the ELF(?) binary using (eg) libdwarf in the walker()
function but this should get you started.
/tmp $ cat -n bar.c
1
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <ucontext.h>
6 #include <dlfcn.h>
7
8 int walker(uintptr_t pc, int sig, void *usrarg) {
9
10 Dl_info dlip;
11
12 if(dladdr((void *)pc, &dlip)) {
13 (void)printf(" %08lx %s %s\n", pc, dlip.dli_fname, dlip.dli_sname);
14 return 0;
15 } else {
16 perror("dladdr()");
17 return -1;
18 }
19
20 }
21
22 int bar() {
23
24 char buf[64];
25 snprintf(buf, 64, "/bin/pstack %i", getpid());
26 system(buf);
27
28 (void)printf("\nprintstack()\n");
29 printstack(0);
30
31 ucontext_t ucp;
32 if(getcontext(&ucp)) {
33 perror("\ngetcontext()");
34 return -1;
35 } else {
36 (void)printf("\nwalkcontext()\n");
37 return walkcontext(&ucp, &walker, NULL);
38 }
39
40 }
41
42 int main(int argc, char *argv[]) {
43 return bar();
44 }
45
/tmp $ gcc -g -o bar bar.c
/tmp $
/tmp $ ./bar
16486: ./bar
ff2cd4d8 waitid (0, 4067, ffbff4b8, 3)
ff2bce94 waitpid (4067, ffbff60c, 0, 0, ffbff664, ff390140) + 60
ff2afe20 system (ffbff928, ff339bd0, 20000, 1, ff3303d8, ffbff664) + 2ec
000108b8 bar (20c70, 1c00, ff335900, 4, ff392a00, ff2b6d6c) + 38
00010968 main (1, ffbffa4c, ffbffa54, 20c94, ff3900c0, 0) + c
00010698 _start (0, 0, 0, 0, 0, 0) + 5c
printstack()
/tmp/bar:bar+0x54
/tmp/bar:main+0xc
/tmp/bar:_start+0x5c
walkcontext()
00010968 /tmp/bar main
00010698 /tmp/bar _start
Upvotes: 3