28 august 2011
寫c/c++程式經常要直接和記憶體打交道,一不小心就會造成程式執行時產生segment fault而掛掉。一般這種情況都是因為陣列越界訪問,空指標或是野指標讀寫造成的。程式小的話還比較好辦,對著源**仔細檢查就能解決。但是對於**量較大的程式,裡邊包含n多函式呼叫,n多陣列指標訪問,這時想定位問題就不是很容易了(此時牛人依然可以通過在適當位置打printf加二分查詢的方式迅速定位:p)。懶人的話還是直接gdb搞起吧。
神馬是core dump檔案
偶爾就能聽見某程式設計師同學抱怨: 「擦,又出core了!」。簡單來說,core dump說的是作業系統執行的乙個動作,當某個程序因為一些原因意外終止(crash)的時候,作業系統會將這個程序當時的記憶體資訊轉儲(dump)到磁碟上1。產生的檔案就是core檔案了,一般會以core.***形式命名。
如何產生core dump
發生doredump一般都是在程序收到某個訊號的時候,linux上現在大概有60多個訊號,可以使用 kill -l 命令全部列出來。
$ kill -l
1) sighup 2) sigint 3) sigquit 4) sigill 5) sigtrap
6) sigabrt 7) sigbus 8) sigfpe 9) sigkill 10) sigusr1
11) sigsegv 12) sigusr2 13) sigpipe 14) sigalrm 15) sigterm
16) sigstkflt 17) sigchld 18) sigcont 19) sigstop 20) sigtstp
21) sigttin 22) sigttou 23) sigurg 24) sigxcpu 25) sigxfsz
26) sigvtalrm 27) sigprof 28) sigwinch 29) sigio 30) sigpwr
31) sigsys 34) sigrtmin 35) sigrtmin+1 36) sigrtmin+2 37) sigrtmin+3
38) sigrtmin+4 39) sigrtmin+5 40) sigrtmin+6 41) sigrtmin+7 42) sigrtmin+8
43) sigrtmin+9 44) sigrtmin+10 45) sigrtmin+11 46) sigrtmin+12 47) sigrtmin+13
48) sigrtmin+14 49) sigrtmin+15 50) sigrtmax-14 51) sigrtmax-13 52) sigrtmax-12
53) sigrtmax-11 54) sigrtmax-10 55) sigrtmax-9 56) sigrtmax-8 57) sigrtmax-7
58) sigrtmax-6 59) sigrtmax-5 60) sigrtmax-4 61) sigrtmax-3 62) sigrtmax-2
63) sigrtmax-1 64) sigrtmax
針對特定的訊號,應用程式可以寫對應的訊號處理函式。如果不指定,則採取預設的處理方式, 預設處理是coredump的訊號如下:
3)sigquit 4)sigill 6)sigabrt 8)sigfpe 11)sigsegv 7)sigbus 31)sigsys
5)sigtrap 24)sigxcpu 25)sigxfsz 29)sigiot
我們看到sigsegv在其中,一般陣列越界或是訪問空指標都會產生這個訊號。另外雖然預設是這樣的,但是你也可以寫自己的訊號處理函式改變預設行為,更多訊號相關可以看參考鏈結33。
上述內容只是產生coredump的必要條件,而非充分條件。要產生core檔案還依賴於程式執行的shell,可以通過ulimit -a命令檢視,輸出內容大致如下:
$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 20
file size (blocks, -f) unlimited
pending signals (-i) 16382
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
posix message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) unlimited
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
看到第一行了吧,core file size,這個值用來限制產生的core檔案大小,超過這個值就不會儲存了。我這裡輸出是0,也就是不會儲存core檔案,即使產生了,也儲存不下來==! 要改變這個設定,可以使用ulimit -c unlimited。
ok, 現在萬事具備,只缺乙個能產生core的程式了,介個對c程式設計師來說太容易了。
#include ;
#include ;
intcrash(
)int
foo(
)int
main(
)
上手除錯
上邊的程式編譯的時候有一點需要注意,需要帶上引數-g, 這樣生成的可執行程式中會帶上足夠的除錯資訊。編譯執行之後你就應該能看見期待已久的「segment fault(core dumped)」或是「段錯誤 (核心已轉儲)」之類的字眼了。看看當前目錄下是不是有個core或是core.***的檔案。祭出linux下經典的偵錯程式gdb,首先帶著core檔案引導程式:gdb exefile core,這裡需要注意的這個core檔案必須是exefile產生的,否則符號表會對不上。載入之後大概是這個樣子的:
$ gdb coredump core
core was generated by ./coredump'.
program terminated with signal 11, segmentation fault.
#0 0x080483a7 in crash () at coredump.c:8
8 ***[1] = 'd';
(gdb)
我們看到已經能直接定位到出core的地方了,在第8行寫了乙個唯讀的記憶體區域導致觸發segment fault訊號。在載入core的時候有個小技巧,如果你事先不知道這個core檔案是由哪個程式產生的,你可以先隨便找個代替一下,比如/usr/bin/w就是不錯的選擇。比如我們採用這種方法載入上邊產生的core,gdb會有類似的輸出:
$ gdb /usr/bin/w core
core was generated by ./coredump'.
program terminated with signal 11, segmentation fault.
#0 0x080483a7 in ?? ()
(gdb)
可以看到gdb已經提示你了,這個core是由哪個程式產生的。
gdb 常用操作
上邊的程式比較簡單,不需要另外的操作就能直接找到問題所在。現實卻不是這樣的,常常需要進行單步跟蹤,設定斷點之類的操作才能順利定位問題。下邊列出了gdb一些常用的操作。
bt這個命令重點推薦,尤其是當函式巢狀很深,呼叫關係複雜的時候,他能夠顯示出整個函式的呼叫堆疊,呼叫關係一目了然。另外上邊有兩個單步執行的命令,乙個是n,乙個是s。主要區別是n會將函式呼叫當成一步執行,而s會跟進呼叫函式內部。
參考zhpengg
GDB除錯coredump檔案
linux上程式崩潰起來挺煩人,不過linux 比較好的是有gdb.echo ulimit c unlimited etc profile 然後記得敲入命令 source etc profile然後敲入命令 ulimit c效果如下 確認能否生成coredump檔案,使用如下命令 使用時注意,我在測...
gdb除錯coredump檔案
linux上程式崩潰起來挺煩人,不過linux 比較好的是有gdb.echo ulimit c unlimited etc profile 然後記得敲入命令 source etc profile然後敲入命令 ulimit c效果如下 確認能否生成coredump檔案,使用如下命令 使用時注意,我在測...
g 編譯 gdb除錯 coredump除錯
1.使用g 編譯cpp檔案如果用gcc編譯c 原始檔時,加以下選項 lstdc 否則使用了c 操作的檔案編譯會出錯。2.gcc g 在執行編譯時,需要4步 預處理,生成.i的檔案 使用 e引數 將預處理後的檔案把轉換成組合語言,生成檔案.s 使用 s引數 由彙編變為目標 機器 生成.o的檔案 使用 ...