coredump產生的幾種可能情況
造成程序coredump的原因有很多,這里總結一些比較常用的經驗吧:
1,內存訪問越界
a) 由於使用錯誤的下標,導致數組訪問越界。
b) 搜索字符串時,依靠字符串結束符來判斷字符串是否結束,但是字符串沒有正常的使用結束符。
c) 使用strcpy, strcat, sprintf, strcmp,strcasecmp等字符串操作函數,將目標字符串讀/寫爆。應該使用strncpy, strlcpy, strncat, strlcat, snprintf, strncmp, strncasecmp等函數防止讀寫越界。
2,多線程程序使用了線程不安全的函數。
應該使用下面這些可重入的函數,它們很容易被用錯:
asctime_r(3c) gethostbyname_r(3n) getservbyname_r(3n)ctermid_r(3s) gethostent_r(3n) getservbyport_r(3n) ctime_r(3c) getlogin_r(3c)getservent_r(3n) fgetgrent_r(3c) getnetbyaddr_r(3n) getspent_r(3c)fgetpwent_r(3c) getnetbyname_r(3n) getspnam_r(3c) fgetspent_r(3c)getnetent_r(3n) gmtime_r(3c) gamma_r(3m) getnetgrent_r(3n) lgamma_r(3m) getauclassent_r(3)getprotobyname_r(3n) localtime_r(3c) getauclassnam_r(3) etprotobynumber_r(3n)nis_sperror_r(3n) getauevent_r(3) getprotoent_r(3n) rand_r(3c) getauevnam_r(3)getpwent_r(3c) readdir_r(3c) getauevnum_r(3) getpwnam_r(3c) strtok_r(3c) getgrent_r(3c)getpwuid_r(3c) tmpnam_r(3s) getgrgid_r(3c) getrpcbyname_r(3n) ttyname_r(3c)getgrnam_r(3c) getrpcbynumber_r(3n) gethostbyaddr_r(3n) getrpcent_r(3n)
3,多線程讀寫的數據未加鎖保護。
對於會被多個線程同時訪問的全局數據,應該注意加鎖保護,否則很容易造成coredump
4,非法指針
a) 使用空指針
b) 隨意使用指針轉換。一個指向一段內存的指針,除非確定這段內存原先就分配為某種結構或類型,或者這種結構或類型的數組,否則不要將它轉換為這種結構或類型的指針,而應該將這段內存拷貝到一個這種結構或類型中,再訪問這個結構或類型。這是因為如果這段內存的開始地址不是按照這種結構或類型對齊的,那么訪問它時就很容易因為bus error而core dump。
5,堆棧溢出
不要使用大的局部變量(因為局部變量都分配在棧上),這樣容易造成堆棧溢出,破壞系統的棧和堆結構,導致出現莫名其妙的錯誤。
六,利用gdb進行coredump的定位
其實分析coredump的工具有很多,現在大部分類unix系統都提供了分析coredump文件的工具,不過,我們經常用到的工具是gdb。
這里我們以程序為例子來說明如何進行定位。
1, 段錯誤 – segmentfault
Ø 我們寫一段代碼往受到系統保護的地址寫內容。
Ø 按如下方式進行編譯和執行,注意這里需要-g選項編譯。
從紅色方框截圖可以看到,程序中止是因為信號11,且從bt(backtrace)命令(或者where)可以看到函數的調用棧,即程序執行到coremain.cpp的第5行,且里面調用scanf 函數,而該函數其實內部會調用_IO_vfscanf_internal()函數。
接下來我們繼續用gdb,進行調試對應的程序。
記住幾個常用的gdb命令:
l(list) ,顯示源代碼,並且可以看到對應的行號;
b(break)x, x是行號,表示在對應的行號位置設置斷點;
p(print)x, x是變量名,表示打印變量x的值
r(run), 表示繼續執行到斷點的位置
n(next),表示執行下一步
c(continue),表示繼續執行
q(quit),表示退出gdb
啟動gdb,注意該程序編譯需要-g選項進行。
注: SIGSEGV 11 Core Invalid memoryreference
七,附注:
1, gdb的查看源碼
顯示源代碼
GDB 可以打印出所調試程序的源代碼,當然,在程序編譯時一定要加上-g的參數,把源程序信息編譯到執行文件中。不然就看不到源程序了。當程序停下來以后,GDB會報告程序停在了那個文件的第幾行上。你可以用list命令來打印程序的源代碼。還是來看一看查看源代碼的GDB命令吧。
list<linenum>
顯示程序第linenum行的周圍的源程序。
list<function>
顯示函數名為function的函數的源程序。
list
顯示當前行后面的源程序。
list -
顯示當前行前面的源程序。
一般是打印當前行的上5行和下5行,如果顯示函數是是上2行下8行,默認是10行,當然,你也可以定制顯示的范圍,使用下面命令可以設置一次顯示源程序的行數。
setlistsize <count>
設置一次顯示源代碼的行數。
showlistsize
查看當前listsize的設置。
list命令還有下面的用法:
list<first>, <last>
顯示從first行到last行之間的源代碼。
list ,<last>
顯示從當前行到last行之間的源代碼。
list +
往后顯示源代碼。
一般來說在list后面可以跟以下這些參數:
<linenum> 行號。
<+offset> 當前行號的正偏移量。
<-offset> 當前行號的負偏移量。
<filename:linenum> 哪個文件的哪一行。
<function> 函數名。
<filename:function>哪個文件中的哪個函數。
<*address> 程序運行時的語句在內存中的地址。
2, 一些常用signal的含義
SIGABRT:調用abort函數時產生此信號。進程異常終止。
SIGBUS:指示一個實現定義的硬件故障。
SIGEMT:指示一個實現定義的硬件故障。EMT這一名字來自PDP-11的emulator trap 指令。
SIGFPE:此信號表示一個算術運算異常,例如除以0,浮點溢出等。
SIGILL:此信號指示進程已執行一條非法硬件指令。4.3BSD由abort函數產生此信號。SIGABRT現在被用於此。
SIGIOT:這指示一個實現定義的硬件故障。IOT這個名字來自於PDP-11對於輸入/輸出TRAP(input/outputTRAP)指令的縮寫。系統V的早期版本,由abort函數產生此信號。SIGABRT現在被用於此。
SIGQUIT:當用戶在終端上按退出鍵(一般采用Ctrl-/)時,產生此信號,並送至前台進
程組中的所有進程。此信號不僅終止前台進程組(如SIGINT所做的那樣),同時產生一個core文件。
SIGSEGV:指示進程進行了一次無效的存儲訪問。名字SEGV表示“段違例(segmentationviolation)”。
SIGSYS:指示一個無效的系統調用。由於某種未知原因,進程執行了一條系統調用指令,但其指示系統調用類型的參數卻是無效的。
SIGTRAP:指示一個實現定義的硬件故障。此信號名來自於PDP-11的TRAP指令。
SIGXCPUSVR4和4.3+BSD支持資源限制的概念。如果進程超過了其軟C P U時間限制,則產生此信號。
SIGXFSZ:如果進程超過了其軟文件長度限制,則SVR4和4.3+BSD產生此信號。
3, Core_pattern的格式
可以在core_pattern模板中使用變量還很多,見下面的列表:
%% 單個%字符
%p 所dump進程的進程ID
%u 所dump進程的實際用戶ID
%g 所dump進程的實際組ID
%s 導致本次core dump的信號
%t core dump的時間 (由1970年1月1日計起的秒數)
%h 主機名
%e 程序文件名
