日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
學(xué)習(xí)使用GDB調(diào)試代碼

使用 GNU 調(diào)試器來解決你的代碼問題。

成都創(chuàng)新互聯(lián)公司主要從事成都網(wǎng)站設(shè)計(jì)、網(wǎng)站制作、網(wǎng)頁設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)公安,10多年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):18980820575

GNU 調(diào)試器常以它的命令 gdb 稱呼它,它是一個(gè)交互式的控制臺(tái),可以幫助你瀏覽源代碼、分析執(zhí)行的內(nèi)容,其本質(zhì)上是對(duì)錯(cuò)誤的應(yīng)用程序中出現(xiàn)的問題進(jìn)行逆向工程。

故障排除的麻煩在于它很復(fù)雜。GNU 調(diào)試器 并不是一個(gè)特別復(fù)雜的應(yīng)用程序,但如果你不知道從哪里開始,甚至不知道何時(shí)和為何你可能需要求助于 GDB 來進(jìn)行故障排除,那么它可能會(huì)讓人不知所措。如果你一直使用 print、echo 或 printf 語句來調(diào)試你的代碼,當(dāng)你開始思考是不是還有更強(qiáng)大的東西時(shí),那么本教程就是為你準(zhǔn)備的。

有錯(cuò)誤的代碼

要開始使用 GDB,你需要一些代碼。這里有一個(gè)用 C++ 寫的示例應(yīng)用程序(如果你一般不使用 C++ 編寫程序也沒關(guān)系,在所有語言中原理都是一樣的),其來源于 猜謎游戲系列 中的一個(gè)例子。

  
 
 
  1. #include
  2. #include //srand
  3. #include //printf
  4.  
  5. using namespace std;
  6.  
  7. int main () {
  8.  
  9. srand (time(NULL));
  10. int alpha = rand() % 8;
  11. cout << "Hello world." << endl;
  12. int beta = 2;
  13.  
  14. printf("alpha is set to is %s\n", alpha);
  15. printf("kiwi is set to is %s\n", beta);
  16.  
  17. return 0;
  18. } // main

這個(gè)代碼示例中有一個(gè) bug,但它確實(shí)可以編譯(至少在 GCC 5 的時(shí)候)。如果你熟悉 C++,你可能已經(jīng)看到了,但這是一個(gè)簡(jiǎn)單的問題,可以幫助新的 GDB 用戶了解調(diào)試過程。編譯并運(yùn)行它就可以看到錯(cuò)誤:

  
 
 
  1. $ g++ -o buggy example.cpp
  2. $ ./buggy
  3. Hello world.
  4. Segmentation fault

排除段故障

從這個(gè)輸出中,你可以推測(cè)變量 alpha 的設(shè)置是正確的,因?yàn)榉駝t的話,你就不會(huì)看到它后面的那行代碼執(zhí)行。當(dāng)然,這并不總是正確的,但這是一個(gè)很好的工作理論,如果你使用 printf 作為日志和調(diào)試器,基本上也會(huì)得出同樣的結(jié)論。從這里,你可以假設(shè) bug 在于成功打印的那一行之后的某行。然而,不清楚錯(cuò)誤是在下一行還是在幾行之后。

