功能:程序出错时,比如某些端错误等等,有时候为排查这种错误,会采用加打印信息的方式,去逐个排查,如果存在两个版本(比如SVN上一版本正常)这种情况下还好处理,否则,就是 用gdb,但是需要重新编译,-g, 并且有的时候,使用gdb程序去跑我们自己的程序时,有时候还会因为嵌入式设备资源的限制用不了,或者某些情况下因为随机值(如某些变量为初始化导致的问题,跨界访问导致的问题)问题导致不易重现。 这个时候可以使用core dump 。core dump的使用,本篇不做详解。上面的GDB, core dump + gdb,都较为麻烦,现在采用另一种方式,直接在代码中,当我们程序收到 某些信号时,直接使用 代码 输出当前的栈调用关系,大多时候,还是很有用的。
1.0 backtarce
tip:现在,只有使用ELF二进制格式的程序和苦衷才能获取函数名称和偏移地址.在其他系统,只有16进制的返回地址能被获取.另外,你可能需要传递相应的标志给链接器,以能支持函数名功能(比如,在使用GNU ld的系统中,需要传递(-rdynamic))
#include <execinfo.h>
需要的头文件
int backtrace(void **buffer, int size);
获取当前线程的调用堆栈,获取的信息将会被存放在buffer中,它是一个指针数组
注意某些编译器的优化选项对获取正确的调用堆栈有干扰,另外内联函数没有堆栈框架;删除框架指针也会使无法正确解析堆栈内容
char **backtrace_symbols(void *const *buffer, int size);
将从backtrace函数获取的信息转化为一个字符串数组.
backtrace_symbols生成的字符串都是malloc出来的,但是不要最后一个一个的free,因为backtrace_symbols是根据backtrace给出的call stack层数,一次性的malloc出来一块内存来存放结果字符串的,所以,只需要在最后,free backtrace_symbols的返回指针就OK了。这一点backtrace的manual中也是特别提到的。
2.0
使用signal函数,或者sigaction函数,在程序的最开始就设置好对应的信号的处理函数,这样程序在出错收到诸如
SIGSEGV(segmention)时,就会使用我们注册好的信号处理函数,也就是上面的backtrace的包装,输出栈。得到
函数调用关系
上demo:
运行以下程序,在另一个终端使用 kill -11 pid(demo程序的线程id) ,给demo发送 信号11 SIGSEGV
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <execinfo.h>
#include<signal.h>
typedef void (*sighandler_t)(int);//后续使用 sighandler_t sig :即定义一个 void (int) 函数指针
//
void show_backtrace()
{
void *trace[16];
char **messages = (char **)NULL;
int i, trace_size = 0;
trace_size = backtrace(trace, 16);
messages = backtrace_symbols(trace, trace_size);
fprintf(stderr, "Execution path:\n");
for (i = 0; i < trace_size; ++i)
{
fprintf(stderr, "[bt%02d] %s\n", i, messages[i]);
}
free(messages);
}
void Signal_Ignore(int signum)
{
printf("wang %d %s sigtype:%d \n",__LINE__,__FUNCTION__,signum);
}
static void Signal_showTrack(int signum)
{
printf("wang %d %s sigtype:%d \n",__LINE__,__FUNCTION__,signum);
show_backtrace();
}
int debug_info_init()
{
signal(SIGPIPE,Signal_Ignore);
signal(SIGKILL,Signal_Ignore); // the signal SIGKILL can not be ignore ,can not be handle
struct sigaction sigact={0};
sigact.sa_handler= Signal_Ignore;
sigaction(SIGUSR1,&sigact,NULL);
//注册对 SIGSEGV信号的处理
sigact.sa_handler = Signal_showTrack;
sigaction(SIGSEGV,&sigact,NULL);
return 0;
};
int main()
{
debug_info_init();
while(1)
{
}
return 0;
}