Linux下使用backtrace捕获死机堆栈信息

Linux信号量

linux下常见的死机信号,如下:

信号名编号值描述系统默认处理方式
SIGINT2正常终止进程(ctrl + c)终止进程
SIGQUIT3异常终止进程终止进程,并允许产生core文件
SIGABRT6异常终止进程终止进程,并允许产生core文件
SIGBUS7异常终止进程,硬件错误终止进程,并允许产生core文件
SIGFPE8错误的算术运算,比如除数为0终止进程
SIGKILL9异常终止进程,非法指令终止进程
SIGSEGV11异常终止进程,访问无效的地址终止进程,并允许产生core文件
SIGPIPE13向没有读权限的管道文件写操作终止进程
SIGTERM15kill PID时默认发送的信号终止进程
SIGSTKFLT16Linux专用,数学协处理器的栈异常终止进程

代码捕获

static void print_maps(void)
{
	int pid = 0;
	FILE* fp = NULL;
	char cmd[64] = {0x00};
	char buff[512] = {0};
	
	pid = getpid();
	snprintf(cmd, sizeof(cmd),"cat /proc/%d/maps", pid); 
    fp = popen((const char*)cmd, "r");
	
	if(fp != NULL){
		printf("===========Catch errors, pid = %d===========\n", pid);
		while(fgets(buff, sizeof(buff), fp) != NULL)
		{
			printf("%s", buff);
		}
	}
	pclose(fp);
}

static void SystemErrorHandler(int signum)
{
	const int len = 64;
	void *func[len];
	size_t size;
	int i;
	char **error_strings;

	print_maps();
	signal(signum,SIG_DFL);
	size=backtrace(func,len);
	error_strings=(char**)backtrace_symbols(func,size);
	printf("=======System error [%s], Stack trace: size = %d==========\n",sys_siglist[signum],size);
	for(i=0;i<size;++i) printf("[%02d] %s \n",i,error_strings[i]);
	free(error_strings);
}

int catch_sigsegv_info(void)
{
	signal(SIGSEGV,SystemErrorHandler); //Invaild memory address
	signal(SIGABRT,SystemErrorHandler); // Abort signal
	signal(SIGILL,SystemErrorHandler);
	signal(SIGBUS,SystemErrorHandler);
	signal(SIGFPE,SystemErrorHandler);
	signal(SIGSTKFLT,SystemErrorHandler);
	signal(SIGPIPE,SystemErrorHandler);
	return 0;
}

其中print_maps打印的是进程运行时动态加载的maps映射,因为动态库在运行的时候才分配地址空间,每次运行时的地址范围都不一样。用backtrace打印出来的死机地址偏移,并不能直接用addr2line命令寻址找到位置,会提示??:?
必须通过maps的范围,找到正确的地址偏移,才能用addr2line正确定位到死机位置。

例如:
b6b58000-b6c99000 r-xp 00000000 b3:03 18443 /system/lib/libtest.so
b6c99000-b6ca8000 —p 00141000 b3:03 18443 /system/lib/libtest.so
b6ca8000-b6cc8000 rw-p 00140000 b3:03 18443 /system/lib/libtest.so
若死机地址是libtest.so(0xb6b59000), 那么用0xb6b59000 - 0xb6b58000 = 0x1000,然后用addr2liine -C -e libtest.so 0x1000 则可得到正确的死机位置

注意事项

  1. 编译的时候需要加上参数:-g -rdynamic
  2. 嵌入式系统还需要加上参数: -funwind-tables -ffunction-sections

打印信号字符串

  1. 字符串数组
    使用signal.h头文件下的字符串数组sys_siglist,将信号作为下标时,字符串就是对应的信号含义:
    #include <signal.h>
    extern const char * const sys_siglist[];

  2. psignal函数
    定义如下:
    #include <signal.h>
    void psignal (int signo, const char *msg);
    这个函数的使用同perror类似,例如,调用:
    psignal(13, “signal”);
    将会显示“signal: Broken pipe”

  3. strsignal函数
    与perror和strerror两个函数类似,有psignal,其实也有strsignal函数,定义如下:
    #include <string.h>
    char * strsignal (int signo);
    效果和strerror类似。需要注意这个函数返回的字符串指针仅在下一次调用strsignal前保持不变,所以这个函数不是线程安全的。

No pains, no gains.

Logo

更多推荐