工作遇到了一个段错误的异常,本来想在程序里使用backtrace等接口抓取一下异常现场的信息,结果并没有抓到,所以记录一下这个问题。
一开始我是在程序里设置了如下的代码
void logTrace(int signal)
{
void* array[300];
size_t size;
char** strings;
int i;
size = backtrace(array, 300);
strings = backtrace_symbols(array, size);
if (NULL == strings) {
perror("backtrace_symbols");
exit(EXIT_FAILURE);
}
LOG(LOG_LEVEL_ERROR, "Obtained %zd stack frames.", size);
for(i = 0 ; i < size; i++){
LOG(LOG_LEVEL_ERROR, "[%d] %s", i, strings[i]);
}
free(strings);
strings = NULL;
exit(EXIT_SUCCESS);
}
并在main函数中对几个信号都进行了捕捉
signal(SIGSEGV, logTrace);
signal(SIGTERM, logTrace);
signal(SIGKILL, logTrace);
signal(SIGINT, logTrace);
本以为在程序异常退出的时候,可以记录出程序退出的回溯栈信息,方便定位问题,但结果并不是我想象的那样,只输出了一句话!!!
Obtained 1 stack frames.
[0] app(logTrace+0x1c) [0x405410]
我就奇怪了?为啥?百思不得其解。于是我写了一个简单的c程序,打算验证一下这个事情。
#include <stdio.h>
#include <execinfo.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <stdlib.h>
#include<string.h>
void logTrace(int signal)
{
void* array[300];
size_t size;
char** strings;
int i;
size = backtrace(array, 300);
strings = backtrace_symbols(array, size);
if (NULL == strings) {
perror("backtrace_symbols");
exit(EXIT_FAILURE);
}
printf("Obtained %zd stack frames.\n", size);
for(i = 0 ; i < size; i++){
printf("[%d] %s\n", i, strings[i]);
}
free(strings);
strings = NULL;
exit(EXIT_SUCCESS);
}
void Fun1()
{
char *p=NULL;
*p = 'A';
}
void Fun()
{
Fun1();
}
int main(void)
{
signal(SIGHUP, logTrace);
signal(SIGINT, logTrace);
signal(SIGQUIT, logTrace);
signal(SIGILL, logTrace);
signal(SIGTRAP, logTrace);
signal(SIGKILL, logTrace);
signal(SIGSEGV, logTrace);
signal(SIGTERM, logTrace);
Fun();
return 0;
}
我把常见的几个信号都绑定了logTrace函数,当信号发生时,会先触发这个函数。然后故意搞了个Fun函数访问空指针,触发段错误,看能不能打印回溯信息。
gcc -g -rdynamic trace.c -o trace
事实证明,确实会打印出回溯信息。
Obtained 7 stack frames.
[0] ./trace(logTrace+0x34) [0x55df6c534a6e]
[1] /lib/x86_64-linux-gnu/libc.so.6(+0x3ef10) [0x7f196e15df10]
[2] ./trace(Fun1+0x10) [0x55df6c534b61]
[3] ./trace(Fun+0xe) [0x55df6c534b75]
[4] ./trace(main+0x96) [0x55df6c534c0e]
[5] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7) [0x7f196e140c87]
[6] ./trace(_start+0x2a) [0x55df6c53495a]
从这个信息上看基本能看出来问题出现的大概位置,这样的效果我也是能接受的,但是,为啥我的业务程序加了同样的代码却没有这样的回溯信息呢?于是我把这个程序使用交叉工具编译,上传到arm平台上验证了一下,不验不知道,一验才知道,是!真!的!不!可!以!
代码原封不动,我只是换了交叉工具编译而已。
aarch64-linux-gnu-gcc -g -rdynamic trace.c -o trace
也不知道是什么妖孽在做妖,偏偏为难我这个程序员,它只输出了一句话,和我业务程序的现象一毛一样。
Obtained 1 stack frames.
[0] ./trace(logTrace+0x1c) [0x400ad0]
查了半天才试出来,原来和编译选项有关。在arm平台编译的时候需要加上-funwind-tables -ffunction-sections选项。
aarch64-linux-gnu-gcc -g -rdynamic -funwind-tables -ffunction-sections trace.c -o trace
这样编译出来的程序,才香!
Obtained 7 stack frames.
[0] ./trace(logTrace+0x1c) [0x400ad0]
[1] linux-vdso.so.1(__kernel_rt_sigreturn+0) [0x7fb666c66c]
[2] ./trace(Fun1+0x10) [0x400b8c]
[3] ./trace(Fun+0xc) [0x400ba8]
[4] ./trace(main+0xac) [0x400c60]
[5] /lib/libc.so.6(__libc_start_main+0xe4) [0x7fb64f3ce4]
[6] ./trace() [0x4009fc]
这样的效果至少能给点崩溃的信息,有点作用。这两个选项啥意思?附上GCC的说明
-funwind-tables
Similar to -fexceptions, except that it will just generate any needed static data, but will not affect the generated code in any other way. You will normally not enable this option; instead, a language processor that needs this handling would enable it on your behalf.
简单的说,这个选项是会生成一些静态数据,也不会影响正式代码。
-ffunction-sections
instructs gcc to place each function(including static ones) in its own section named .text.function_name instead of placing all functions in one big .text section.
这个的意思应该是将每个函数或符号创建为一个单独的.text.function_name,而不是放在一个大的.text里。
通常在资源不是特别紧张的时候开启这两个选项没太大问题,但对于资源紧张的情况,可能需要注意以下程序的大小,必要时可以优化以下其他的选项。
这里只是记录一下这个问题,在其他的一些平台我没有试过。结束。