前述:
工作中,發現項目里的進程崩潰時,不會生成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應用到項目里試試吧。
轉載請注明出處!