GNU 調(diào)試器是一個(gè)交互式的故障排除工具,所以你可以使用 gdb 命令來運(yùn)行錯(cuò)誤的代碼。為了得到更好的結(jié)果,你應(yīng)該從包含有調(diào)試符號(hào)的源代碼中重新編譯你的錯(cuò)誤應(yīng)用程序。首先,看看 GDB 在不重新編譯的情況下能提供哪些信息:

  
 
 
  1. $ gdb ./buggy
  2. Reading symbols from ./buggy...done.
  3. (gdb) start
  4. Temporary breakpoint 1 at 0x400a44
  5. Starting program: /home/seth/demo/buggy
  6.  
  7. Temporary breakpoint 1, 0x0000000000400a44 in main ()
  8. (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)一步的見解。很直觀,GDB 用來啟動(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

  
 
 
  1. (gdb) continue
  2. Continuing.
  3. Hello world.
  4.  
  5. Program received signal SIGSEGV, Segmentation fault.
  6. 0x00007ffff71c0c0b in vfprintf () from /lib64/libc.so.6
  7. (gdb)

毫不意外:應(yīng)用程序在打印 “Hello world” 后不久就崩潰了,但 GDB 可以提供崩潰發(fā)生時(shí)正在發(fā)生的函數(shù)調(diào)用。這有可能就足夠你找到導(dǎo)致崩潰的 bug,但為了更好地了解 GDB 的功能和一般的調(diào)試過程,想象一下,如果問題還沒有變得清晰,你想更深入地挖掘這段代碼發(fā)生了什么。

用調(diào)試符號(hào)編譯代碼

要充分利用 GDB,你需要將調(diào)試符號(hào)編譯到你的可執(zhí)行文件中。你可以用 GCC 中的 -g 選項(xiàng)來生成這個(gè)符號(hào):

  
 
 
  1. $ g++ -o debuggy example.cpp
  2. $ ./debuggy
  3. Hello world.
  4. Segmentation fault

將調(diào)試符號(hào)編譯到可執(zhí)行文件中的結(jié)果是得到一個(gè)大得多的文件,所以通常不會(huì)分發(fā)它們,以增加便利性。然而,如果你正在調(diào)試開源代碼,那么用調(diào)試符號(hào)重新編譯測(cè)試是有意義的:

  
 
 
  1. $ ls -l *buggy* *cpp
  2. -rw-r--r--    310 Feb 19 08:30 debug.cpp
  3. -rwxr-xr-x  11624 Feb 19 10:27 buggy*
  4. -rwxr-xr-x  22952 Feb 19 10:53 debuggy*

用 GDB 調(diào)試

加載新的可執(zhí)行文件(本例中為 debuggy)以啟動(dòng) GDB:

  
 
 
  1. $ gdb ./debuggy
  2. Reading symbols from ./debuggy...done.
  3. (gdb) start
  4. Temporary breakpoint 1 at 0x400a44
  5. Starting program: /home/seth/demo/debuggy
  6.  
  7. Temporary breakpoint 1, 0x0000000000400a44 in main ()
  8. (gdb)

如前所述,使用 start 命令進(jìn)行:

  
 
 
  1. (gdb) start
  2. Temporary breakpoint 1 at 0x400a48: file debug.cpp, line 9.
  3. Starting program: /home/sek/demo/debuggy
  4.  
  5. Temporary breakpoint 1, main () at debug.cpp:9
  6. 9       srand (time(NULL));
  7. (gdb)

這一次,自動(dòng)的 main 斷點(diǎn)可以指明 GDB 暫停的行號(hào)和該行包含的代碼。你可以用 continue 恢復(fù)正常操作,但你已經(jīng)知道應(yīng)用程序在完成之前就會(huì)崩潰,因此,你可以使用 next 關(guān)鍵字逐行步進(jìn)檢查你的代碼:

  
 
 
  1. (gdb) next
  2. 10 int alpha = rand() % 8;
  3. (gdb) next
  4. 11 cout << "Hello world." << endl;
  5. (gdb) next
  6. Hello world.
  7. 12 int beta = 2;
  8. (gdb) next
  9. 14 printf("alpha is set to is %s\n", alpha);
  10. (gdb) next
  11.  
  12. Program received signal SIGSEGV, Segmentation fault.
  13. 0x00007ffff71c0c0b in vfprintf () from /lib64/libc.so.6
  14. (gdb)

從這個(gè)過程可以確認(rèn),崩潰不是發(fā)生在設(shè)置 beta 變量的時(shí)候,而是執(zhí)行 printf 行的時(shí)候。這個(gè) bug 在本文中已經(jīng)暴露了好幾次(破壞者:向 printf 提供了錯(cuò)誤的數(shù)據(jù)類型),但暫時(shí)假設(shè)解決方案仍然不明確,需要進(jìn)一步調(diào)查。

設(shè)置斷點(diǎn)

一旦你的代碼被加載到 GDB 中,你就可以向 GDB 詢問到目前為止代碼所產(chǎn)生的數(shù)據(jù)。要嘗試數(shù)據(jù)自省,通過再次發(fā)出 start 命令來重新啟動(dòng)你的應(yīng)用程序,然后進(jìn)行到第 11 行。一個(gè)快速到達(dá) 11 行的簡(jiǎn)單方法是設(shè)置一個(gè)尋找特定行號(hào)的斷點(diǎn):

  
 
 
  1. (gdb) start
  2. The program being debugged has been started already.
  3. Start it from the beginning? (y or n) y
  4. Temporary breakpoint 2 at 0x400a48: file debug.cpp, line 9.
  5. Starting program: /home/sek/demo/debuggy
  6.  
  7. Temporary breakpoint 2, main () at debug.cpp:9
  8. 9       srand (time(NULL));
  9. (gdb) break 11
  10. Breakpoint 3 at 0x400a74: file debug.cpp, line 11.

建立斷點(diǎn)后,用 continue 繼續(xù)執(zhí)行:

  
 
 
  1. (gdb) continue
  2. Continuing.
  3.  
  4. Breakpoint 3, main () at debug.cpp:11
  5. 11 cout << "Hello world." << endl;
  6. (gdb)

現(xiàn)在暫停在第 11 行,就在 alpha 變量被設(shè)置之后,以及 beta 被設(shè)置之前。

用 GDB 進(jìn)行變量自省

要查看一個(gè)變量的值,使用 print 命令。在這個(gè)示例代碼中,alpha 的值是隨機(jī)的,所以你的實(shí)際結(jié)果可能與我的不同:

  
 
 
  1. (gdb) print alpha
  2. $1 = 3
  3. (gdb)

當(dāng)然,你無法看到一個(gè)尚未建立的變量的值:

  
 
 
  1. (gdb) print beta
  2. $2 = 0

使用流程控制

要繼續(xù)進(jìn)行,你可以步進(jìn)代碼行來到達(dá)將 beta 設(shè)置為一個(gè)值的位置:

  
 
 
  1. (gdb) next
  2. Hello world.
  3. 12  int beta = 2;
  4. (gdb) next
  5. 14  printf("alpha is set to is %s\n", alpha);
  6. (gdb) print beta
  7. $3 = 2

另外,你也可以設(shè)置一個(gè)觀察點(diǎn),它就像斷點(diǎn)一樣,是一種控制 GDB 執(zhí)行代碼流程的方法。在這種情況下,你知道 beta 變量應(yīng)該設(shè)置為 2,所以你可以設(shè)置一個(gè)觀察點(diǎn),當(dāng) beta 的值發(fā)生變化時(shí)提醒你:

  
 
 
  1. (gdb) watch beta > 0
  2. Hardware watchpoint 5: beta > 0
  3. (gdb) continue
  4. Continuing.
  5.  
  6. Breakpoint 3, main () at debug.cpp:11
  7. 11 cout << "Hello world." << endl;
  8. (gdb) continue
  9. Continuing.
  10. Hello world.
  11.  
  12. Hardware watchpoint 5: beta > 0
  13.  
  14. Old value = false
  15. New value = true
  16. main () at debug.cpp:14
  17. 14 printf("alpha is set to is %s\n", alpha);
  18. (gdb)

你可以用 next 手動(dòng)步進(jìn)完成代碼的執(zhí)行,或者你可以用斷點(diǎn)、觀察點(diǎn)和捕捉點(diǎn)來控制代碼的執(zhí)行。

用 GDB 分析數(shù)據(jù)

你可以以不同格式查看數(shù)據(jù)。例如,以八進(jìn)制值查看 beta 的值:

  
 
 
  1. (gdb) print /o beta
  2. $4 = 02

要查看其在內(nèi)存中的地址:

  
 
 
  1. (gdb) print /o beta
  2. $5 = 0x2

你也可以看到一個(gè)變量的數(shù)據(jù)類型:

  
 
 
  1. (gdb) whatis beta
  2. type = int

用 GDB 解決錯(cuò)誤

這種自省不僅能讓你更好地了解什么代碼正在執(zhí)行,還能讓你了解它是如何執(zhí)行的。在這個(gè)例子中,對(duì)變量運(yùn)行的 whatis 命令給了你一個(gè)線索,即你的 alpha 和 beta 變量是整數(shù),這可能會(huì)喚起你對(duì) printf 語法的記憶,使你意識(shí)到在你的 printf 語句中,你必須使用 %d 來代替 %s。做了這個(gè)改變,就可以讓應(yīng)用程序按預(yù)期運(yùn)行,沒有更明顯的錯(cuò)誤存在。

當(dāng)代碼編譯后發(fā)現(xiàn)有 bug 存在時(shí),特別令人沮喪,但最棘手的 bug 就是這樣,如果它們很容易被發(fā)現(xiàn),那它們就不是 bug 了。使用 GDB 是獵取并消除它們的一種方法。

下載我們的速查表

生活的真相就是這樣,即使是最基本的編程,代碼也會(huì)有 bug。并不是所有的錯(cuò)誤都會(huì)導(dǎo)致應(yīng)用程序無法運(yùn)行(甚至無法編譯),也不是所有的錯(cuò)誤都是由錯(cuò)誤的代碼引起的。有時(shí),bug 是基于一個(gè)特別有創(chuàng)意的用戶所做的意外的選擇組合而間歇性發(fā)生的。有時(shí),程序員從他們自己的代碼中使用的庫(kù)中繼承了 bug。無論原因是什么,bug 基本上無處不在,程序員的工作就是發(fā)現(xiàn)并消除它們。

GNU 調(diào)試器是一個(gè)尋找 bug 的有用工具。你可以用它做的事情比我在本文中演示的要多得多。你可以通過 GNU Info 閱讀器來了解它的許多功能:

  
 
 
  1. $ info gdb

無論你是剛開始學(xué)習(xí) GDB 還是專業(yè)人員的,提醒一下你有哪些命令是可用的,以及這些命令的語法是什么,都是很有幫助的。

  • 下載 GDB 速查表

網(wǎng)頁名稱:學(xué)習(xí)使用GDB調(diào)試代碼
文章來源:http://www.dlmjj.cn/article/dhjhehh.html