新聞中心
使用 GNU 調(diào)試器來(lái)解決你的代碼問(wèn)題。
成都創(chuàng)新互聯(lián)公司主要從事成都網(wǎng)站設(shè)計(jì)、網(wǎng)站制作、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)公安,10多年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專(zhuān)業(yè),歡迎來(lái)電咨詢(xún)建站服務(wù):18980820575
GNU 調(diào)試器常以它的命令 gdb 稱(chēng)呼它,它是一個(gè)交互式的控制臺(tái),可以幫助你瀏覽源代碼、分析執(zhí)行的內(nèi)容,其本質(zhì)上是對(duì)錯(cuò)誤的應(yīng)用程序中出現(xiàn)的問(wèn)題進(jìn)行逆向工程。
故障排除的麻煩在于它很復(fù)雜。GNU 調(diào)試器 并不是一個(gè)特別復(fù)雜的應(yīng)用程序,但如果你不知道從哪里開(kāi)始,甚至不知道何時(shí)和為何你可能需要求助于 GDB 來(lái)進(jìn)行故障排除,那么它可能會(huì)讓人不知所措。如果你一直使用 print、echo 或 printf 語(yǔ)句來(lái)調(diào)試你的代碼,當(dāng)你開(kāi)始思考是不是還有更強(qiáng)大的東西時(shí),那么本教程就是為你準(zhǔn)備的。
有錯(cuò)誤的代碼
要開(kāi)始使用 GDB,你需要一些代碼。這里有一個(gè)用 C++ 寫(xiě)的示例應(yīng)用程序(如果你一般不使用 C++ 編寫(xiě)程序也沒(méi)關(guān)系,在所有語(yǔ)言中原理都是一樣的),其來(lái)源于 猜謎游戲系列 中的一個(gè)例子。
#include#include//srand #include//printf using namespace std;int main () {srand (time(NULL));int alpha = rand() % 8;cout << "Hello world." << endl;int beta = 2;printf("alpha is set to is %s\n", alpha);printf("kiwi is set to is %s\n", beta);return 0;} // main
這個(gè)代碼示例中有一個(gè) bug,但它確實(shí)可以編譯(至少在 GCC 5 的時(shí)候)。如果你熟悉 C++,你可能已經(jīng)看到了,但這是一個(gè)簡(jiǎn)單的問(wèn)題,可以幫助新的 GDB 用戶(hù)了解調(diào)試過(guò)程。編譯并運(yùn)行它就可以看到錯(cuò)誤:
$ g++ -o buggy example.cpp$ ./buggyHello world.Segmentation fault
排除段故障
從這個(gè)輸出中,你可以推測(cè)變量 alpha 的設(shè)置是正確的,因?yàn)榉駝t的話(huà),你就不會(huì)看到它后面的那行代碼執(zhí)行。當(dāng)然,這并不總是正確的,但這是一個(gè)很好的工作理論,如果你使用 printf 作為日志和調(diào)試器,基本上也會(huì)得出同樣的結(jié)論。從這里,你可以假設(shè) bug 在于成功打印的那一行之后的某行。然而,不清楚錯(cuò)誤是在下一行還是在幾行之后。
GNU 調(diào)試器是一個(gè)交互式的故障排除工具,所以你可以使用 gdb 命令來(lái)運(yùn)行錯(cuò)誤的代碼。為了得到更好的結(jié)果,你應(yīng)該從包含有調(diào)試符號(hào)的源代碼中重新編譯你的錯(cuò)誤應(yīng)用程序。首先,看看 GDB 在不重新編譯的情況下能提供哪些信息:
$ gdb ./buggyReading symbols from ./buggy...done.(gdb) startTemporary breakpoint 1 at 0x400a44Starting program: /home/seth/demo/buggyTemporary breakpoint 1, 0x0000000000400a44 in main ()(gdb)
當(dāng)你以一個(gè)二進(jìn)制可執(zhí)行文件作為參數(shù)啟動(dòng) GDB 時(shí),GDB 會(huì)加載該應(yīng)用程序,然后等待你的指令。因?yàn)檫@是你第一次在這個(gè)可執(zhí)行文件上運(yùn)行 GDB,所以嘗試重復(fù)這個(gè)錯(cuò)誤是有意義的,希望 GDB 能夠提供進(jìn)一步的見(jiàn)解。很直觀,GDB 用來(lái)啟動(dòng)它所加載的應(yīng)用程序的命令就是 start。默認(rèn)情況下,GDB 內(nèi)置了一個(gè)斷點(diǎn),所以當(dāng)它遇到你的應(yīng)用程序的 main 函數(shù)時(shí),它會(huì)暫停執(zhí)行。要讓 GDB 繼續(xù)執(zhí)行,使用命令 continue:
(gdb) continueContinuing.Hello world.Program received signal SIGSEGV, Segmentation fault.0x00007ffff71c0c0b in vfprintf () from /lib64/libc.so.6(gdb)
毫不意外:應(yīng)用程序在打印 “Hello world” 后不久就崩潰了,但 GDB 可以提供崩潰發(fā)生時(shí)正在發(fā)生的函數(shù)調(diào)用。這有可能就足夠你找到導(dǎo)致崩潰的 bug,但為了更好地了解 GDB 的功能和一般的調(diào)試過(guò)程,想象一下,如果問(wèn)題還沒(méi)有變得清晰,你想更深入地挖掘這段代碼發(fā)生了什么。
用調(diào)試符號(hào)編譯代碼
要充分利用 GDB,你需要將調(diào)試符號(hào)編譯到你的可執(zhí)行文件中。你可以用 GCC 中的 -g 選項(xiàng)來(lái)生成這個(gè)符號(hào):
$ g++ -o debuggy example.cpp$ ./debuggyHello world.Segmentation fault
將調(diào)試符號(hào)編譯到可執(zhí)行文件中的結(jié)果是得到一個(gè)大得多的文件,所以通常不會(huì)分發(fā)它們,以增加便利性。然而,如果你正在調(diào)試開(kāi)源代碼,那么用調(diào)試符號(hào)重新編譯測(cè)試是有意義的:
$ ls -l *buggy* *cpp-rw-r--r-- 310 Feb 19 08:30 debug.cpp-rwxr-xr-x 11624 Feb 19 10:27 buggy*-rwxr-xr-x 22952 Feb 19 10:53 debuggy*
用 GDB 調(diào)試
加載新的可執(zhí)行文件(本例中為 debuggy)以啟動(dòng) GDB:
$ gdb ./debuggyReading symbols from ./debuggy...done.(gdb) startTemporary breakpoint 1 at 0x400a44Starting program: /home/seth/demo/debuggyTemporary breakpoint 1, 0x0000000000400a44 in main ()(gdb)
如前所述,使用 start 命令進(jìn)行:
(gdb) startTemporary breakpoint 1 at 0x400a48: file debug.cpp, line 9.Starting program: /home/sek/demo/debuggyTemporary breakpoint 1, main () at debug.cpp:99 srand (time(NULL));(gdb)
這一次,自動(dòng)的 main 斷點(diǎn)可以指明 GDB 暫停的行號(hào)和該行包含的代碼。你可以用 continue 恢復(fù)正常操作,但你已經(jīng)知道應(yīng)用程序在完成之前就會(huì)崩潰,因此,你可以使用 next 關(guān)鍵字逐行步進(jìn)檢查你的代碼:
(gdb) next10 int alpha = rand() % 8;(gdb) next11 cout << "Hello world." << endl;(gdb) nextHello world.12 int beta = 2;(gdb) next14 printf("alpha is set to is %s\n", alpha);(gdb) nextProgram received signal SIGSEGV, Segmentation fault.0x00007ffff71c0c0b in vfprintf () from /lib64/libc.so.6(gdb)
從這個(gè)過(guò)程可以確認(rèn),崩潰不是發(fā)生在設(shè)置 beta 變量的時(shí)候,而是執(zhí)行 printf 行的時(shí)候。這個(gè) bug 在本文中已經(jīng)暴露了好幾次(破壞者:向 printf 提供了錯(cuò)誤的數(shù)據(jù)類(lèi)型),但暫時(shí)假設(shè)解決方案仍然不明確,需要進(jìn)一步調(diào)查。
設(shè)置斷點(diǎn)
一旦你的代碼被加載到 GDB 中,你就可以向 GDB 詢(xún)問(wèn)到目前為止代碼所產(chǎn)生的數(shù)據(jù)。要嘗試數(shù)據(jù)自省,通過(guò)再次發(fā)出 start 命令來(lái)重新啟動(dòng)你的應(yīng)用程序,然后進(jìn)行到第 11 行。一個(gè)快速到達(dá) 11 行的簡(jiǎn)單方法是設(shè)置一個(gè)尋找特定行號(hào)的斷點(diǎn):
(gdb) startThe program being debugged has been started already.Start it from the beginning? (y or n) yTemporary breakpoint 2 at 0x400a48: file debug.cpp, line 9.Starting program: /home/sek/demo/debuggyTemporary breakpoint 2, main () at debug.cpp:99 srand (time(NULL));(gdb) break 11Breakpoint 3 at 0x400a74: file debug.cpp, line 11.
建立斷點(diǎn)后,用 continue 繼續(xù)執(zhí)行:
(gdb) continueContinuing.Breakpoint 3, main () at debug.cpp:1111 cout << "Hello world." << endl;(gdb)
現(xiàn)在暫停在第 11 行,就在 alpha 變量被設(shè)置之后,以及 beta 被設(shè)置之前。
用 GDB 進(jìn)行變量自省
要查看一個(gè)變量的值,使用 print 命令。在這個(gè)示例代碼中,alpha 的值是隨機(jī)的,所以你的實(shí)際結(jié)果可能與我的不同:
(gdb) print alpha$1 = 3(gdb)
當(dāng)然,你無(wú)法看到一個(gè)尚未建立的變量的值:
(gdb) print beta$2 = 0
使用流程控制
要繼續(xù)進(jìn)行,你可以步進(jìn)代碼行來(lái)到達(dá)將 beta 設(shè)置為一個(gè)值的位置:
(gdb) nextHello world.12 int beta = 2;(gdb) next14 printf("alpha is set to is %s\n", alpha);(gdb) print beta$3 = 2
另外,你也可以設(shè)置一個(gè)觀察點(diǎn),它就像斷點(diǎn)一樣,是一種控制 GDB 執(zhí)行代碼流程的方法。在這種情況下,你知道 beta 變量應(yīng)該設(shè)置為 2,所以你可以設(shè)置一個(gè)觀察點(diǎn),當(dāng) beta 的值發(fā)生變化時(shí)提醒你:
(gdb) watch beta > 0Hardware watchpoint 5: beta > 0(gdb) continueContinuing.Breakpoint 3, main () at debug.cpp:1111 cout << "Hello world." << endl;(gdb) continueContinuing.Hello world.Hardware watchpoint 5: beta > 0Old value = falseNew value = truemain () at debug.cpp:1414 printf("alpha is set to is %s\n", alpha);(gdb)
你可以用 next 手動(dòng)步進(jìn)完成代碼的執(zhí)行,或者你可以用斷點(diǎn)、觀察點(diǎn)和捕捉點(diǎn)來(lái)控制代碼的執(zhí)行。
用 GDB 分析數(shù)據(jù)
你可以以不同格式查看數(shù)據(jù)。例如,以八進(jìn)制值查看 beta 的值:
(gdb) print /o beta$4 = 02
要查看其在內(nèi)存中的地址:
(gdb) print /o beta$5 = 0x2
你也可以看到一個(gè)變量的數(shù)據(jù)類(lèi)型:
(gdb) whatis betatype = int
用 GDB 解決錯(cuò)誤
這種自省不僅能讓你更好地了解什么代碼正在執(zhí)行,還能讓你了解它是如何執(zhí)行的。在這個(gè)例子中,對(duì)變量運(yùn)行的 whatis 命令給了你一個(gè)線(xiàn)索,即你的 alpha 和 beta 變量是整數(shù),這可能會(huì)喚起你對(duì) printf 語(yǔ)法的記憶,使你意識(shí)到在你的 printf 語(yǔ)句中,你必須使用 %d 來(lái)代替 %s。做了這個(gè)改變,就可以讓?xiě)?yīng)用程序按預(yù)期運(yùn)行,沒(méi)有更明顯的錯(cuò)誤存在。
當(dāng)代碼編譯后發(fā)現(xiàn)有 bug 存在時(shí),特別令人沮喪,但最棘手的 bug 就是這樣,如果它們很容易被發(fā)現(xiàn),那它們就不是 bug 了。使用 GDB 是獵取并消除它們的一種方法。
下載我們的速查表
生活的真相就是這樣,即使是最基本的編程,代碼也會(huì)有 bug。并不是所有的錯(cuò)誤都會(huì)導(dǎo)致應(yīng)用程序無(wú)法運(yùn)行(甚至無(wú)法編譯),也不是所有的錯(cuò)誤都是由錯(cuò)誤的代碼引起的。有時(shí),bug 是基于一個(gè)特別有創(chuàng)意的用戶(hù)所做的意外的選擇組合而間歇性發(fā)生的。有時(shí),程序員從他們自己的代碼中使用的庫(kù)中繼承了 bug。無(wú)論原因是什么,bug 基本上無(wú)處不在,程序員的工作就是發(fā)現(xiàn)并消除它們。
GNU 調(diào)試器是一個(gè)尋找 bug 的有用工具。你可以用它做的事情比我在本文中演示的要多得多。你可以通過(guò) GNU Info 閱讀器來(lái)了解它的許多功能:
$ info gdb
無(wú)論你是剛開(kāi)始學(xué)習(xí) GDB 還是專(zhuān)業(yè)人員的,提醒一下你有哪些命令是可用的,以及這些命令的語(yǔ)法是什么,都是很有幫助的。
- 下載 GDB 速查表
網(wǎng)站題目:學(xué)習(xí)使用GDB調(diào)試代碼
本文地址:http://fisionsoft.com.cn/article/dhjhehh.html


咨詢(xún)
建站咨詢(xún)

