? ? 無(wú)論多么有經(jīng)驗(yàn)的程序員,開發(fā)的任何軟件都不可能完全沒有 bug。因此,排查及修復(fù) bug 成為軟件開發(fā)周期中最重要的任務(wù)之一。有許多辦法可以排查 bug(測(cè)試、代碼自審等等),但是還有一些專用軟件(稱為調(diào)試器)可以幫助準(zhǔn)確定位問題的所在,以便進(jìn)行修復(fù)。
如果你是 C/C++ 程序員,或者使用 Fortran 和 Modula-2 編程語(yǔ)言開發(fā)軟件,那么你將會(huì)很樂意知道有這么一款優(yōu)秀的調(diào)試器 -?GDB?- 可以幫你更輕松地調(diào)試代碼 bug 以及其它問題。在這篇文章中,我們將討論一下 GDB 調(diào)試器的基礎(chǔ)知識(shí),包括它提供的一些有用的功能/選項(xiàng)。
在我們開始之前,值得一提的是,文章中的所有說明和示例都已經(jīng)在 Ubuntu 14.04 LTS 中測(cè)試過。教程中的示例代碼都是 C 語(yǔ)言寫的;使用的 shell 為 bash(4.3.11);GDB 版本為 7.7.1。
GDB 調(diào)試器基礎(chǔ)
通俗的講,GDB 可以讓你看到程序在執(zhí)行過程時(shí)的內(nèi)部流程,并幫你明確問題的所在。我們將在下一節(jié)通過一個(gè)有效的示例來(lái)討論 GDB 調(diào)試器的用法,但在此之前,我們先來(lái)探討一些之后對(duì)你有幫助的基本要點(diǎn)。
首先,為了能夠順利使用類似 GDB 這樣的調(diào)試器,你必須以指定的方式編譯程序,讓編譯器產(chǎn)生調(diào)試器所需的調(diào)試信息。例如,在使用 gcc 編譯器(我們將在本教程之后的章節(jié)用它來(lái)編譯 C 程序示例)編譯代碼的時(shí)候,你需要使用?-g?命令行選項(xiàng)。
想要了解 gcc 編譯器手冊(cè)頁(yè)中關(guān)于?-g?命令行選項(xiàng)相關(guān)的內(nèi)容,請(qǐng)看這里。
下一步,確保在你的系統(tǒng)中已經(jīng)安裝 GDB 調(diào)試器。如果沒有安裝,而且你使用的是基于 Debian 的系統(tǒng)(如 Ubuntu),那么你就可以使用以下命令輕松安裝該工具:
sudo apt-get install gdb
在其他發(fā)行版上的安裝方法,請(qǐng)看這里。
現(xiàn)在,當(dāng)你按照上述的方式編譯完程序(gcc?-g?命令行選項(xiàng)),同時(shí)也已經(jīng)安裝好 GDB 調(diào)試器,那么你就可以使用以下命令讓程序在調(diào)試模式中運(yùn)行:
gdb [可執(zhí)行程序的名稱]
這樣做會(huì)初始化 GDB 調(diào)試器,但你的可執(zhí)行程序此時(shí)還不會(huì)被啟動(dòng)。在這個(gè)時(shí)候你就可以定義調(diào)試相關(guān)的設(shè)置。例如,你可以在特定行或函數(shù)中設(shè)置一個(gè)斷點(diǎn)讓 GDB 在該行暫停程序的執(zhí)行。
接著,為了啟動(dòng)你的程序,你必須輸入執(zhí)行以下 gdb 命令:
run
在這里,值得一提的是,如果你的程序需要一些命令行參數(shù),那么你可以在這里指定這些參數(shù)。例如:
run [參數(shù)]
GDB 提供了很多有用的命令,在調(diào)試的時(shí)候總是能派的上用場(chǎng)。我們將在下一節(jié)討論其中一部分命令。
GDB 調(diào)試器用例
現(xiàn)在我們對(duì) GDB 及其用法有了基本的概念。因此,讓我們舉例來(lái)應(yīng)用所學(xué)的知識(shí)。這是一段示例代碼:
#include
int main()
{
int out = 0, tot = 0, cnt = 0;
int val[] = {5, 54, 76, 91, 35, 27, 45, 15, 99, 0};
while(cnt < 10)
{
out = val[cnt];
tot = tot + 0xffffffff/out;
cnt++;
}
printf(" Total = [%d] ", tot);
return 0;
}
簡(jiǎn)單說明一下這段代碼要做什么事。獲取?val?數(shù)組中每一個(gè)值,將其賦值給?out?變量,然后將?tot?之前的值與?0xffffffff/out?的結(jié)果值累加,賦值給?tot?變量。
這里遇到的問題是,當(dāng)執(zhí)行這段代碼編譯后的可執(zhí)行程序時(shí),產(chǎn)生以下錯(cuò)誤:
$ ./gdb-test
Floating point exception (core dumped)
因此,要調(diào)試這段代碼,第一步是使用?-g?選項(xiàng)編譯程序。命令如下:
gcc -g -Wall gdb-test.c -o gdb-test
接著,讓我們運(yùn)行 GDB 調(diào)試器并指定要調(diào)試的可執(zhí)行程序。命令如下:
gdb ./gdb-test
現(xiàn)在,我剛才得到的錯(cuò)誤是?Floating point exception,大部分人可能已經(jīng)知道,這是因?yàn)?n % x,當(dāng) x 為 0 時(shí)導(dǎo)致的錯(cuò)誤。所以,考慮到這一點(diǎn),我在 11 行代碼除法運(yùn)算的位置處添加了一個(gè)斷點(diǎn)。如下:
(gdb)&;break 11
注意?(gdb)?是調(diào)試器的提示信息,我只輸入了?break 11?命令。
現(xiàn)在,讓 GDB 開始運(yùn)行程序:
run
當(dāng)斷點(diǎn)第一次被命中時(shí),GDB 顯示如下輸出:
Breakpoint 1, main () at gdb-test.c:1111 tot = tot + 0xffffffff/out;(gdb)
正如你所看到的那樣,調(diào)試器會(huì)顯示斷點(diǎn)所在的行代碼?,F(xiàn)在,讓我們打印出此時(shí)?out?的值。如下:
(gdb) print out
$1 = 5
(gdb)
如上所示,值?5?被打印出來(lái)了。這個(gè)時(shí)候一切都還是正常的。讓調(diào)試器繼續(xù)執(zhí)行程序直到命中下一個(gè)斷點(diǎn),可以通過使用?c?命令來(lái)完成:
c
重復(fù)上述操作,直到?out?值變?yōu)?0?時(shí)。
...
...
...
Breakpoint 1, main () at gdb-test.c:11
11 tot = tot + 0xffffffff/out;
(gdb) print out
$2 = 99
(gdb) c
Continuing.
Breakpoint 1, main () at gdb-test.c:11
11 tot = tot + 0xffffffff/out;
(gdb) print out
$3 = 0
(gdb)
現(xiàn)在,為了進(jìn)一步確認(rèn)問題,我使用 GDB 的?s(或?step) 命令代替?c?命令。因?yàn)椋抑幌胱尞?dāng)前程序在第 11 行之后暫停,再一步步執(zhí)行,看看這個(gè)時(shí)候是否會(huì)發(fā)生崩潰。
以下是執(zhí)行之后輸出信息:
(gdb) s
Program received signal SIGFPE, Arithmetic exception.
0x080484aa in main () at gdb-test.c:11
11 tot = tot + 0xffffffff/out;
是的,如上輸出的第一行內(nèi)容所示,這就是拋出異常的地方。當(dāng)我再次嘗試運(yùn)行?s?命令時(shí),問題最終也得到了確認(rèn):
(gdb) s
Program terminated with signal SIGFPE, Arithmetic exception.
The program no longer exists.
通過這種方式,你就可以使用 GDB 調(diào)試你的程序。
總結(jié)
GDB 提供了很多功能供用戶研究和使用,在這里,我們僅僅只介紹了很少一部分內(nèi)容。通過 GDB 的手冊(cè)頁(yè)可以進(jìn)一步了解這個(gè)工具,當(dāng)你在調(diào)試代碼的時(shí)候,嘗試使用一下它。GDB 調(diào)試器有一定的學(xué)習(xí)難度,但是它很值得你下功夫?qū)W習(xí)。
?
評(píng)論
查看更多