原文位置 http://www.unknownroad.com/rtfm/gdbtut/gdbsegfault.html
GDB儲存斷點資訊的方法
(1)建立一個檔案a;
(2)在a中加入dgb斷點資訊,如: b XCAssit.cpp:666;
(3)在dgb中載入這些斷點資訊:gdb ./XCAssist -x ./;
我們打算使用gdb去解決為什麼下面的程式(檔案為segfault.c)引起了段錯誤的問題。下面的這段程式是從使用者那裡讀入一行文本字串然後顯示在螢幕上。然而,如下當前的程式並不會如期執行...
[cpp]
view plaincopyprint?
- <span style="font-size:18px;">#include <stdio.h>
- #include <stdlib.h>
-
- int main(int argc, char **argv)
- {
- char *buf;
-
- buf = malloc(1<<31);
-
- fgets(buf, 1024, stdin);
- printf("%s\n", buf);
-
- return 1;
- }</span>
第一步是使用帶有調試標誌(debugging flags)的方式編譯這段代碼,如下:
~# gcc -g segfault.c
然後運行:
~# a.out
Hello World!
Segmentation fault
這並不是我們所期待的。是時候啟動強大的gdb了。
~# gdb a.outGNU gdb 5.0Copyright 2000 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions.There is absolutely no warranty for GDB. Type "show warranty" for details.This GDB was configured as "i686-pc-linux-gnu"...(gdb)
我們直接運行就來看看到底發生了什麼:
(gdb) runStarting program: /home/dgawd/cpsc/363/a.out test stringProgram received signal SIGSEGV, Segmentation fault.0x4007fc13 in _IO_getline_info () from /lib/libc.so.6
我們收到了來自作業系統的SIGSEGV訊號。這就意味著我們試圖去訪問一段非法的記憶體,讓我們試試backtrace(= bt)命令:
(gdb) backtrace#0 0x4007fc13 in _IO_getline_info () from /lib/libc.so.6#1 0x4007fb6c in _IO_getline () from /lib/libc.so.6#2 0x4007ef51 in fgets () from /lib/libc.so.6#3 0x80484b2 in main (argc=1, argv=0xbffffaf4) at segfault.c:10#4 0x40037f5c in __libc_start_main () from /lib/libc.so.6
這裡我們只關心我們自己的代碼,因此我們就切換到3號堆疊框架(stack frame3)來看看程式在哪裡崩潰的:
(gdb) frame 3#3 0x80484b2 in main (argc=1, argv=0xbffffaf4) at segfault.c:1010 fgets(buf, 1024, stdin)
哦,原來是調用fgets引起的崩潰。一般的,我們都假設庫函數比如fgets都可以正確地工作(如果不是這樣的話,我們的麻煩就大了)。因此這個問題的原因就一定是其中我們的一個參數的問題。你也許不知道‘stdin’是一個全域的變數,它是被stdio 庫建立的。因此我們假定這個參數是正確的。那麼剩下的就只能是‘buf’了,然後查看buf當前的值:
(gdb) print buf$1 = 0x0
buf的值是0x0,也就是NULL指標。這並不是我們鎖期待的 —— buf應該指向第8行代碼分配到的記憶體。因此我們需要返回到第8行並看看在哪裡發生了什麼。首先kill掉我們程式當前啟動並執行調用:
(gdb) killKill the program being debugged? (y or n) y
(注意:不用使用quit直接退出gdb,這樣比較麻煩。直接kill掉當前的程式調用即可)
然後在第8行設定一個斷點:
(gdb) break segfault.c:8Breakpoint 1 at 0x8048486: file segfault.c, line 8.
再次運行程式:
(gdb) runStarting program: /home/dgawd/cpsc/363/a.out Breakpoint 1, main (argc=1, argv=0xbffffaf4) at segfault.c:88 buf = malloc(1<<31);
我們檢查malloc調用前後buf值的變化。初始化buf以前,其值應該是一個隨機雜亂值(garbage),就像這裡的:
(gdb) print buf$2 = 0xbffffaa8 "鰓?\177\003@t`\001@\001"
我們step over(逐步執行)malloc調用然後再次檢查buf的值:
(gdb) next10 fgets(buf, 1024, stdin);(gdb) print buf$3 = 0x0
可見調用了malloc之後,buf是NULL。如果你查看malloc的手冊頁(man page),你就會發現malloc在不能分配夠所需的記憶體的時候就會返回NULL。因此確定是我們的malloc失敗了。讓我們返回到代碼再次看看:
7 : buf = malloc(1<<31);
哦,運算式1<<31(整型1左移31次,原文錯寫為右移)是429497295, 或4GB (gigabytes).很少有機器會有這樣的記憶體——大多數只有256MB(顯然這篇文章有年頭了,都什麼年代了,這點記憶體作業系統估計啟動一半就掛了)。因此malloc必然會失敗。此外,在fgets中我們唯讀入1024位元組。所有的額外空間都會白白浪費掉,儘管我們可以分配到。這裡我們將1<<31改為1024(或者1<<9),這樣程式就會按照我們的期望運行了:
~# a.outHello World!Hello World!
這樣你就可以知道怎樣使用gdb來調試段錯誤了,這是非常有用的。這個例子同時也說明了一個非常重要的準則:總是檢查malloc的傳回值!擁有美好的一天(說實在,我讓段錯誤噁心了一天。但以後就應該不太噁心了,以後每一天都美好吧 ^_^)。