GDB輕松調試
一、引言
在了解GDB可以做什么,怎么做之前,讓我們先來看看為什么要用GDB,或者說對調試工具有什么期望。
一般我們使用GDB(或其他調試工具)是為了發(fā)現(xiàn)程序bug,更經常地是在已知程序有錯的情況下定位bug。既然這樣,我們就需要跟蹤程序的執(zhí)行情況,查看程序執(zhí)行是否正常,當然這就需要有個讓我們與執(zhí)行程序交互的環(huán)境,調試工具提供一個能讓程序在你的掌控下執(zhí)行,并讓你能夠查看一些執(zhí)行過程中的“內幕信息”的環(huán)境。
為了查看程序運行過程中的狀態(tài),我們就希望程序能在適當?shù)奈恢没蛘咴谝欢ǖ臈l件下能夠暫停運行;為此,調試工具提供了斷點、查看變量/表達式、顯示程序棧等功能??戳四硞€點的“內幕”后,我們還期望更多,所以要能控制程序運行才行,這就要求斷點、繼續(xù)運行、單步(多步)運行、進入函數(shù)運行等功能,在某些情況下,還需要通過修改當前的執(zhí)行環(huán)境(變量等)來達到期望的執(zhí)行順序。也就是說,光看著是不夠的,還需要能改才行。
理解了這些問題后,我們就明白GDB的各個功能的用意了,自然也就明白該如何使用調試工具了。當然,要讓GDB有效的發(fā)揮作用,還是需要一定的經驗與技巧,而這主要靠實踐,學習資料(包括本文)充其量只能幫你一把(小心別讓它幫倒忙)。
總而言之,我們首先要明白使用調試工具的目的和用意,才能理解它的各項功能,才能借助它快速有效的發(fā)現(xiàn)問題;否則,即使工具再強大,你也不知道該如何使用才好。
另外要多結合使用代碼檢視、運行日志、測試工具等方法來發(fā)現(xiàn)潛在的問題,提供程序的質量。這些問題將在另文探討,先做個廣告。
?
?
二、GDB能做什么
GDB可以用來調試C、C++、Modula-2的程序。一般來說,GDB能做的事大致可以分為四類:
1、啟動程序,按指定的方式執(zhí)行程序。
2、在指定條件下使程序暫停.
3、當程序被停住時,可以檢查此時你的程序中的變化。
4、改變程序中的變量或執(zhí)行順序來試驗。
?
?
三、GDB使用概述
首先要了解的是gdb的help命令,因為你可能記不住各個命令的語法和用途,但只要能正確使用help命令,你就不需要任何其它的gdb資料。
啟動gdb后,輸入help
[eric@linux eric]$ gdb
GNU gdb Red Hat Linux (5.3.90-0.20030710.40rh)
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome 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 "i386-redhat-linux-gnu".
(gdb) help
List of classes of commands:
aliases -- Aliases of other commands
breakpoints -- Making program stop at certain points
data -- Examining data
files -- Specifying and examining files
internals -- Maintenance commands
obscure -- Obscure features
running -- Running the program
stack -- Examining the stack
status -- Status inquiries
support -- Support facilities
tracepoints -- Tracing of program execution without stopping the program
user-defined -- User-defined commands
Type "help" followed by a class name for a list of commands in that class.
Type "help" followed by command name for full documentation.
Command name abbreviations are allowed if unambiguous.
(gdb)
如上文顯示,gdb的命令很多,所以把它分成許多個種類。help命令只是例出gdb的命令種類,如果要看某類中的命令,可以使用help <class> 命令,如:help breakpoints,查看設置斷點的所有命令。當如也可以直接help <command>來查看某個命令的具體信息。
gdb 技巧:在記不清整個命令時,可以只打命令的前一個或幾個字符,然后敲擊兩次TAB鍵來列出所有以這幾個字符開頭的命令;另為,大多命令都有縮寫,如b同 break,c同continue,n同next,p同print等。另為,一個命令在輸入能唯一標示命令的前綴后,按一下TAB鍵就能補齊命令的全稱,比如輸入ba后按一下TAB鍵,就自動補齊為backtrace,輸入pr后按一下TAB鍵就補齊為print。
為調試編譯代碼
為了使 gdb 正常工作, 你必須使你的程序在編譯時包含調試信息. 調試信息包含你程序里的每個變量的類型和在可執(zhí)行文件里的地址映射以及源代碼的行號. gdb 利用這些信息使源代碼和機器碼相關聯(lián).
在編譯時用 -g 選項打開調試選項.
在GDB中運行程序
當以gdb <program>方式啟動gdb后,可以使用r或是run命令運行程序。在程序運行之前,你有可能需要設置下面四方面的事。
1、程序運行參數(shù)。
set args 可指定運行時參數(shù)。(如:set args 10 20 30 40 50)
show args 命令可以查看設置好的運行參數(shù)。
2、運行環(huán)境。
path <dir> 可設定程序的運行路徑。
show paths 查看程序的運行路徑。
set environment varname [=value] 設置環(huán)境變量。如:set env USER=hchen
show environment [varname] 查看環(huán)境變量。
3、工作目錄。
cd <dir> 相當于shell的cd命令。
pwd 顯示當前的所在目錄。
4、程序的輸入輸出。
info terminal 顯示你程序用到的終端的模式。
使用重定向控制程序輸出。如:run > outfile
tty命令可以指寫輸入輸出的終端設備。如:tty /dev/ttyb
調試已運行的程序
可以有兩種方法調試已運行程序:
1、用ps查看正在運行的程序的進程ID,然后用gdb <program> PID格式掛接正在運行的程序。
2、先用gdb <program>關聯(lián)上程序,并進行gdb,在gdb中用attach命令來掛接程序正在運行的進程。detach可用來取消掛接的進程。
暫停/恢復程序運行
你可以使用info program 來查看程序的當前的執(zhí)行狀態(tài)。
在gdb中,我們可以有以下幾種暫停方式:斷點(BreakPoint)、觀察點(WatchPoint)、捕捉點(CatchPoint)、信號(Signals)、線程停止(Thread Stops)。如果要恢復程序運行,可以使用c或是continue命令。
查看變量/表達式的值
可以使用print expr(或p expr)來查看程序變量/表達式的值
顯示程序棧
可以使用backtrace(或bt)來顯示程序棧
單步跟蹤
next [n] 執(zhí)行下一條(或n條)語句,不進入子程序
step [n] 執(zhí)行下一條(或n條)語句,進入子程序,可用finish從子程序返回
?
?
?
四、GDB常用命令
backtrace 顯示程序中的當前位置和表示如何到達當前位置的棧跟蹤(同義詞:where)
breakpoint 在程序中設置一個斷點
cd 改變當前工作目錄
clear 刪除剛才停止處的斷點
commands 命中斷點時,列出將要執(zhí)行的命令
continue 從斷點開始繼續(xù)執(zhí)行
delete 刪除一個斷點或監(jiān)測點;也可與其他命令一起使用
display 程序停止時顯示變量和表達時
down 下移棧幀,使得另一個函數(shù)成為當前函數(shù)
frame 選擇下一條continue命令的幀
info 顯示與該程序有關的各種信息
jump 在源程序中的另一點開始運行
kill 異常終止在gdb 控制下運行的程序
list 列出相應于正在執(zhí)行的程序的原文件內容
next 執(zhí)行下一個源程序行,從而執(zhí)行其整體中的一個函數(shù)
print 顯示變量或表達式的值
pwd 顯示當前工作目錄
pype 顯示一個數(shù)據(jù)結構(如一個結構或C++類)的內容
quit 退出gdb
reverse-search 在源文件中反向搜索正規(guī)表達式
run 執(zhí)行該程序
search 在源文件中搜索正規(guī)表達式
set variable 給變量賦值
signal 將一個信號發(fā)送到正在運行的進程
step 執(zhí)行下一個源程序行,必要時進入下一個函數(shù)
undisplay display命令的反命令,不要顯示表達式
until 結束當前循環(huán)
up 上移棧幀,使另一函數(shù)成為當前函數(shù)
watch 在程序中設置一個監(jiān)測點(即數(shù)據(jù)斷點)
whatis 顯示變量或函數(shù)類型
命令的具體使用方法請用上面介紹的help查詢,看不明白的地方就多試試。
?
?
五、用例子說話
本文是打算寫個簡單的程序作為例子講解的,后來一想:“太假”,就講一個前幾天我的實際調試經歷吧,因為當時沒抓圖,這里就用文字描述了,請讀者注意其中的思路和方法,具體的一些操作就要勞煩自己去實踐了
背景:在將一個linux程序(姑且就叫A吧)重redhat 9.0移植到redhat es時發(fā)現(xiàn)程序core了
開始了,呵呵:
1、首先我查看了程序日志,找到引起程序core掉的數(shù)據(jù)(一個網頁);//所以說日志很重要
2、下載了那個網頁,用它作為輸入,結果必core;//確認出錯環(huán)境
3、用gdb啟動程序,然后觸發(fā)錯誤后,用bt查看程序棧,記錄棧中的函數(shù)調用鏈以及出錯的代碼行數(shù)
4、在用gdb啟動程序,在出錯行前設斷點,運行之,再觸發(fā)錯誤
5、使用next和step精確定位到出錯行
6、print一個指針變量,發(fā)現(xiàn)不是NULL,再看指針所指結構的各個變量也正常
7、好像沒錯呀,呵呵,此處是個循環(huán),繼續(xù)單步便重復6
8、發(fā)現(xiàn)循環(huán)中指針遞減,懷疑指針所指數(shù)組越界,打印數(shù)組起始位置地址
9、繼續(xù)循環(huán)一直到出錯,打印指針變量,發(fā)現(xiàn)其指向的地址低于數(shù)組起始位置地址,真的越界了
10、初步算是找到了,查看程序源碼,發(fā)現(xiàn)循環(huán)中沒有判斷該指針是否低于數(shù)組起始位置地址
11、修改代碼后重新運行,程序不core了
12、將新程序放到正常執(zhí)行環(huán)境下工作,長時間運行后沒有發(fā)現(xiàn)該問題重現(xiàn),確認解決問題
13、通知出錯部分(一個功能函數(shù)庫)的作者問題找到、原因
注:為簡單起見省略了過程中的一些因系統(tǒng)特殊性引起的工作
?