1. 程式人生 > >gdb除錯命令及程式崩潰時的核心轉存core dump

gdb除錯命令及程式崩潰時的核心轉存core dump

1.gcc -g filename.c -o filename 需要生成帶除錯資訊的檔案

2.除錯

  方式一:gdb filename 除錯file可執行檔案

  方式二:>>gdb

             >>file filename

$gdb -tui     啟動gdb,並且分屏顯示原始碼

3.打斷點的方式

  break line_num  在main.c中line_num打斷點

  break filename.c:line_num 在filename.c中line_num打斷點

  break funcname 在funcname函式入口上打斷點

  break line_num if 條件   條件成立時在某行上打斷點

4.info break 檢視所有設定的斷點號,並列出斷點序號

5.delete break_num  刪除斷點

6.list funname 檢視指定的函式程式碼

   list filename:N 檢視指定檔案第N行附近的程式碼

7.run 開始全速執行程式,直到斷點

8.next 單步執行,不進入子程式;  step 單步執行,進入子程式

9.continue 繼續全速執行,直到斷點

10.print +表示式  檢視指定變數名  ;watch  varriblename  對指定變數進行監控

display  表示式   與print的區別是它會在程式停住時自動顯示變數的值

examine  地址

1)顯示動態記憶體的值:

    int *array = (int *) malloc (len * sizeof (int));
    於是,在GDB除錯過程中,你可以以如下命令顯示出這個動態陣列的取值:
 >>p *[email protected]
    @的左邊是陣列的首地址的值,也就是變數array所指向的內容,右邊則是資料的長度,其
儲存在變數len中,其輸出結果,大約是下面這個樣子的:
(gdb) p *[email protected]
$1 = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40}

2)顯示動態記憶體的值

      如果是靜態陣列的話,可以直接用print陣列名,就可以顯示陣列中所有資料的內容了。

3)print 顯示資料的格式

x 按十六進位制格式顯示變數。
d 按十進位制格式顯示變數。
u 按十六進位制格式顯示無符號整型。
o 按八進位制格式顯示變數。
t 按二進位制格式顯示變數。
a 按十六進位制格式顯示變數。
c 按字元格式顯示變數。
f 按浮點數格式顯示變數。


(gdb) print/a   i

$22 = 0x65

4)print i=10   修改變數的值為10

5)examine/3uh 0x54320 以16進位制顯示3個雙位元組

6)列印變數的地址(print &var) 
   列印地址的資料值(print *address)
7)強制呼叫某個函式
(gdb) call <expr> 
這裡,<expr>可以是一個函式,這樣就會返回函式的返回值,如果函式的返回型別是void那麼就不會列印函式的返回值,但是實踐發現,函式執行過程中的列印語句還是沒有被打印出來。
(gdb) print <expr> 
這裡,print和call的功能類似,不同的是,如果函式的返回值是void那麼call不會列印返回值,但是print還是會打印出函式的返回值並且存放到歷史記錄中。

11.finish  執行程式,直到當前函式結束

12.quit 退出gdb

13.backtrace  檢視當前的程式棧

14.break  make_ <按TAB鍵>   補齊函式

核心轉儲是讓系統在訊號中斷造成的應用程式錯誤時產生core檔案, 儲存應用程式當前狀態。
核心轉儲檔案的作用:作業系統用來儲存某應用程式崩潰時的程式執行狀態,gdb可以用該轉儲檔案來還原到程式崩潰時的狀態。
一、程式執行崩潰的原因
Linux下c/c++開發程式崩潰(Segment fault)通常都是指標錯誤引起的.
比如:
(1)訪問了不存在的記憶體地址
(2)訪問了只讀的記憶體地址
(3)訪問了系統保護的記憶體地址int *p=0;*p=100;
(4)棧溢位,無限遞迴
(5)記憶體溢位

二、核心轉儲檔案作用
發生Segment fault時,核心轉儲檔案(core dump)作用
(1) 核心轉儲的最大好處是能夠儲存問題發生時的狀態。
(2) 只要有可執行檔案和核心轉儲,就可以知道程序當時的狀態。
(3) 只要獲取核心轉儲,那麼即使沒有復現環境,也能除錯。

