linux下打印函数调用栈(stack backtrace in Linux)

来源:互联网 发布:javascript 文字滚动 编辑:程序博客网 时间:2024/05/22 17:41

今天遇到个这样的需求,希望能当动态链接库中抛出异常时,能把调用栈打印出来。经过在google上一翻搜寻,找到了一些线索。

我的文件目录如下:当前目录下是main.cpp 和test/

test下面是A.h, A.cpp(定义一个类)和backtrace.h, backtrace.cpp

第一步,实现打印调用栈,backtrace函数实现如下:

backtrace.cpp

#include "backtrace.h"

#include <unistd.h>
#include <stdio.h>
#include <execinfo.h>
#include <stdlib.h>
#include <string.h>
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 <stdio.h>
#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 <stdio.h>

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命令可以在运行时打出函数调用栈信息,这种办法对静态链接有效,对动态库不行(暂时没发现行)。


转自:http://hi.baidu.com/anatacollin/item/d31d031135202d9d99ce33b9

原创粉丝点击