自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

學(xué)習(xí)使用 GDB 調(diào)試代碼

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

[[387491]]

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

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

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

有錯誤的代碼

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

  1. #include <iostream>
  2. #include <stdlib.h> //srand
  3. #include <stdio.h> //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

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

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

排除段故障

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

GNU 調(diào)試器是一個交互式的故障排除工具,所以你可以使用 gdb 命令來運(yùn)行錯誤的代碼。為了得到更好的結(jié)果,你應(yīng)該從包含有調(diào)試符號的源代碼中重新編譯你的錯誤應(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)你以一個二進(jìn)制可執(zhí)行文件作為參數(shù)啟動 GDB 時,GDB 會加載該應(yīng)用程序,然后等待你的指令。因為這是你第一次在這個可執(zhí)行文件上運(yùn)行 GDB,所以嘗試重復(fù)這個錯誤是有意義的,希望 GDB 能夠提供進(jìn)一步的見解。很直觀,GDB 用來啟動它所加載的應(yīng)用程序的命令就是 start。默認(rèn)情況下,GDB 內(nèi)置了一個斷點,所以當(dāng)它遇到你的應(yīng)用程序的 main 函數(shù)時,它會暫停執(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ā)生時正在發(fā)生的函數(shù)調(diào)用。這有可能就足夠你找到導(dǎo)致崩潰的 bug,但為了更好地了解 GDB 的功能和一般的調(diào)試過程,想象一下,如果問題還沒有變得清晰,你想更深入地挖掘這段代碼發(fā)生了什么。

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

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

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

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

  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)以啟動 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)

這一次,自動的 main 斷點可以指明 GDB 暫停的行號和該行包含的代碼。你可以用 continue 恢復(fù)正常操作,但你已經(jīng)知道應(yīng)用程序在完成之前就會崩潰,因此,你可以使用 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)

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

設(shè)置斷點

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

  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.

建立斷點后,用 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)行變量自省

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

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

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

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

使用流程控制

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

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

  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 手動步進(jìn)完成代碼的執(zhí)行,或者你可以用斷點、觀察點和捕捉點來控制代碼的執(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

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

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

用 GDB 解決錯誤

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

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

下載我們的速查表

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

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

  1. $ info gdb

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

 

責(zé)任編輯:龐桂玉 來源: Linux中國
相關(guān)推薦

2021-07-28 08:53:53

GoGDB調(diào)試

2015-08-14 09:21:09

gdb工具調(diào)試 Go

2025-03-31 03:25:00

2022-07-25 07:57:19

工具代碼調(diào)試

2017-02-06 18:42:37

Linuxgdb程序

2021-06-04 05:18:29

ARM程序Gdbserver

2010-06-04 17:48:20

Linux編程工具

2022-09-15 14:56:12

GDB調(diào)試鴻蒙

2023-11-22 13:13:54

多線程死鎖

2009-12-04 13:31:50

VS代碼調(diào)試

2022-12-19 10:10:07

GDB命令

2010-03-26 15:41:39

Python腳本

2017-09-12 15:11:12

Chrome

2009-06-03 14:42:21

Eclipse調(diào)試調(diào)試Java程序

2019-12-06 14:30:41

GNU調(diào)試器GDB修復(fù)代碼

2021-07-05 11:00:43

GDB??臻g編程語言

2024-11-25 16:08:57

Python代碼代碼調(diào)試

2023-05-04 12:39:27

GDB命令程序

2009-12-14 10:57:34

Ruby調(diào)試器

2012-05-21 10:13:05

XCode調(diào)試技巧
點贊
收藏

51CTO技術(shù)棧公眾號