GDB 快速指南



什麼是 GNU 偵錯程式?

偵錯程式是一個執行其他程式的程式,允許使用者控制這些程式,並在出現問題時檢查變數。

GNU 偵錯程式,也稱為gdb,是 UNIX 系統上用於除錯 C 和 C++ 程式最流行的偵錯程式。

GNU 偵錯程式幫助您獲取以下資訊

  • 如果發生核心轉儲,程式在哪個語句或表示式崩潰?

  • 如果在執行函式時發生錯誤,程式的哪一行包含對該函式的呼叫,引數是什麼?

  • 程式變數在程式執行期間特定點的值是什麼?

  • 程式中特定表示式的結果是什麼?

GDB 如何除錯?

GDB 允許您將程式執行到特定點,然後停止並打印出該點某些變數的值,或者一次一行地逐步執行程式,並在執行每一行後打印出每個變數的值。

GDB 使用簡單的命令列介面。

注意事項

  • 儘管 GDB 可以幫助您找出與記憶體洩漏相關的錯誤,但它不是檢測記憶體洩漏的工具。

  • GDB 不能用於編譯有錯誤的程式,它也不能幫助修復這些錯誤。

GDB - 安裝

在進行安裝之前,請透過發出以下命令檢查您的 Unix 系統上是否已安裝 gdb

$gdb -help 

如果已安裝 GDB,則它將顯示 GDB 中所有可用的選項。如果未安裝 GDB,則繼續進行全新安裝。

您可以按照下面討論的簡單步驟在您的系統上安裝 GDB。

步驟 1:確保您具備安裝 gdb 的先決條件

  • 符合 ANSI 標準的 C 編譯器(推薦使用 gcc - 請注意,gdb 可以除錯由其他編譯器生成的程式碼)

  • 在您將要構建 gdb 的分割槽上需要 115 MB 的可用磁碟空間。

  • 在您將要安裝 gdb 的分割槽上需要 20 MB 的可用磁碟空間。

  • GNU 的解壓縮程式,gzip

  • make 實用程式 - 已知 GNU 版本可以正常工作,其他版本可能也可以。

步驟 2:ftp.gnu.org/gnu/gdb下載 gdb 原始碼發行版。(我們使用gdb-6.6.tar.gz作為這些說明的示例。)將發行版檔案放在您的構建目錄中。

步驟 3:在您的構建目錄中,解壓縮 gdb-6.6.tar.gz 並從存檔中提取原始檔。檔案提取完成後,將您的工作目錄更改為自動在您的構建目錄中建立的 gdb-6.6 目錄。

$ build> gzip -d gdb-6.6.tar.gz 
$ build> tar xfv gdb-6.6.tar 
$ build> cd gdb-6.6 

步驟 4:執行 configure 指令碼以根據您的平臺配置原始碼樹。

$ gdb-6.6> .⁄configure 

步驟 5:使用make實用程式構建 gdb。

$ gdb-6.6> make 

步驟 6:以 root 使用者身份登入並使用以下命令安裝 gdb。

$ gdb-6.6> make install 

步驟 7:如果需要,可以在安裝完成後刪除 gdb 構建目錄和存檔檔案以回收磁碟空間。

$ gdb-6.6> cd .. 
$ build> rm -r gdb-6.6 
$ build> rm gdb-6.6.tar 

您現在已在系統上安裝了 gdb,它可以立即使用了。

GDB - 除錯符號

除錯符號表將編譯後的二進位制程式中的指令對映到原始碼中相應的變數、函式或行。此對映可能類似於

  • 程式指令 ⇒ 專案名稱、專案型別、原始檔案、定義的行號。

符號表可以嵌入到程式中,也可以作為單獨的檔案儲存。因此,如果您計劃除錯您的程式,則需要建立一個包含除錯程式所需資訊的符號表。

我們可以推斷出關於符號表的以下事實

  • 符號表適用於程式的特定版本 - 如果程式發生更改,則必須建立一個新的表。

  • 除錯版本通常比零售(非除錯)版本更大且速度更慢;除錯版本包含符號表和其他輔助資訊。

  • 如果您想除錯自己沒有編譯的二進位制程式,則必須從作者處獲取符號表。

為了讓 GDB 能夠逐行從符號表中讀取所有這些資訊,我們需要以稍微不同的方式進行編譯。通常我們將程式編譯為

gcc hello.cc -o hello 

與其這樣做,我們需要使用 -g 標誌進行編譯,如下所示

gcc -g hello.cc -o hello 

GDB - 命令

GDB 提供了大量的命令,但是以下命令是最常用的命令

  • b main - 在程式的開頭設定斷點

  • b - 在當前行設定斷點

  • b N - 在第 N 行設定斷點

  • b +N - 在當前行以下 N 行設定斷點

  • b fn - 在函式“fn”的開頭設定斷點

  • d N - 刪除斷點編號 N

  • info break - 列出斷點

  • r - 執行程式直到斷點或錯誤

  • c - 繼續執行程式直到下一個斷點或錯誤

  • f - 執行直到當前函式完成

  • s - 執行程式的下一行

  • s N - 執行程式的下一 N 行

  • n - 與 s 相同,但它不會進入函式

  • u N - 執行直到到達當前行之前的 N 行

  • p var - 列印變數“var”的當前值

  • bt - 列印堆疊跟蹤

  • u - 在堆疊中向上移動一級

  • d - 在堆疊中向下移動一級

  • q - 退出 gdb

GDB - 除錯程式

