Linux打印函数调用栈

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

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

输入:

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)

就可以了?