core和CallStack

core和CallStack

  最近项目开始集中测试了,服务器程序经常crash,由于服务器一般情况下都是关闭了core的,所以好几次都只能通过杂乱的日志来定位问题。
当然,我们可以通过ulimit来打开core开关,不过这可能带来新的问题:我们的服务器程序每个core文件大概有1G多,测试期间如果频繁crash,没有注意及时清理,一不小心就会把磁盘写满,
而且core文件毕竟是和进程程序相关的,有时候找相应版本也是个麻烦事。

能否在程序crash的时候,将callStack以及参数和局部变量都记录到日志里?
这个技术其实在游戏客户端已经用了很多年了,一般游戏客户端crash后,都会弹出一个是否发送错误的选择框,其实就是发送的CallStack的日志和MiniDUmp文件。
要想记录CallStack就必然涉及到Stack的遍历,linux下的Stack遍历使用很简单,简单的backtrace就可以搞定,man backtrace就有现成的例子,
这比windows下复杂的头疼的StackWalk好用的多。

解决了Stack遍历问题后,还剩下一个问题:如何在程序crash的时候得到通知执行我们自己的dump代码?
在Windwos下有SEH异常来实现这个功能,而linux下可以通过使用信号在进程crash的时候执行自己的处理代码。

好了,开始写个简单代码测试下:
首先设置几个主要crash信号的处理函数
signal(SIGSEGV, &DumpHelper::OnCrash);
signal(SIGABRT, &DumpHelper::OnCrash);
signal(SIGFPE, &DumpHelper::OnCrash);

在OnCrash里我们用前面提到的backtrace系列函数,来记录堆栈:
void* szStackFrame[100];
int nFrameCount = backtrace(szStackFrame, 100);
char** strFrameInfo = backtrace_symbols(szStackFrame, nFrameCount); 
char szDumpFileName[1024] = {0};
snprintf(szDumpFileName, sizeof(szDumpFileName), "dump_%u.log", (unsigned int)time(NULL) );
FILE* pFile = fopen(szDumpFileName, "wb");
if(!pFile) return;
for(int i = 0; i < nFrameCount; i++)
{
    fprintf(pFile, "%s\n", strFrameInfo[i]);
}
fclose(pFile);
free(strFrameInfo);

接着,设置几个嵌套调用的函数:
void fun()
{
 //assert(0);
 int* p = NULL;
 *p =3;
}

void fun1()
{
 fun();
}

void fun2()
{
 fun1();
}

void fun3()
{
 fun2();
}

最后,我们在main函数里执行fun3,注意编译的时候带上-rdynamic 选项。

运行下,果然可以打印基本的堆栈,不过马上,发现了新的问题:这个堆栈信息也太简陋了,只有调用函数的名字,其余的参数、局部变量完全没有,
这个和gdb能看到的callStack差距也太大了。
解决这个问题最简单的办法就是用gdb来打印堆栈,在这里,gdb和其他程序有区别,如果你试图通过 echo "bt"|gdb -p XXX>a.txt来获得堆栈,那将会非常失望,
根本不起作用,google了下,基本没什么解决办法。
不过gdb 可以从文件读入指令,例如 gdb XXX<cmddata,这给了我们机会,
system("echo  \"bt full|gcore\">testcmd");
  char dbx[160]={0};
     sprintf(dbx, "gdb -p %d ./main<testcmd >gdbdump_%d.log", getpid(), getpid() );
  system(dbx);

测试运行,发现可以打印详细的堆栈,不过,要求机器上有gdb.
上面的命令还dump了一个core文件,不过这个core文件的堆栈信息是错误的,我不知道为什么。。。。

多线程环境下使用上述办法,只能输出一个线程的堆栈,需要先获取线程数目,然后逐个线程打印堆栈。

最后,为了避免影响正常的coredump,要在OnCrash的处理函数里将信号的处理函数设置为默认。
如果我一定要有core呢,setrlimit吧,去掉core限制即可。

你可能感兴趣的:(core和CallStack)