Linux打印函数调用栈

时间:2021-02-23 03:39:17

目的:打印函数调用栈,方便问题定位

输入:

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、上面的代码可能会泄露内存

Linux打印函数调用栈

Linux打印函数调用栈
Linux打印函数调用栈

按照man的描述,需要调用者来释放内存,是不是直接

free(strings)

就可以了?