程序崩潰時的堆棧捕捉


前述:

工作中,發現項目里的進程崩潰時,不會生成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應用到項目里試試吧。

 

轉載請注明出處!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM