1 前言
在cpp
项目中,我们除了使用GDB-生产环境如何兼顾效率与排错这种方式定位问题之外,还有一种在不损失性能的基础上尽可能帮助我们找到问题的方法:在收到SIGNINTER
信号后调用backtrace
方法,并输出调用栈信息。
再通过addr2line命令工具定位到具体代码行。
2 示例
下面我们举个简单的例子,我们准备一个段错误代码:
#include <execinfo.h>
#include <iostream>
#include <signal.h>
using namespace std;
void stage2()
{
int *a = nullptr;
*a = 1;
}
void stage1()
{
stage2();
}
#define BACKTRACE_SIZE 16
void dump(void)
{
int i = 0, nptrs = 0;
void *buf[BACKTRACE_SIZE];
char **strings;
nptrs = backtrace(buf, BACKTRACE_SIZE);
printf("backtrace() returned %d addresses\n", nptrs);
strings = backtrace_symbols(buf, nptrs);
if (strings == NULL)
{
perror("backtrace_symbols");
exit(EXIT_FAILURE);
}
for (i = 0; i < nptrs; i++)
{
printf(" [%02d] %s\n", i, strings[i]);
}
free(strings);
}
void signal_handler(int signo)
{
printf("\n=================>>>catch signal %d<<<=====================\n", signo);
printf("Dump stack start...\n");
dump();
printf("Dump stack end...\n");
signal(signo, SIG_DFL);
raise(signo);
}
int main(int argc, char const *argv[])
{
signal(SIGSEGV, signal_handler);
stage1();
return 0;
}
运行后输出:
=================>>>catch signal 11<<<=====================
Dump stack start...
backtrace() returned 7 addresses
[00] ./test(+0x1386) [0x56279566e386]
[01] ./test(+0x145d) [0x56279566e45d]
[02] /lib/x86_64-linux-gnu/libc.so.6(+0x42520) [0x7ff5e5a3f520]
[03] ./test(+0x11db) [0x56279566e1db]
[04] /lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7ff5e5a26d90]
[05] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7ff5e5a26e40]
[06] ./test(+0x1245) [0x56279566e245]
Dump stack end...
Segmentation fault
我们可以看到,输出的都是地址,仅凭这些我们是无法定位到问题的。
这个时候我们可以使用addr2line
确定行号:
addr2line -Cif -e ${program_path} ${addr}
示例:
wzq@WZQ-Laptop:~/cpp_test/build$ addr2line -Cif -e ./test 0x11db
stage2()
/home/wzq/cpp_test/test.cpp:11
stage1()
/home/wzq/cpp_test/test.cpp:16
main
/home/wzq/cpp_test/test.cpp:55
可以看到,它帮助我们定位到了第11行*a = 1;
这里。这样我们可以大致确定问题所在了。