程序崩溃时的堆栈捕捉

前述:

工作中,发现项目里的进程崩溃时,不会生成core文件,排查顺序:1、查看core文件的生成路径:cat /proc/sys/kernel/core_pattern; 2、查看core信息设置的是否正确:ulimit -a。

经过排查后,我发现我本地的环境没有问题,还写过demo测试,能生成core的。

core的一些设置,参考:https://blog.csdn.net/lanmolei814/article/details/45201693

本篇主要解决了两个问题:1、为什么没有core文件生成;2、为什么栈溢出的时候没有堆栈日志。

发现问题1:

1、我发现项目的代码有添加信号捕捉的功能(可以参考下面的代码CrashHandler);

2、每次捕捉后,确实有堆栈日志输出的,这个功能是正常的;

3、那么问题出在哪里呢?

答:我在调试的时候,发现每次在打印完堆栈日志,程序就安全的退出了,不会有core文件生成,当时就怀疑是exit(-1)导致的,所以自己肯定会测试下exit(signo),但这会导致可怕的事故发生,那就是程序死循环了,一直打印堆栈日志,不会正常退出。当然,在没有core文件的情况下,如果定位bug,我也研究了的。(因为还有堆栈日志可以使用,所以直接能看到崩溃的函数调用栈,但难受的是:生成环境是release 不加 -g的版本,也就是没有符号文件;所以我得利用nm找到函数地址,然后再gdb反汇编那个函数地址,通过偏移定位到引起崩溃的那行代码)

4、解决方案:使用代码注释的方法2。

分析:

crash_signal.cpp 代码:

#include<iostream>
#include<stdlib.h>
#include <signal.h>       /* for signal */
#include <execinfo.h>     /* for backtrace() */
using namespace std;
void CrashHandler(int signo) {
    cout << "crash sig:" << signo;

    int size = 16;
    void * array[16];
    int stack_num = backtrace(array, size);
    char ** stacktrace = backtrace_symbols(array, stack_num);
    for (int i = 0; i < stack_num; ++i)
    {
        cout << stacktrace[i] << endl;
    }

    free(stacktrace);

    exit(-1);//方法1:程序就直接退出了,不会有生成core文件生成

    //signal(signo, SIG_DFL);//方法2: /* 恢复信号默认处理 */
    //raise(signo);                   /* 重新发送信号 */
}

int fun(int n)
{
    if (n <= 0)
        return 0;
{//测试1
    int a = 0;
    n = n / a;//目的:测试信号SIGFPE
  return 0;
} {//测试2 int nLen = 10240; char arr[10240] = { 0 }; arr[0] = 0x0; arr[1] = 0x0; arr[2] = 0x1; arr[3] = 0x1; return 1 + fun(n - 1) + *(arr + 3);//这会造成栈溢出,ulimit -s可以查看系统的栈大小,当然也可以手动设置
}
} int fun_stack_overflow() { int nTotal = fun(1024); return nTotal; } int main() { signal(SIGABRT, CrashHandler); signal(SIGFPE, CrashHandler); signal(SIGILL, CrashHandler); signal(SIGSEGV, CrashHandler); int nSum = fun_stack_overflow(); cout << nSum << endl; return 0; }

编译:g++ -g crash_signal.cpp -o crash.out

测试1:(当前使用的是测试1代码噢)

1)、测试方法1:gdb crash.out打开,break CrashHandler下一个断点,run运行程序。

 测试结果:有堆栈日志,但程序退出时,没有core文件生成。注:图中第一次中断是gdb捕捉的,第二次是程序里的捕捉。

2)、改善为方法2,退出程序。

 测试结果:有core文件生成,有堆栈日志输出。注:图中第一次中断时gdb捕捉的,第二次是程序的中断,第三次是raise返回给程序的。

测试2:(使用的测试2代码,方法1和2不限)

 1)测试结果,有core文件,但是没有堆栈日志。注:第一次中断是gdb捕捉的,第二次应该是gdb返回给程序的?

分析测试2:测试的崩溃原因是堆栈溢出了,即就是递归导致栈满了。但为何程序的signal信号没有捕捉到堆栈满的错误呢,gdb返回的是段错误呀,我猜测:低版本的gdb可能也无法回溯出堆栈信息吧,这个还得继续研究。

以上就是本次的研究成果,把方法2应用到项目里试试吧。

转载请注明出处!

猜你喜欢

转载自www.cnblogs.com/mingbujian/p/12676217.html
今日推荐