Linux下使用backtrace捕获死机堆栈信息
Linux下使用backtrace捕获死机堆栈信息Linux信号量代码捕获注意事项打印信号字符串Linux信号量linux下常见的死机信号,如下:信号名编号值描述系统默认处理方式SIGINT2正常终止进程(ctrl + c)终止进程SIGQUIT3异常终止进程终止进程,并允许产生core文件SIGABRT6异常终止进程终止进程,并允许产生core文件SIGBUS7异常终止进程,硬件错误终止进程,并
Linux信号量
linux下常见的死机信号,如下:
信号名 | 编号值 | 描述 | 系统默认处理方式 |
---|---|---|---|
SIGINT | 2 | 正常终止进程(ctrl + c) | 终止进程 |
SIGQUIT | 3 | 异常终止进程 | 终止进程,并允许产生core文件 |
SIGABRT | 6 | 异常终止进程 | 终止进程,并允许产生core文件 |
SIGBUS | 7 | 异常终止进程,硬件错误 | 终止进程,并允许产生core文件 |
SIGFPE | 8 | 错误的算术运算,比如除数为0 | 终止进程 |
SIGKILL | 9 | 异常终止进程,非法指令 | 终止进程 |
SIGSEGV | 11 | 异常终止进程,访问无效的地址 | 终止进程,并允许产生core文件 |
SIGPIPE | 13 | 向没有读权限的管道文件写操作 | 终止进程 |
SIGTERM | 15 | kill PID时默认发送的信号 | 终止进程 |
SIGSTKFLT | 16 | Linux专用,数学协处理器的栈异常 | 终止进程 |
代码捕获
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 则可得到正确的死机位置
注意事项
- 编译的时候需要加上参数:-g -rdynamic
- 嵌入式系统还需要加上参数: -funwind-tables -ffunction-sections
打印信号字符串
-
字符串数组
使用signal.h头文件下的字符串数组sys_siglist,将信号作为下标时,字符串就是对应的信号含义:
#include <signal.h>
extern const char * const sys_siglist[]; -
psignal函数
定义如下:
#include <signal.h>
void psignal (int signo, const char *msg);
这个函数的使用同perror类似,例如,调用:
psignal(13, “signal”);
将会显示“signal: Broken pipe” -
strsignal函数
与perror和strerror两个函数类似,有psignal,其实也有strsignal函数,定义如下:
#include <string.h>
char * strsignal (int signo);
效果和strerror类似。需要注意这个函数返回的字符串指针仅在下一次调用strsignal前保持不变,所以这个函数不是线程安全的。
No pains, no gains.
更多推荐
所有评论(0)