linux下打印函数调用栈(stack backtrace in Linux)
2010-01-14 23:20
今天遇到个这样的需求,希望能当动态链接库中抛出异常时,能把调用栈打印出来。经过在google上一翻搜寻,找到了一些线索。
我的文件目录如下:当前目录下是main.cpp 和test/
test下面是A.h, A.cpp(定义一个类)和backtrace.h, backtrace.cpp
第一步,实现打印调用栈,backtrace函数实现如下:
backtrace.cpp
#include "backtrace.h"
#include
#include
#include
#include
#include
void backtrace()
{
const int maxLevel = 200;
void* buffer[maxLevel];
int level = backtrace(buffer, maxLevel);
const int SIZE = 1024;
char cmd[SIZE] = "addr2line -C -f -e ";
// let prog point to the end of "cmd"
char* prog = cmd + strlen(cmd);
int r = readlink("/proc/self/exe", prog, sizeof(cmd) - (prog-cmd)-1);
FILE* fp = popen(cmd, "w");
if (!fp)
{
perror("popen");
return;
}
for (int i = 0; i < level; ++i)
{
fprintf(fp, "%p/n", buffer[i]);
}
fclose(fp);
}
main.cpp
include
#include "A.h"
#include "backtrace.h"
void foo(int, char*)
{
backtrace();
}
void bar(double)
{
foo(0, NULL);
}
int main()
{
bar(0.0);
//A a;
return 0;
}
按如下编译,注意-g 和 -rdynamic参数。
[Charlie@localhost so]$ g++ -g -rdynamic -Itest main.cpp test/backtrace.cpp
[Charlie@localhost so]$ ./a.out
backtrace()
/home/Charlie/workspace/so/test/backtrace.cpp:12
foo(int, char*)
/home/Charlie/workspace/so/main.cpp:7
bar(double)
/home/Charlie/workspace/so/main.cpp:11
main
/home/Charlie/workspace/so/main.cpp:17
??
??:0
_start
??:0
从上述输出结果可以看到,调用栈被正确地打印了出来,包括函数名,代码文件:行数
下面的??表示无法得到代码文件名,必要时可能进行过滤。
第二步,尝试在动态链接库中打印调用栈。
A.cpp
#include "A.h"
#include "backtrace.h"
#include
A::A()
{
printf("A::A/n");
backtrace();
}
main.cpp中相关改动为:
int main()
{
//bar(0.0);
A a;
return 0;
}
首先编译生成动态链接库:
[Charlie@localhost test]$ g++ -g -rdynamic -o libA.so -shared A.cpp backtrace.cpp
[Charlie@localhost so]$ g++ -g -rdynamic -Ltest -Itest -lA main.cpp
[Charlie@localhost so]$ ./a.out
A::A
??
??:0
??
??:0
main
/home/Charlie/workspace/so/main.cpp:7
??
??:0
_start
??:0
结果非常遗憾,只有main函数能打出文件名和行数。动态链接库中不能打出来。
动态库不行,试试静态库呢?凭直觉应该是行的,因为它和直接编译链接差不多嘛。
cd 到test下面,
[Charlie@localhost test]$ g++ -g -rdynamic -c backtrace.cpp A.cpp
编译生成.o文件,再用ar将其打成.a文件。
[Charlie@localhost test]$ ar -rc libA.a A.o backtrace.o
[Charlie@localhost test]$ cd ..
[Charlie@localhost so]$ g++ -g -rdynamic -Itest main.cpp test/libA.a
[Charlie@localhost so]$ ./a.out
A::A
backtrace()
/home/Charlie/workspace/so/test/backtrace.cpp:12
A
/home/Charlie/workspace/so/test/A.cpp:9
main
/home/Charlie/workspace/so/main.cpp:17
??
??:0
_start
??:0
这次执行果然得到了我们希望的结果,正确地打印出了栈信息。
结论:利用backtrace借助addr2line命令可以在运行时打出函数调用栈信息,这种办法对静态链接有效,对动态库不行(暂时没发现行)。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
取得函数调用栈 - __builtin_frame_address
2007-09-17 15:01
GCC提供了两个内置函数用来在运行时取得函数调用栈中的返回地址和框架地址
如果使用glibc 2.1或更新版本,可以使用backtrace()函数,参看,其他系统可能有不同的技术支持。
Built-in Function: void * __builtin_return_address (unsigned int level)
Built-in Function: void * __builtin_frame_address (unsigned int level)
一个例子
#include <memory.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <ucontext.h>
#include <dlfcn.h>
#include <execinfo.h>
void showBacktrace()
{
void * ret = __builtin_return_address(1);
printf("ret address [%x]/n", ret);
void * caller = __builtin_frame_address(0);
printf("call address [%x]/n", caller);
#ifdef __cplusplus
Dl_info dlinfo;
void *ip = ret;
if(!dladdr(ip, &dlinfo)) {
perror("addr not found/n");
return;
}
const char *symname = dlinfo.dli_sname;
int f = 0;
fprintf(stderr, "% 2d: %p %s+%u (%s)/n",
++f,
ip,
symname, 0,
// (unsigned)(ip - dlinfo.dli_saddr),
dlinfo.dli_fname);
#endif
}
int MyFunc_A()
{
showBacktrace();
return 10;
}
int MyFunc_B()
{
return MyFunc_A();
}
int main()
{
MyFunc_B();
return 0;
}
$ g++ t.cpp -rdynamic -ldl -o t
$ ./t
ret address [80488ff]
call address [bfffb938]
1: 0x80488ff _Z8MyFunc_Bv+0 (./t)