詳解coredump

一,什麼是coredump

        我們經常聽到大家說到程序core掉了,需要定位解決,這裏說的大部分是指對應程序由於各種異常或者bug導致在運行過程中異常退出或者中止,並且在滿足一定條件下(這裏爲什麼說需要滿足一定的條件呢?下面會分析)會產生一個叫做core的文件。

        通常情況下,core文件會包含了程序運行時的內存,寄存器狀態,堆棧指針,內存管理信息還有各種函數調用堆棧信息等,我們可以理解爲是程序工作當前狀態存儲生成第一個文件,許多的程序出錯的時候都會產生一個core文件,通過工具分析這個文件,我們可以定位到程序異常退出的時候對應的堆棧調用等信息,找出問題所在並進行及時解決。


二,coredump文件的存儲位置

   core文件默認的存儲位置與對應的可執行程序在同一目錄下,文件名是core,大家可以通過下面的命令看到core文件的存在位置:

   cat  /proc/sys/kernel/core_pattern

   缺省值是core

 

注意:這裏是指在進程當前工作目錄的下創建。通常與程序在相同的路徑下。但如果程序中調用了chdir函數,則有可能改變了當前工作目錄。這時core文件創建在chdir指定的路徑下。有好多程序崩潰了,我們卻找不到core文件放在什麼位置。和chdir函數就有關係。當然程序崩潰了不一定都產生 core文件。

如下程序代碼:則會把生成的core文件存儲在/data/coredump/wd,而不是大家認爲的跟可執行文件在同一目錄。

 

通過下面的命令可以更改coredump文件的存儲位置,若你希望把core文件生成到/data/coredump/core目錄下:

   echo “/data/coredump/core”> /proc/sys/kernel/core_pattern

 

注意,這裏當前用戶必須具有對/proc/sys/kernel/core_pattern的寫權限。

 

缺省情況下,內核在coredump時所產生的core文件放在與該程序相同的目錄中,並且文件名固定爲core。很顯然,如果有多個程序產生core文件,或者同一個程序多次崩潰,就會重複覆蓋同一個core文件,因此我們有必要對不同程序生成的core文件進行分別命名。

 

我們通過修改kernel的參數,可以指定內核所生成的coredump文件的文件名。例如,使用下面的命令使kernel生成名字爲core.filename.pid格式的core dump文件:

echo “/data/coredump/core.%e.%p” >/proc/sys/kernel/core_pattern

這樣配置後,產生的core文件中將帶有崩潰的程序名、以及它的進程ID。上面的%e和%p會被替換成程序文件名以及進程ID。

如果在上述文件名中包含目錄分隔符“/”,那麼所生成的core文件將會被放到指定的目錄中。 需要說明的是,在內核中還有一個與coredump相關的設置,就是/proc/sys/kernel/core_uses_pid。如果這個文件的內容被配置成1,那麼即使core_pattern中沒有設置%p,最後生成的core dump文件名仍會加上進程ID。

三,如何判斷一個文件是coredump文件?

在類unix系統下,coredump文件本身主要的格式也是ELF格式,因此,我們可以通過readelf命令進行判斷。

   

     可以看到ELF文件頭的Type字段的類型是:CORE (Core file)

     可以通過簡單的file命令進行快速判斷:     

四,產生coredum的一些條件總結

1,  產生coredump的條件,首先需要確認當前會話的ulimit –c,若爲0,則不會產生對應的coredump,需要進行修改和設置。

ulimit  -c unlimited  (可以產生coredump且不受大小限制)

 

若想甚至對應的字符大小,則可以指定:

ulimit –c [size]

               

       可以看出,這裏的size的單位是blocks,一般1block=512bytes

        如:

        ulimit –c 4  (注意,這裏的size如果太小,則可能不會產生對應的core文件,筆者設置過ulimit –c 1的時候,系統並不生成core文件,並嘗試了1,2,3均無法產生core,至少需要4才生成core文件)

       

但當前設置的ulimit只對當前會話有效,若想系統均有效,則需要進行如下設置:

Ø  在/etc/profile中加入以下一行,這將允許生成coredump文件

ulimit-c unlimited

Ø  在rc.local中加入以下一行,這將使程序崩潰時生成的coredump文件位於/data/coredump/目錄下:

echo /data/coredump/core.%e.%p> /proc/sys/kernel/core_pattern 

注意rc.local在不同的環境,存儲的目錄可能不同,susu下可能在/etc/rc.d/rc.local

      更多ulimit的命令使用,可以參考:http://baike.baidu.com/view/4832100.htm

      這些需要有root權限, 在ubuntu下每次重新打開中斷都需要重新輸入上面的ulimit命令, 來設置core大小爲無限.

2, 當前用戶,即執行對應程序的用戶具有對寫入core目錄的寫權限以及有足夠的空間。

3, 幾種不會產生core文件的情況說明:

The core file will not be generated if

(a)    the process was set-user-ID and the current user is not the owner of the program file, or

(b)     the process was set-group-ID and the current user is not the group owner of the file,

(c)     the user does not have permission to write in the current working directory, 

(d)     the file already exists and the user does not have permission to write to it, or 

(e)     the file is too big (recall the RLIMIT_CORE limit in Section 7.11). The permissions of the core file (assuming that the file doesn't already exist) are usually user-read and user-write, although Mac OS X sets only user-read.

 

五,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選項編譯。


可以看到,當輸入12的時候,系統提示段錯誤並且core dumped

 

Ø  我們進入對應的core文件生成目錄,優先確認是否core文件格式並啓用gdb進行調試。


從紅色方框截圖可以看到,程序中止是因爲信號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 程序文件名


發佈了7 篇原創文章 · 獲贊 23 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章