嵌入式小专题--代码出错时栈信息自输出的实现

功能:程序出错时,比如某些端错误等等,有时候为排查这种错误,会采用加打印信息的方式,去逐个排查,如果存在两个版本(比如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;
}

猜你喜欢

转载自blog.csdn.net/u012459903/article/details/80566108
今日推荐