1. coredump 產生的原理和侷限
1.1. 如何產生 core 文件
要素一,必須有信號產生:
從上面的信號定義和說明可以看出,進程中止前肯定會產生信號,然後內核根據信號的類型來決定是否要產生 core 文件。
要素二,編譯器支持:
要產生 core 文件,編譯器必須支持把當前進程的鏡像以某種格式 dump 到一個文件中,常見的比如 gcc/g++ 的 -g 選項。
要素三,環境參數支持:
通過 ulimit –a 查看 core file size 是否爲 0 ,如果爲 0 則不能產生 core 文件。
通過 ulimit –c unlimited 可以系統能支持的產生足夠大的 core 文件,也可以設置爲具體值。
特別說明:
core 文件的產生不是 POSIX.1 所屬部分,而是很多 UNIX/Linux 版本的實現特徵。
1.2. Unix/Linux 對信號的處理方式
UNIX System signals |
||||||||
Name |
Description |
ISO C |
SUS |
FreeBSD 5.2.1 |
Linux 2.4.22 |
Mac OS X 10.3 |
Solaris 9 |
Default action |
SIGABRT |
abnormal termination (abort ) |
• |
• |
• |
• |
• |
• |
terminate+core |
SIGALRM |
timer expired (alarm ) |
|
• |
• |
• |
• |
• |
terminate |
SIGBUS |
hardware fault |
|
• |
• |
• |
• |
• |
terminate+core |
SIGCANCEL |
threads library internal use |
|
|
|
|
|
• |
ignore |
SIGCHLD |
change in status of child |
|
• |
• |
• |
• |
• |
ignore |
SIGCONT |
Continue stopped process |
|
• |
• |
• |
• |
• |
continue/ignore |
SIGEMT |
hardware fault |
|
|
• |
• |
• |
• |
terminate+core |
SIGFPE |
arithmetic exception |
• |
• |
• |
• |
• |
• |
terminate+core |
SIGFREEZE |
checkpoint freeze |
|
|
|
|
|
• |
ignore |
SIGHUP |
hangup |
|
• |
• |
• |
• |
• |
terminate |
SIGILL |
illegal instruction |
• |
• |
• |
• |
• |
• |
terminate+core |
SIGINFO |
status request from keyboard |
|
|
• |
|
• |
|
ignore |
SIGINT |
terminal interrupt character |
• |
• |
• |
• |
• |
• |
terminate |
SIGIO |
asynchronous I/O |
|
|
• |
• |
• |
• |
terminate/ignore |
SIGIOT |
hardware fault |
|
|
• |
• |
• |
• |
terminate+core |
SIGKILL |
termination |
|
• |
• |
• |
• |
• |
terminate |
SIGLWP |
threads library internal use |
|
|
|
|
|
• |
ignore |
SIGPIPE |
write to pipe with no readers |
|
• |
• |
• |
• |
• |
terminate |
SIGPOLL |
pollable event (poll ) |
|
XSI |
|
• |
|
• |
terminate |
SIGPROF |
profiling time alarm (setitimer ) |
|
XSI |
• |
• |
• |
• |
terminate |
SIGPWR |
power fail/restart |
|
|
|
• |
|
• |
terminate/ignore |
SIGQUIT |
terminal quit character |
|
• |
• |
• |
• |
• |
terminate+core |
SIGSEGV |
invalid memory reference |
• |
• |
• |
• |
• |
• |
terminate+core |
SIGSTKFLT |
coprocessor stack fault |
|
|
|
• |
|
|
terminate |
SIGSTOP |
stop |
|
• |
• |
• |
• |
• |
stop process |
SIGSYS |
invalid system call |
|
XSI |
• |
• |
• |
• |
terminate+core |
SIGTERM |
termination |
• |
• |
• |
• |
• |
• |
terminate |
SIGTHAW |
checkpoint thaw |
|
|
|
|
|
• |
ignore |
SIGTRAP |
hardware fault |
|
XSI |
• |
• |
• |
• |
terminate+core |
SIGTSTP |
terminal stop character |
|
• |
• |
• |
• |
• |
stop process |
SIGTTIN |
background read from control tty |
|
• |
• |
• |
• |
• |
stop process |
SIGTTOU |
background write to control tty |
|
• |
• |
• |
• |
• |
stop process |
SIGURG |
urgent condition (sockets) |
|
• |
• |
• |
• |
• |
ignore |
SIGUSR1 |
user-defined signal |
|
• |
• |
• |
• |
• |
terminate |
SIGUSR2 |
user-defined signal |
|
• |
• |
• |
• |
• |
terminate |
SIGVTALRM |
virtual time alarm (setitimer ) |
|
XSI |
• |
• |
• |
• |
terminate |
SIGWAITING |
threads library internal use |
|
|
|
|
|
• |
ignore |
SIGWINCH |
terminal window size change |
|
|
• |
• |
• |
• |
ignore |
SIGXCPU |
CPU limit exceeded (setrlimit ) |
|
XSI |
• |
• |
• |
• |
terminate+core/ignore |
SIGXFSZ |
file size limit exceeded (setrlimit ) |
|
XSI |
• |
• |
• |
• |
terminate+core/ignore |
SIGXRES |
resource control exceeded |
|
|
|
|
|
• |
ignore |
2. core 文件的使用
3. core 文件的侷限
core 文件提供了當前進程的所有線程的堆棧信息,但是也有他的侷限性。
由於 core 是對當前進程地址空間的鏡像,所以 core 文件一般比較巨大,特別是針對服務器程序。
這樣如果服務器程序自動重啓幾次,可能就會導致磁盤空間佔滿。
另外,由於 core 文件巨大,不刪除的話佔用大量磁盤空間,下載下來又比較費時。
對於緩衝區溢出導致的 coredump ,進程的調用堆棧已經被覆蓋破壞了, core 文件顯示的堆棧信息往往錯誤。
一些信號導致進程崩潰,但是不產生 core 文件,比如 SIGPIPE 。
只能在進程中止時纔可以產生 core, 不能實時產生。
4. 堆棧打印
4.1. 如何打印堆棧
1 、通過backtrace 和backtrace_symbols 或backtrace_symbols_fd 實現當前線程的堆棧打印。
堆棧打印的一個簡單示例:
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
/* Obtain a backtrace and print it to stdout. */
void print_trace (void)
{
void *array[10];
size_t size;
size_t i;
size = backtrace (array, 10);
printf ("Obtained %zd stack frames./n", size);
for (i = 0; i < size; i++)
printf ("% p /n", array [i]);
}
/* A dummy function to make the backtrace more interesting. */
void dummy_function (void)
{
print_trace ();
}
int main (void)
{
dummy_function ();
return 0;
}
2 、編譯時必須加上 -rdynamic –ldl 選項。
3 、如果要打印崩潰線程的堆棧信息,則必須在信號處理函數中打印堆棧。注意要通過 setjmp/longjmp 或 sigsetjmp/siglongjmp 來跳轉,否則會陷於死循環。因爲信號是軟件中斷,處理完後會返回到發生異常的地方繼續執行,這樣又會重新產生異常。
4 、我寫了一個 coredump 組件,支持打印崩潰線程的堆棧信息和寄存器變量,捕捉 SIGINT 、 SIGTERM 、 SIGFPE 、 SIGABRT SIGSEGV 等信號引起的異常崩潰,打印信息輸出到一個文本文件。該組件將會放到 SGDP 中供項目組選用。具體使用方法請看 readme 和 example 。
4.2. 打印堆棧的優缺點
優點: 1 、不依賴 core 文件。
2 、可以打印當前崩潰線程的堆棧調用信息。
3 、輸出到文件佔用的磁盤空間很小。
缺點:
1、 只能打印當前線程的堆棧信息。
2、 只能打印堆棧調用信息。
3、 不能打印當前線程的所有變量值。
4、 輸出函數名沒有 gdb 的 core 文件可讀性好。
5、 對於 abort 異常定位,需要和 nm 等代碼分析工具配合使用。
6、
5. GCC/G++ 對內存錯誤的處理
GCC 對內存錯誤的檢查,在不同版本都有一定的體現。
GCC 對內存錯誤的檢查主要依靠 3 個選項: -fstack-check -fbounds-check -fstack-protector-all 。
其中 -fstack-check 的準確性較差,特別是在代碼執行了 O2 以上的優化後。
-fbounds-check 可以執行數組邊界錯誤,主要是檢查對數組賦值越界。
-fstack-protector-all 可以防止緩衝區溢出攻擊,也可以利用此選項來檢查代碼是否有緩衝區溢出的錯誤。
GCC 4.1 以前的版本支持 -fstack-check -fbounds-check , GCC 4.1 以後版本支持 -fstack-check -fbounds-check -fstack-protector-all 。
GCC 4.1 以前的版本對於緩衝區溢出檢查沒有很好的解決方案,如果調用堆棧被破壞的不多還有可能定位出錯函數的位置,否則只是一個錯誤的堆棧信息甚至無信息。
-fstack-protector-all 選項加上後會在標準輸出上打印當前崩潰線程的堆棧調用信息,但是有時也不準確,同時產生 core 文件, core 文件中的出錯堆棧信息相對比較準確性。
6. SGDP 的 coredump 組件
我們自己開發了一個 Linux 版打印進程崩潰堆棧信息的 coredump 組件,放在 SGDP 的工具裏面。
coredump 組件支持打印崩潰線程的堆棧信息和寄存器變量,捕捉SIGINT 、SIGTERM 、SIGFPE 、SIGABRT 和SIGSEGV 等信號引起的異常崩潰,打印信息輸出到一個文本文件。
使用方法:1 、代碼中包含coredump.h 。
2 、在main 函數開始的地方調用setup_sigcoredump() 。
如果大家需要的話,可以聯繫服務器引擎部,提供二進制版本的庫和示例代碼。
7. Google coredumper
7.1. 項目介紹
在進程不中止的情況下可以產生 core 文件,該 core 文件可以使用 gdb 調試,對於多線程調試有重要意義。
在產生 core 文件時會掛起所有線程,所以是線程安全的。
版權聲明爲 BSD 授權。
當前最新版本爲 1.2.1 。
項目鏈接: http://code.google.com/p/google-coredumper/
7.2. 安裝方法
與標準的開源項目基本上差不多: ./configure 、 make 、 make install 。
需要注意的是編譯生成的庫在當前目錄和標準目錄中都沒有,具體需要查看 libcoredumper.la 描述信息,方法: vi libcoredumper.la 。
否則有可能找不到動態庫或靜態庫。
7.3. 使用方法
1、 包含 src 目錄下的 google/coredumper.h 。
2、 調用 GetCoreDump 或 WriteCoreDump 等系列接口生成 core 文件。
3、 在 coredumper.h 中大部分函數都有 man 幫助。
4、 特別注意的是 WriteCoreDump 實際上是調用 GetCoreDump ,語義上好像是有點問題,看接口名稱是有點困惑,我是看代碼才發現的。
5、 可以指定預定義的壓縮器來生成壓縮的 core 文件,也可以自定義壓縮算法生成 core 文件。
6 、