目的:打印函数调用栈,方便问题定位
输入:
void back_trace() { int i =0; void *bt[30]; char **strings; size_t sz; sz = backtrace(bt, 30); printf("sz = %d\n", sz); strings = backtrace_symbols(bt, sz); for(i = 0; i < sz; ++i) { printf("%s\n", strings[i]); } } int func4() { (void)back_trace(); return 0; } int func3() { (void)func4(); return 0; } int func2() { (void)func3(); return 0; } int func1() { (void)func2(); return 0; } int main(int argc, char **argv) { (void)func1(); }
输出:
[root@/home/caijinqiu/SVN/src/networking/common]$./out sz = 8 ./out() [0x804849a] ./out() [0x80484fe] ./out() [0x8048510] ./out() [0x8048522] ./out() [0x8048534] ./out() [0x8048546] /lib/libc.so.6(__libc_start_main+0xe6) [0xb85e16] ./out() [0x80483e1]
分析:
1、用readelf命令打印出进程的符号表,有如下
60: 08048474 127 FUNC GLOBAL DEFAULT 13 back_trace 61: 08048505 18 FUNC GLOBAL DEFAULT 13 func3 62: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_ 63: 0804860c 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used 64: 00000000 0 FUNC GLOBAL DEFAULT UND backtrace_symbols@@GLIBC_ 65: 080497c4 0 NOTYPE GLOBAL DEFAULT 24 __data_start 66: 08048610 0 OBJECT GLOBAL HIDDEN 15 __dso_handle 67: 08048517 18 FUNC GLOBAL DEFAULT 13 func2 68: 080496cc 0 OBJECT GLOBAL HIDDEN 19 __DTOR_END__ 69: 08048550 90 FUNC GLOBAL DEFAULT 13 __libc_csu_init 70: 00000000 0 FUNC GLOBAL DEFAULT UND printf@@GLIBC_2.0 71: 080497c8 0 NOTYPE GLOBAL DEFAULT ABS __bss_start 72: 00000000 0 FUNC GLOBAL DEFAULT UND backtrace@@GLIBC_2.1 73: 08048529 18 FUNC GLOBAL DEFAULT 13 func1 74: 080497d0 0 NOTYPE GLOBAL DEFAULT ABS _end 75: 00000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.0 76: 080497c8 0 NOTYPE GLOBAL DEFAULT ABS _edata 77: 080485b5 0 FUNC GLOBAL HIDDEN 13 __i686.get_pc_thunk.bx 78: 080484f3 18 FUNC GLOBAL DEFAULT 13 func4 79: 0804853b 20 FUNC GLOBAL DEFAULT 13 main 80: 0804831c 0 FUNC GLOBAL DEFAULT 11 _init
2、按照地址大小进行排序
./out() [0x8048546] 0804853b main ./out() [0x8048534] 08048529 func1 ./out() [0x8048522] 08048517 func2 ./out() [0x8048510] 08048505 func3 ./out() [0x80484fe] 080484f3 func4 ./out() [0x804849a] 08048474 back_trace结论:
打印出来的调用栈,先打印的是栈底,即第一个调用的函数,接下来按照函数的调用顺序进行打印,也就是我们写代码的顺序
遗留问题:
1、有没有函数可以直接打印出函数的名称,这样通过对比地址,太麻烦
2、调用栈一共有八层,main函数前面的两侧是什么函数,按照符号表来看,应该是_init或者_start之类的东西
3、上面的代码可能会泄露内存
按照man的描述,需要调用者来释放内存,是不是直接
free(strings)
就可以了?