Linux下面生成core文件並使用gdb進行簡單的調試

一、core文件

(1)什麼是core文件

有問題的程序運行後,產生“段錯誤(核心已轉儲)”,“Segmentation fault (core dumped)”,時候會生成具有堆棧信息和調試信息的文件,編譯得的時候需要加上-g選項使程序生成調試信息: g++ -g helloworld.cc -o helloworld

 

(2)如何判斷一個文件是coredump文件

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

例如: readelf -h core

在現實的內容裏面會看到ELF頭文件的Type字段類型: Type: CORE (Core file)

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

 

(3)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,堆棧溢出

不要使用大的局部變量(因爲局部變量都分配在棧上),這樣容易造成堆棧溢出,破壞系統的棧和堆結構,導致出現莫名其妙的錯誤。

 

(4)coredump文件的相關配置

1、core文件大小的設置

產生coredump文件的首要條件是確認當前終端是否允許產生一定大小的coredump文件

通過指令 ulimit -c查看,如果結果爲0,則不能產生,需要通過一定的修改和設置纔可以

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

ulimit -c [size]   (可以自行設置大小,親自測試過size至少爲4纔可以生成coredump文件)

一般我們都選擇coredump文件大小不受限制的方法,但是每次都需要在這個終端輸入這條指令,爲了方便我們一般的做法是把這條指令寫入/etc/profile,以便於開機後自動執行。

注意:亦可以把這句話寫入~/.bashrc文件中,但這個文件是在打開終端的時候纔會執行,開機不會自動執行。

2、coredump的保存路徑與文件名

core文件一般都有默認的生路路徑,一般默認生成路徑在可執行文件下面,ros程序在~/.ros文件夾。文件名默認爲core

但是爲了方便,一般我們需要指定文件路徑與文件名:

echo "/home/liu/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern

注意:需sudo su進入root權限纔可以,文件路徑需要完整路徑,不可以使用相對路徑,需要手動創建好/home/liu/corefile這個目錄,系統不會自動創建。經過實際測試發現每次修改完這個文件,重啓系統後又重新回到原狀,修改消失,暫時還沒有找到原因。

%e表示生成coredump的可執行文件名稱

%p表示當前pid

%t表示core文件產生時候的unix時間  (由1970年1月1日計起的秒數)

3、爲了生成core文件,該如何設置可執行程序

最簡單的編譯的時候添加-g指令: g++ -g helloworld.cc -o helloworld

如果使用CMakelists.txt文件編譯,需要在該文件裏面添加下面兩行:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
set(CMAKE_VERBOSE_MAKEFILE ON)

 

 

二、調試core文件

gdb 可執行文件  coredump文件

(1)記住幾個常用的gdb命令

記住幾個常用的gdb命令:

l(list) ,顯示源代碼,並且可以看到對應的行號;

b(break)x, x是行號,表示在對應的行號位置設置斷點;

p(print)x, x是變量名,表示打印變量x的值

r(run), 表示繼續執行到斷點的位置

n(next),表示執行下一步

c(continue),表示繼續執行

q(quit),表示退出gdb

 

(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)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>  程序運行時的語句在內存中的地址。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章