linux c 程序异常退出时打印堆栈调用信息

先来了解三个函数

#include <execinfo.h>

 int backtrace(void **buffer, int size);

 char **backtrace_symbols(void *const *buffer, int size);

 void backtrace_symbols_fd(void *const *buffer, int size, int fd);

int backtrace(void **buffer,int size)
   该函数用与获取当前线程的调用堆栈,获取的信息将会被存放在buffer中,它是一个指针数组。参数 size 用来指定buffer中可以保存多少个void* 元素。函数返回值是实际获取的指针个数,最大不超过size大小在buffer中的指针实际是从堆栈中获取的返回地址,每一个堆栈框架有一个返回地址。
    注意某些编译器的优化选项对获取正确的调用堆栈有干扰,另外内联函数没有堆栈框架;删除框架指针也会使无法正确解析堆栈内容
   char ** backtrace_symbols (void *const *buffer, int size)
    backtrace_symbols将从backtrace函数获取的信息转化为一个字符串数组. 参数buffer应该是从backtrace函数获取的数组指针,size是该数组中的元素个数(backtrace的返回值),函数返回值是一个指向字符串数组的指针,它的大小同buffer相同.每个字符串包含了一个相对于buffer中对应元素的可打印信息.它包括函数名,函数的偏移地址,和实际的返回地址
    现在,只有使用ELF二进制格式的程序和苦衷才能获取函数名称和偏移地址.在其他系统,只有16进制的返回地址能被获取.另外,你可能需要传递相应的标志给链接器,以能支持函数名功能(比如,在使用GNU ld的系统中,你需要传递(-rdynamic))
backtrace_symbols生成的字符串都是malloc出来的,但是不要最后一个一个的free,因为backtrace_symbols是根据backtrace给出的call stack层数,一次性的malloc出来一块内存来存放结果字符串的,所以,像上面代码一样,只需要在最后,free backtrace_symbols的返回指针就OK了。这一点backtrace的manual中也是特别提到的。
注意:如果不能为字符串获取足够的空间函数的返回值将会为NULL
    void backtrace_symbols_fd (void *const *buffer, int size, int fd)
    backtrace_symbols_fd与backtrace_symbols 函数具有相同的功能,不同的是它不会给调用者返回字符串数组,而是将结果写入文件描述符为fd的文件中,每个函数对应一行.它不需要调用malloc函数,因此适用于有可能调用该函数会失败的情况。

程序测试:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <execinfo.h>

void out_stack(char *sig);

void signal_exit(int dunno) 
{ 
	char* signal_str = "";
	char dunno_str[10] = {0};
	sprintf(dunno_str, "%d", dunno);
	switch (dunno) 
	{
		case 1:
			signal_str = "SIGHUP(1)";
			break;
		case 2:
			signal_str = "SIGINT(2:CTRL_C)"; //CTRL_C
			break;
		case 3:
			signal_str = "SIGQUIT(3)";
			break;
		case 6:
		{
			signal_str = "SIGABRT(6)";
			out_stack(signal_str);
		}
		break;
		case 9:
			signal_str = "SIGKILL(9)";
			break;
		case 15:
			signal_str = "SIGTERM(15 KILL)"; //kill 
			break;
		case 11:
		{
			signal_str = "SIGSEGV(11)"; //SIGSEGV 
			out_stack(signal_str);
		}
		break;	
		default:
			signal_str = "OTHER";
			break;
	}
	exit(0);
}

static void output_addrline(char addr[])
{
	char cmd[256];
	char line[256];
	char addrline[32]={0,};
	char *str1, *str2;
	FILE* file;

	str1 = strchr(addr,'[');
	str2 = strchr(addr, ']');
	if(str1 == NULL || str2 == NULL)
	{
		return;
	}	
	memcpy(addrline, str1 + 1, str2 -str1);
	snprintf(cmd, sizeof(cmd), "addr2line -e /proc/%d/exe %s ", getpid(), addrline);
	file = popen(cmd, "r");
	if(NULL != fgets(line, 256, file)) 
	{
		printf("%s\n", line);
	}
	pclose(file);	
}
void out_stack(char *sig)
{
	void *array[32];
	size_t size;
	char **strings;
	int i;
	
	printf("%s\n", sig);
	size = backtrace (array, 32);
	strings = backtrace_symbols (array, size);
	if (NULL == strings)
	{
		printf("backtrace_symbols\n");
		return ;
	}
	
	for (i = 0; i < size; i++)
	{
		printf("%s",strings[i]);
		output_addrline(strings[i]);	
	}
	
	free(strings);
}
void test3(int n)
{
	char *str;
	printf("in test3 [%d]\n", n);
	strcpy(str, "123");
}
void test2(int n)
{
	printf("in test2 [%d]\n", n);
	test3(3);
}
void test1(int n)
{
	printf("in test1 [%d]\n", n);
	test2(2);
}
int main()
{
	signal(SIGHUP, signal_exit); 
	signal(SIGINT, signal_exit);
	signal(SIGQUIT, signal_exit);
	signal(SIGABRT, signal_exit);
	signal(SIGKILL, signal_exit);
	signal(SIGTERM, signal_exit);
	signal(SIGSEGV, signal_exit);
	
	test1(1);
}

注意编译的时候要加-g 和 -rdynamic选项

猜你喜欢

转载自blog.csdn.net/u014608280/article/details/84974877
今日推荐