三、配置作業系統的核心轉儲功能
可以參考《高併發伺服器開發與配置》中,使用者能開啟的最大檔案數的設定方法。
啟動系統的核心轉儲功能,需要做如下配置:
(1)檢視當前轉儲檔案大小
>> ulimit -c
0   為0,表示當前轉儲檔案大小為0,沒有啟動核心轉儲
>>ulimit -c unlimited       #設定coredump 大小為無限大
這些需要有root許可權, 在ubuntu下每次重新開啟中斷都需要重新輸入上面的第一條命令, 來設定core大小為無限.

四、gdb使用核心轉儲檔案再現崩潰時的狀態
>>./test   ->執行test崩潰,在當前目錄下將產生一個core檔案

>>gdb -c ./corefile  ./test   使用gdb再現崩潰狀態
在進入gdb後, 用bt命令檢視backtrace以檢查發生程式執行到哪裡, 來定位core dump的檔案->行.

五、System Dump和Core Dump的區別

1) 系統Dump(System Dump)
所有開放式作業系統,都存在系統DUMP問題。
產生原因:
由於系統關鍵/核心程序,產生嚴重的無法恢復的錯誤,為了避免系統相關資源受到更大損害,作業系統都會強行停止執行,並將當前記憶體中的各種結構,核心程序出錯位置及其程式碼狀態,儲存下來,以

便以後分析。最常見的原因是指令走飛,或者緩衝區溢位,或者記憶體訪問越界。走飛就是說程式碼流有問題,導致執行到某一步指令混亂,跳轉到一些不屬於它的指令位置去執行一些莫名其妙的東西(沒

人知道那些地方本來是程式碼還是資料,而且是不是正確的程式碼開始位置),或者呼叫到不屬於此程序的記憶體空間。寫過C程式及彙編程式的人士,對這些現象應當是很清楚的。
系統DUMP生成過程的特點:
在生成DUMP過程中,為了避免過多的操作結構,導致問題所在位置正好也在生成DUMP過程所涉及的資源中,造成DUMP不能正常生成,作業系統都用盡量簡單的程式碼來完成,所以避開了一切複雜的管理結

構,如檔案系統)LVM等等,所以這就是為什麼幾乎所有開放系統,都要求DUMP裝置空間是物理連續的——不用定位一個個資料塊,從DUMP裝置開頭一直寫直到完成,這個過程可以只用BIOS級別的操作就

可以。這也是為什麼在企業級UNIX普遍使用LVM的現狀下,DUMP裝置只可能是裸裝置而不可能是檔案系統檔案,而且[b]只[/b]用作DUMP的裝置,做 LVM映象是無用的——系統此時根本沒有LVM操作,它不

會管什麼映象不映象,就用第一份連續寫下去。
所以UNIX系統也不例外,它會將DUMP寫到一個裸設或磁帶裝置。在重啟的時候,如果設定的DUMP轉存目錄(檔案系統中的目錄)有足夠空間,它將會轉存成一個檔案系統檔案,預設情況下,[b]對於AIX

來說是/var/adm/ras/下的vmcore*這樣的檔案,對於HPUX來說是 /var/adm/crash下的目錄及檔案。[/b]
當然,也可以選擇將其轉存到磁帶裝置。
會造成系統DUMP的原因主要是:
系統補丁級別不一致或缺少)系統核心擴充套件有BUG(例如Oracle就會安裝系統核心擴充套件))驅動程式有 BUG(因為裝置驅動程式一般是工作在核心級別的),等等。所以一旦經常發生類似的系統DUMP,可

以考慮將系統補丁包打到最新並一致化)升級微碼)升級裝置驅動程式(包括FC多路冗餘軟體))升級安裝了核心擴充套件的軟體的補丁包等等。
2) 程序Core Dump
程序Core Dump產生的技術原因,基本等同於系統DUMP,就是說從程式原理上來說是基本一致的。
但程序是執行在低一級的優先順序上(此優先順序不同於系統中對程序定義的優先順序,而是指CPU程式碼指令的優先順序),被作業系統所控制,所以作業系統可以在一個程序出問題時,不影響其他程序的情況下

,中止此程序的執行,並將相關環境儲存下來,這就是core dump檔案,可供分析。
如果程序是用高階語言編寫並編譯的,且使用者有源程式,那麼可以通過在編譯時帶上診斷用符號表(所有高階語言編譯程式都有這種功能),通過系統提供的分析工具,加上core檔案,能夠分析到哪一