入門:啟動和停止

  • gcc -g myprogram.c

    • 使用除錯選項 (-g) 編譯 myprogram.c。您仍然會得到一個 a.out,但它包含除錯資訊,允許您在 GDB 中使用變數和函式名稱,而不是原始記憶體位置(不好玩)。

  • gdb a.out

    • 使用檔案 a.out 開啟 GDB,但不執行程式。您將看到一個提示符 (gdb) - 所有示例均來自此提示符。

  • r

  • r arg1 arg2

  • r < file1

    • 三種執行之前載入的“a.out”的方法。您可以直接執行它 (r)、傳遞引數 (r arg1 arg2) 或輸入檔案。您通常會在執行之前設定斷點。

  • help

  • h breakpoints

    • 列出幫助主題 (help) 或獲取有關特定主題的幫助 (h breakpoints)。GDB 文件齊全。

  • q - 退出 GDB

單步執行程式碼

單步執行允許您跟蹤程式的路徑,並確定導致崩潰或返回無效輸入的程式碼。

  • l

  • l 50

  • l myfunction

    • 列出當前行 (l)、特定行 (l 50) 或函式 (l myfunction) 的 10 行原始碼。

  • next

    • 執行程式直到下一行,然後暫停。如果當前行是函式,它將執行整個函式,然後暫停。next 非常適合快速瀏覽您的程式碼。

  • step

    • 執行下一條指令,而不是下一行。如果當前指令是設定變數,則它與next相同。如果它是一個函式,它將跳轉到該函式,執行第一個語句,然後暫停。step 非常適合深入瞭解程式碼的細節。

  • finish

    • 完成執行當前函式,然後暫停(也稱為步出)。如果您不小心進入了一個函式,這很有用。

斷點或觀察點

斷點在除錯中扮演著重要的角色。當程式到達某個點時,它們會暫停(中斷)程式。您可以檢查和更改變數並恢復執行。當發生某些輸入失敗或需要測試輸入時,這很有用。

  • break 45

  • break myfunction

    • 在第 45 行或 myfunction 處設定斷點。程式到達斷點時將暫停。
  • watch x == 3

    • 設定觀察點,當條件更改時(當 x == 3 更改時)暫停程式。觀察點非常適合某些輸入 (myPtr != NULL),而無需在每個函式呼叫上中斷。

  • continue

    • 在斷點/觀察點暫停後恢復執行。程式將繼續執行,直到遇到下一個斷點/觀察點。

  • delete N

    • 刪除斷點 N(斷點在建立時編號)。

設定變數

在執行時檢視和更改變數是除錯的關鍵部分。嘗試向函式提供無效輸入或執行其他測試用例以查詢問題的根本原因。通常,您會在程式暫停時檢視/設定變數。

  • print x

    • 列印變數 x 的當前值。能夠使用原始變數名稱是需要 (-g) 標誌的原因;常規編譯的程式已刪除此資訊。

  • set x = 3

  • set x = y

    • 將 x 設定為設定的值 (3) 或另一個變數 (y)
  • call myfunction()

  • call myotherfunction(x)

  • call strlen(mystring)

    • 呼叫使用者定義的函式或系統函式。這非常有用,但要注意呼叫有錯誤的函式。

  • display x

    • 持續顯示變數 x 的值,該值在每次單步執行或暫停後顯示。如果您不斷檢查某個值,這很有用。

  • undisplay x

    • 刪除 display 命令顯示的變數的持續顯示。

回溯和更改幀

堆疊是當前函式呼叫的列表 - 它顯示您在程式中的位置。儲存單個函式呼叫的詳細資訊,例如引數。

  • bt

    • 回溯或列印當前函式堆疊以顯示您在當前程式中的位置。如果 main 呼叫函式 a(),a() 呼叫 b(),b() 呼叫 c(),則回溯為

  • c <= current location 
    b 
    a 
    main 
    
  • up

  • down

    • 在函式堆疊中向上或向下移動到下一個幀。如果您在c中,您可以移動到ba以檢查區域性變數。

  • return

    • 從當前函式返回。

處理訊號

訊號是在某些事件(例如計時器或錯誤)後丟擲的訊息。GDB 在遇到訊號時可能會暫停;您可能希望忽略它們。

  • handle [signalname] [action]

  • handle SIGUSR1 nostop

  • handle SIGUSR1 noprint

  • handle SIGUSR1 ignore

    • 指示GDB忽略特定訊號(SIGUSR1)的發生。忽略的級別各有不同。

GDB - 除錯示例

請仔細閱讀以下示例,瞭解除錯程式和核心轉儲的過程。

  • 除錯示例1

    此示例演示如何捕獲由於除零異常而引發的錯誤。

  • 除錯示例2

    此示例演示一個程式,由於記憶體未初始化而可能導致核心轉儲。

這兩個程式都是用C++編寫的,由於不同的原因導致核心轉儲。在學習完這兩個示例後,您應該能夠除錯產生核心轉儲的C或C++程式。

GDB - 總結

學習完本教程後,您應該已經很好地理解了如何使用GNU偵錯程式除錯C或C++程式。現在,學習其他偵錯程式的功能應該很容易,因為它們與GDB非常相似。強烈建議您也學習其他偵錯程式,以便熟悉它們的功能。

市場上有很多不錯的偵錯程式。

  • DBX偵錯程式 - 此偵錯程式隨Sun Solaris一起提供,您可以使用dbx的手冊頁(即man dbx)獲取有關此偵錯程式的完整資訊。

  • DDD偵錯程式 - 這是dbx的圖形版本,可在Linux上免費獲得。要了解完整詳情,請使用ddd的手冊頁(即man ddd)。

您可以從以下連結獲取有關GNU偵錯程式的全面詳細資訊:使用GDB進行除錯

廣告