输出函数调用的堆栈信息
1.1 引言
GDB的backtrace命令可以查看堆栈信息。但很多时候,GDB根本用不上。比如说,在线上环境中可能没有GDB,即使有,也有时候堆栈消息是乱的。如果能让程序自己输出调用栈,是最好不过的。另外该方法也不一定只有进程挂掉才能看,正常情况也是可以使用的,比如当你想知道你提供的接口到底是谁在调用的时候。
1.2 接口说明及使用
#include <execinfo.h>
int backtrace(void **buffer, int size);
char **backtrace_symbols(void *const *buffer, int size);
void backtrace_symbols_fd(void *const *buffer, int size, int fd);
以上内容源自man手册。
先简单介绍一下这几个函数的功能:
- backtrace:获取当前的调用栈信息,结果存储在buffer中,返回值为栈的深度,参数size限制栈的最大深度,即最大取size步的栈信息。
- backtrace_symbols:把backtrace获取的栈信息转化为字符串,以字符指针数组的形式返回,参数size限定转换的深度,一般用backtrace调用的返回值。
- backtrace_symbols_fd:它的功能和backtrace_symbols差不多,只不过它不把转换结果返回给调用方,而是写入fd指定的文件描述符。
Man手册里,有一个简单的实例,自己也写了简单的来测试一下:
/*************************************************************************
> File Name: backtrace.cpp
> Author: Veitch
> Created Time: 2019年08月19日 星期一 14时25分45秒
************************************************************************/
#include<iostream>
#include <execinfo.h>
#include <stdio.h>
using namespace std;
void func1()
{
void *addr[32];
int size = backtrace(addr,100);
char **array = backtrace_symbols(addr,size);
//printf("array[0]:%s",array[0]);
if (array == NULL) {
perror("backtrace_symbols");
exit(EXIT_FAILURE);
}
for(int i = 0;i<size;i++)
{
printf("addr[%d]:%p\n",i,(char*)(addr[i]));
printf("array[%d]:%s\n",i,array[i]);
}
free(array);
}
void func2()
{
func1();
}
int main()
{
func2();
return 0;
}
结果输出如下:
array[0]:./backtrace() [0x400914]
array[1]:./backtrace() [0x4009da]
array[2]:./backtrace() [0x4009e6]
array[3]:/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7ffad3f7c830]
array[4]:./backtrace() [0x400819]
这样,是输出了调用栈,不过只是以十六进制输出函数地址而已,可读性很差。仔细看下man手册,原来很简单,编译时加上个参数 rdynamic:
g++ backtrace.cpp -o backtrace -std=c++11 -rdynamic
重新编译运行:
./backtrace
addr[0]:0x400b24
array[0]:./backtrace(_Z5func1v+0x2e) [0x400b24]
addr[1]:0x400c11
array[1]:./backtrace(_Z5func2v+0x9) [0x400c11]
addr[2]:0x400c1d
array[2]:./backtrace(main+0x9) [0x400c1d]
addr[3]:0x7f09c6de0830
array[3]:/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f09c6de0830]
addr[4]:0x400a29
array[4]:./backtrace(_start+0x29) [0x400a29]
这样就能看到函数名了