個源程式語句造成的問題,進而比較容易地修正問題,當然,要做到這樣,除非一開始就帶上了符號表進行編譯,否則只能重新編譯程式,並重新執行程式,重現錯誤,才能顯示出源程式出錯位置。
如果使用者沒有源程式,那麼只能分析到彙編指令的級別,難於查詢問題所在並作出修正,所以這種情況下就不必多費心了,找到出問題的地方也沒有辦法。
程序Core Dump的時候,作業系統會將程序異常終止掉並釋放其佔用的資源,不可能對系統本身的執行造成危害。這是與系統DUMP根本區別的一點,系統DUMP產生時,一定伴隨著系統崩潰和停機,程序

Core Dump時,只會造成相應的程序被終止,系統本身不可能崩潰。當然如果此程序與其他程序有關聯,其他程序也會受到影響,至於後果是什麼,就看相關程序對這種異常情況(與自己相關的程序突然

終止)的處理機制是什麼了,沒有一概的定論。

六、核心轉儲檔案(core dump)永久生效的辦法
在終端中輸入以下命令,檢視核心轉儲是否有效。
#ulimit -c
0
-c 表示核心轉儲檔案的大小限制,現在顯示為0,表示不能用。

永久生效的辦法是:
#vi /etc/profile 然後,在profile中新增:
ulimit -c 1073741824      --1G大小
(但是,若將產生的轉儲檔案大小大於該數字時,將不會產生轉儲檔案)或者
ulimit -c unlimited
這樣重啟機器後生效了。 或者, 使用source命令使之馬上生效。
#source /etc/profile

七、指定核心轉儲的檔名和目錄
預設情況下,核心在coredump時所產生的core檔案放在與該程式相同的目錄中,並且檔名固定為core。很顯然,如果有多個程式產生core檔案,或者同一個程式多次崩潰,就會重複覆蓋同一個core文

件。可以通過修改kernel的引數,指定核心轉儲所生成的core檔案的路徑和檔名。
可以通過在/etc/sysctl.conf檔案中,對sysctl變數kernel.core_pattern的設定。
>>vi /etc/sysctl.conf
然後,在sysctl.conf檔案中新增下面兩句話:
kernel.core_pattern = /var/core/core_%e_%p
kernel.core_uses_pid = 0
需要說明的是, /proc/sys/kernel/core_uses_pid。如果這個檔案的內容被配置成1,即使core_pattern中沒有設定%p,最後生成的core dump檔名仍會加上程序ID。
這裡%e, %p分別表示:
%c 轉儲檔案的大小上限
%e 所dump的檔名
%g 所dump的程序的實際組ID
%h 主機名
%p 所dump的程序PID
%s 導致本次coredump的訊號
%t 轉儲時刻(由1970年1月1日起計的秒數)
%u 所dump程序的實際使用者ID
可以使用以下命令,使修改結果馬上生效。
>>sysctl –p /etc/sysctl.conf

請在/var目錄下先建立core資料夾,然後執行a.out程式,就會在/var/core/下產生以指定格式命名的核心轉儲檔案。檢視轉儲檔案的情況:
#ls /var/core
core_a.out_2834
八、例子
Linux下c/c++開發之程式崩潰(Segment fault)時核心轉儲檔案(coredump)生成舉例說明
例子的原始碼:
#include <stdio.h>

int main(void)

{
int *a = NULL;
*a = 0x1;
return 0;

}

把以上原始碼,寫成一個a.c檔案後,編譯a.c檔案產生一個a.out的可執行檔案:
#gcc -g a.c -o a.out
修改a.out檔案的許可權後,執行它:
#./a.out
就會顯示:
Segmentation fault(core dump)
這表示在當前目錄下, 已經生成了a.out對應的核心轉儲檔案。
注意:後面帶有(core dump), 才說明轉儲檔案成功生成了。
#file core*
core:ELF 64-bit LSB core file x86-64, version 1(SYSV), SVR4-style, from './a.out'
coreDump: UTF-8 Unicode C program text
要用GDB除錯核心轉儲檔案,應該使用以下方式啟動GDB:
#gdb -c ./core ./a.out

GNU gdb (GDB) 7.1-Ubuntu
...
Core was generated by './a.out'.
Program terminated with signal 11, Segmentation fault.
#0 0x00000000004004dc in main() at a.c:6
6 *a =0x1;
a.c的第6行收到了11號訊號。用GDB的list命令可以檢視附近的原始碼。
(gdb) l 5
1            #include <stdio.h>
2     
3            int main(void)
4            {
5                   int *a = NULL;
6                   *a = 0x1;
7                   return 0;
8            }

這裡預設都是當前目錄,也可以給core 和a.out 指定路徑。