1. 程式人生 > >使用gdb除錯程式完全教程

使用gdb除錯程式完全教程

                     

程式的除錯過程主要有:單步執行,跳入函式,跳出函式,設定斷點,設定觀察點,檢視變數。
本文將主要介紹linux下的強大除錯工具是怎麼完成這些工作的。

之所以要除錯程式,是因為程式的執行結果和預期結果不一致,或者程式出現執行時錯誤。
除錯的基本思想是:
分析現象 -> 假設錯誤原因 -> 產生新的現象去驗證假設

偵錯程式(如GDB)的目的是允許你在程式執行時進入到某個程式內部去看看該程式在做什麼,或者在該程式崩潰時它在做什麼。

GDB主要可以做4大類事(加上一些其他的輔助工作),以幫助使用者在程式執行過程中發現bug。
*    啟動您的程式,並列出可能會影響它執行的一些資訊
*    使您的程式在特定條件下停止下來
*    當程式停下來的時候,檢查發生了什麼
*    對程式做出相應的調整,這樣您就能嘗試糾正一個錯誤並繼續發現其它錯誤

您能使用GDB除錯用C、C++、Modula-2寫的程式等GNU Fortran編譯器準備好過後,GDB將提供對Fortran的支援

gdb引數選項詳解

gcc除錯相關編譯選項

GDB通過在命令列方式下輸入gdb來執行。啟動過後,GDB會從終端讀取命令,直到您輸入GDB命令quit使GDB退出。您能通過GDB命

gcc -g main.c
  • 1

gdb主要除錯的是C/C++的程式。要除錯C/C++的程式,首先在編譯時,必須要把除錯資訊加到可執行檔案中。使用編譯器(cc/gcc/g++)的 -g 引數即可。如:
如果沒有-g,將看不見程式的函式名和變數名,代替它們的全是執行時的記憶體地址。當用-g把除錯資訊加入,併成功編譯目的碼以後,看看如何用gdb來除錯。

要用gdb除錯程式,必須在編譯時加上-g和-ggdb選項,-g選項的作用是在可執行檔案中加入原始檔資訊,但並不是將原始檔嵌入可執行檔案,所以在除錯時必須保證gdb必須能找到原始檔.

-g 和 -ggdb 都是令 gcc 生成除錯資訊,但是它們也是有區別的

           
選項描述
g該選項可以利用作業系統的“原生格式(native format)”生成除錯資訊。GDB 可以直接利用這個資訊,其它偵錯程式也可以使用這個除錯資訊
ggdb使 GCC為GDB 生成專用的更為豐富的除錯資訊,但是,此時就不能用其他的偵錯程式來進行除錯了 (如 ddx)

-g 和 -ggdb 也是分級別的

               
選項描述
g1級別1(-g1)不包含區域性變數和與行號有關的除錯資訊,因此只能夠用於回溯跟蹤和堆疊轉儲之用。回溯跟蹤指的是監視程式在執行過程中的函式呼叫歷史,堆疊轉儲則是一種以原始的十六進位制格式儲存程式執行環境的方法,兩者都是經常用到的除錯手段
g2這是預設的級別,此時產生的除錯資訊包括擴充套件的符號表、行號、區域性或外部變數資訊
g3包含級別2中的所有除錯資訊,以及原始碼中定義的巨集

gdb引數選項(啟動)

啟動gdb的標準命令如下

gdb    [-help] [-nx] [-q] [-batch] [-cd=dir] [-f] [-b bps]          [-tty=dev] [-s symfile] [-e prog] [-se prog] [-c core]          [-x cmds] [-d dir] [prog[core|procID]]
  • 1
  • 2
  • 3

您能以無引數無選項的形式執行GDB,不過通常的情況是以一到兩個引數執行GDB,以待除錯的可執行程式名(-se指定)為引數和core dump檔案(-c指定)

但是我們啟動的時候,往往不需要指定-se和-c, 因為如果啟動gdb時候提供了引數, 那麼任何引數而非選項指明瞭一個可執行檔案及core 檔案(或者程序ID)

  • 所遇到的第一個未關聯選項標誌的引數與 ‘-se’ 選項等價
  • 第二個,如果存在,且是一個檔案的名字,則等價與 ‘-c’ 選項。
 

許多選項都有一個長格式與短格式;都會在這裡表示出來。如果你把一個長格式截短,只要不引起歧義,那麼它還是可以被識別。(如果你願意,你可以使用 ‘+’ 而非 ‘-’ 標記選項引數,不過我們在例子中仍然遵從通常的慣例)

           
選項描述
gdb 程式名, gdb 程式名 core您能用兩個引數來執行GDB,可執行程式名與core檔案
gdb 程式名 1234您可以以程序ID作為第二個引數,以調式一個正在執行的程序, 將會把gdb附在程序1234之上(除非您正好有個檔案叫1234,gdb總是先查詢core檔案)

啟動gdb的方法有以下幾種:
1.    gdb 
program也就是執行檔案,一般在當前目錄下。
所遇到的第一個未關聯選項標誌的引數與 ‘-se’ 選項等價
因此等價於gdb -se

  1. gdb  core

用gdb同時除錯一個執行程式和core檔案,core是程式非法執行後,core dump後產生的檔案。
相當於 gdb -se  -c core

  1. gdb 

如果程式是一個服務程式,那麼可以指定這個服務程式執行時的程序ID。gdb會自動attach上去,並除錯它。program應該在PATH環境變數中搜索得到。

gdb引數選項

                                                                                               
選項簡寫描述
-help-h列出所有選項,並附簡要說明
-symbols=file-s file讀出檔案(file)中的符號表
-write開通(enable)往可執行檔案和核心檔案寫的許可權
-exec=file-e file在適當時候把File作為可執行的檔案執行,來檢測與core dump結合的資料
-se File從指定檔案中讀取符號表資訊,並把它用在可執行檔案中
-core File-c File把File作為core dump來執行,除錯時core dump的core檔案。
-command=File-x File從File中執行GDB命令
-directory=Directory-d Directory把Dicrctory加入原始檔搜尋的路徑中,加入一個原始檔的搜尋路徑。預設搜尋路徑是環境變數中PATH所定義的路徑
-nx-n不從任何.gdbinit初始化檔案中執行命令。通常情況下,這些檔案中的命令是在所有命令選項和引數處理完後才執行
-quiet-q“Quiet”.不輸入介紹和版權資訊。這些資訊輸出在batch模式下也被關閉
-batch執行batch模式。在處理完所有用’-x’選項指定的命令檔案(還有’.gdbi-nit’,如果沒禁用)後退出,並返回狀態碼0.如果在命令檔案中的命令被執行時發生錯誤,則退出,並返回狀態碼非0.batch模式對於執行GDB作為過濾器也許很有用,比如要從另一臺電腦上下載並執行一個程式;為了讓這些更有用,當在batch模式下執行時,訊息:Program exited normally.(不論什麼時候,一個程式在GDB控制下終止執行,這條訊息都會正常發出.),將不會發出
-cd=Directory執行GDB,使用Directory作為它的工作目錄,取代當前工作目錄
-fullname-f當Emacs讓GDB作為一個子程序執行時,設定這個選項.它告訴GDB每當一個堆疊結構(棧幀)顯示出來(包括每次程式停止)就用標準的,認同的方式輸出檔案全名和行號.這裡,認同的格式看起來像兩個’ 32’字元,緊跟檔名,行號和字元位置(由冒號,換行符分隔).Emacs同GDB的介面程式使用這兩個’ 32’字元作為一個符號為框架來顯示原始碼
-bBAUDRATE設定行速(波特率或bits/s).在遠端除錯中GDB在任何序列介面中使用的行速
-tty=Device使用Device作為你程式執行的標準輸入輸出

內部命令(除錯)

                                                                                                                                               
命令描述
file [filename]裝入想要除錯的可執行檔案
kill [filename]終止正在除錯的程式
break [file:]function在(file檔案的)function函式中設定一個斷點
clear刪除一個斷點,這個命令需要指定程式碼行或者函式名作為引數
run [arglist]執行您的程式 (如果指定了arglist,則將arglist作為引數執行程式)
btBacktrace: 顯示程式堆疊資訊
print expr打印表達式的值
continue繼續執行您的程式 (在停止之後,比如在一個斷點之後)
list列出產生執行檔案的原始碼的一部分
next單步執行 (在停止之後); 跳過函式呼叫
nexti執行下一行的原始碼中的一條彙編指令
set設定變數的值。例如:set nval=54 將把54儲存到nval變數中
step單步執行 (在停止之後); 進入函式呼叫
stepi繼續執行程式下一行原始碼中的彙編指令。如果是函式呼叫,這個命令將進入函式的內部,單步執行函式中的彙編程式碼
watch使你能監視一個變數的值而不管它何時被改變
rwatch指定一個變數,如果這個變數被讀,則暫停程式執行,在偵錯程式中顯示資訊,並等待下一個除錯命令。參考rwatch和watch命令
awatch指定一個變數,如果這個變數被讀或者被寫,則暫停程式執行,在偵錯程式中顯示資訊,並等待下一個除錯命令。參考rwatch和watch命令
Ctrl-C在當前位置停止執行正在執行的程式,斷點在當前行
disable禁止斷點功能,這個命令需要禁止的斷點在斷點列表索引值作為引數
display在斷點的停止的地方,顯示指定的表示式的值。(顯示變數)
undisplay刪除一個display設定的變數顯示。這個命令需要將display list中的索引做引數
enable允許斷點功能,這個命令需要允許的斷點在斷點列表索引值作為引數
finish繼續執行,直到當前函式返回
ignore忽略某個斷點制定的次數。例:ignore 4 23 忽略斷點4的23次執行,在第24次的時候中斷
info [name]檢視name資訊
load動態載入一個可執行檔案到偵錯程式
xbreak在當前函式的退出的點上設定一個斷點
whatis顯示變數的值和型別
ptype顯示變數的型別
return強制從當前函式返回
txbreak在當前函式的退出的點上設定一個臨時的斷點(只可使用一次)
make使你能不退出 gdb 就可以重新產生可執行檔案
shell使你能不離開 gdb 就執行 UNIX shell 命令
help [name]顯示GDB命令的資訊,或者顯示如何使用GDB的總體資訊
quit退出gdb
 

要得到所有使用GDB的資料,請參考Using GDB: A Guide to the GNU Source-Level  Debugger,  by Richard M. Stallman and Roland H. Pesch.  當用info檢視的時候,也能看到相同的文章

gdb的命令實在太多了,我們不可能全部列出來, 因此只列出了一部分,我們將在下一節”gdb幫助”中幫助在除錯的過程中通過help來檢視gdb的除錯命令

gdb幫助

我們知道gdb除錯的命令是非常多的, 我們不可能完全記住有些記住的用法也可能不太熟悉,那麼我們在使用的過程中,如果希望檢視某個命令的幫助資訊,可以使用gdb除錯幫助資訊

啟動gdb後,進入gdb的除錯環境中,就可以使用gdb的命令開始除錯程式了。

gdb
  • 1

啟動gdb

gdb的命令可以使用help除錯命令來檢視,如下所示: 

help
  • 1

gdb help

 

注意我們此處所說的help除錯幫助命令與之前在終端中

gdb的命令很多,gdb將之分成許多種類。help命令只是列出gdb的命令種類

                                                   
種類描述
aliasesAliases of other commands
breakpointsMaking program stop at certain points
dataExamining data
filesSpecifying and examining files
internalsMaintenance commands
obscureObscure features
runningRunning the program
stackExamining the stack
statusStatus inquiries
supportSupport facilities
tracepointsTracing of program execution without stopping the program
user-defined – User-defined commands

如果要看其中的命令,可以使用help  命令。
help stack

gdb help stack

或者help breakpoints

gdb help breakpoints

也可以直接用help [command]來檢視命令的幫助。
比如我們知道break可以插入一個斷點, 我們就檢視它的詳細資訊

help break
  • 1

gdb help break

gdb中,輸入命令時,可以不用輸入全部命令,只用輸入命令的前幾個字元就可以了。當然,命令的前幾個字元應該標誌著一個惟一的命令,在Linux下,可以按兩次TAB鍵來補齊命令的全稱,如果有重複的,那麼gdb會把它全部列出來。

gdb tabtab

要退出gdb時,只用輸入quit或其簡稱q就行了。

gdb使用

gdb中執行Linux的shell程式

在gdb環境中,可以執行Linux的shell命令

shell <command string>
  • 1

gdb shell

呼叫Linux的shell來執行,環境變數SHELL中定義的Linux的shell將會用來執行。如果SHELL沒有定義,那就使用Linux的標準shell:/bin/sh(在Windows中使用Command.com或cmd.exe)

還有一個gdb命令是make:

make <make-args>
  • 1

可以在gdb中執行make命令來重新build自己的程式。這個命令等價於shell make

在gdb中執行程式

當以gdb 方式啟動gdb後,gdb會在PATH路徑和當前目錄中搜索的原始檔。如要確認gdb是否讀到原始檔,可使用l或list命令,看看gdb是否能列出原始碼。
在gdb中,執行程式使用r或是run命令。程式的執行,有可能需要設定下面四方面的事。

程式執行引數

set args 可指定執行時引數。如:

set args 10 20 30 40 50
  • 1

show args 命令可以檢視設定好的執行引數。

執行環境

                   
引數描述
path 可設定程式的執行路徑
show paths檢視程式的執行路徑
set environment varname [=value]設定環境變數。如:set env USER=hchen
show environment [varname]檢視環境變數

工作目錄

           
引數描述
cd 相當於shell的cd命令
pwd顯示當前的所在目錄

程式的輸入輸出

               
引數描述
info terminal顯示程式用到的終端的模式
run > outfile使用重定向控制程式輸出
tty /dev/ttybtty命令可以指寫輸入輸出的終端裝置

除錯已執行的程式

除錯已經執行的程式有兩種方法:

  • 在Linux下用ps(第一章已經對ps作了介紹)檢視正在執行的程式的PID(程序ID),然後用gdb  PID格式掛接正在執行的程式。

  • 先用gdb 關聯上原始碼,並進行gdb,在gdb中用attach命令來掛接程序的PID,並用detach來取消掛接的程序。

暫停/恢復程式執行

除錯程式中,暫停程式執行是必需的,gdb可以方便地暫停程式的執行。可以設定程式在哪行停住,在什麼條件下停住,在收到什麼訊號時停往等,以便於使用者檢視執行時的變數,以及執行時的流程。
當程序被gdb停住時,可以使用info program 來檢視程式是否在執行、程序號、被暫停的原因。
在gdb中,有以下幾種暫停方式:斷點(BreakPoint)、觀察點(WatchPoint)、捕捉點(CatchPoint)、訊號(Signals)及執行緒停止(Thread Stops)。
如果要恢復程式執行,可以使用c或是continue命令。

設定斷點(BreakPoint)

用break命令來設定斷點。有下面幾種設定斷點的方法:

                                   
引數描述
break 在進入指定函式時停住。C++中可以使用class::function或function(type,type)格式來指定函式名
break 在指定行號停住
break +offset和beak -offset在當前行號的前面或後面的offset行停住。offiset為自然數
break filename:linenum在原始檔filename的linenum行處停住
break filename:function在原始檔filename的function函式的入口處停住
break *address在程式執行的記憶體地址處停住
break該命令沒有引數時,表示在下一條指令處停住
break … if condition表示條件,在條件成立時停住。比如在迴圈體中,可以設定break if i=100,表示當i為100時停住程式

檢視斷點時,可使用info命令,如下所示(注:n表示斷點號):

           
引數描述
info breakpoints [n]檢視斷點
info break [n]檢視斷點

設定觀察點(WatchPoint)

觀察點一般用來觀察某個表示式(變數也是一種表示式)的值是否變化了。如果有變化,馬上停住程式。有下面的幾種方法來設定觀察點:

                   
引數描述
watch 為表示式(變數)expr設定一個觀察點。一旦表示式值有變化時,馬上停住程式
rwatch 當表示式(變數)expr被讀時,停住程式
awatch 當表示式(變數)的值被讀或被寫時,停住程式
info watchpoints列出當前設定的所有觀察點

設定捕捉點(CatchPoint)

可設定捕捉點來補捉程式執行時的一些事件。如載入共享庫(動態連結庫)或是C++的異常。設定捕捉點的格式為:

           
引數描述
catch 當event發生時,停住程式
tcatch 只設置一次捕捉點,當程式停住以後,應點被自動刪除

event可以是下面的內容

                               
引數描述
throw一個C++丟擲的異常 (throw為關鍵字)
catch一個C++捕捉到的異常 (catch為關鍵字)
exec呼叫系統呼叫exec時(exec為關鍵字,目前此功能只在HP-UX下有用)
fork呼叫系統呼叫fork時(fork為關鍵字,目前此功能只在HP-UX下有用)
vfork呼叫系統呼叫vfork時(vfork為關鍵字,目前此功能只在HP-UX下有)
load 或 load 載入共享庫(動態連結庫)時 (load為關鍵字,目前此功能只在HP-UX下有用)
unload 或 unload 解除安裝共享庫(動態連結庫)時 (unload為關鍵字,目前此功能只在HP-UX下有用)

維護停止點

上面說了如何設定程式的停止點,gdb中的停止點也就是上述的三類。在gdb中,如果覺得已定義好的停止點沒有用了,可以使用delete、clear、disable、enable這幾個命令來進行維護

                                   
引數描述
Clear清除所有的已定義的停止點
clear 和clear 清除所有設定在函式上的停止點
clear 和clear 清除所有設定在指定行上的停止點
delete [breakpoints] [range…]刪除指定的斷點,breakpoints為斷點號。如果不指定斷點號,則表示刪除所有的斷點。range 表示斷點號的範圍(如:3-7)。其簡寫命令為d,比刪除更好的一種方法是disable停止點。disable了的停止點,gdb不會刪除,當還需要時,enable即可,就好像回收站一樣
disable [breakpoints] [range…]disable所指定的停止點,breakpoints為停止點號。如果什麼都不指定,表示disable所有的停止點。簡寫命令是dis
enable [breakpoints] [range…]enable所指定的停止點,breakpoints為停止點號
enable [breakpoints] once range…enable所指定的停止點一次,當程式停止後,該停止點馬上被gdb自動disable
enable [breakpoints] delete range…enable所指定的停止點一次,當程式停止後,該停止點馬上被gdb自動刪除

停止條件維護

前面在介紹設定斷點時,提到過可以設定一個條件,當條件成立時,程式自動停止。這是一個非常強大的功能,這裡,專門介紹這個條件的相關維護命令。
一般來說,為斷點設定一個條件,可使用if關鍵詞,後面跟其斷點條件。並且,條件設定好後,可以用condition命令來修改斷點的條件(只有break和watch命令支援if,catch目前暫不支援if)。

               
引數描述
condition  修改斷點號為bnum的停止條件為expression
condition 清除斷點號為bnum的停止條件
ignore  還有一個比較特殊的維護命令ignore,可以指定程式執行時,忽略停止條件幾次。表示忽略斷點號為bnum的停止條件count次

為停止點設定執行命令

可以使用gdb提供的command命令來設定停止點的執行命令。也就是說,當執行的程式在被停住時,我們可以讓其自動執行一些別的命令,這很有利行自動化除錯。

commands [bnum]... command-list ...end
  • 1
  • 2
  • 3

為斷點號bnum指定一個命令列表。當程式被該斷點停住時,gdb會依次執行命令列表中的命令。
例如:

break foo if x>0commandsprintf "x is %d/n",xcontinueend
  • 1
  • 2
  • 3
  • 4
  • 5

斷點設定在函式foo中,斷點條件是x>0,如果程式被斷住後,也就是一旦x的值在foo函式中大於0,gdb會自動打印出x的值,並繼續執行程式。
如果要清除斷點上的命令序列,那麼只要簡單地執行一下commands命令,並直接在輸入end就行了。

斷點選單

在C++中,可能會重複出現同一個名字的函式若干次(函式過載)。在這種情況下,break 不能告訴gdb要停在哪個函式的入口。當然,可以使用break

恢復程式執行和單步除錯

當程式被停住後,可以用continue命令恢復程式的執行直到程式結束,或下一個斷點到來。也可以使用step或next命令單步跟蹤程式。
continue [ignore-count]
c [ignore-count]
fg [ignore-count]
恢復程式執行,直到程式結束,或是下一個斷點到來。ignore-count表示忽略其後的斷點次數。continue,c,fg三個命令都是一樣的意思。
step 
單步跟蹤,如果有函式呼叫,它會進入該函式。進入函式的前提是,此函式被編譯有debug資訊。很像VC等工具中的step in。後面可以加count也可以不加,不加表示一條條地執行,加表示執行後面的count條指令,然後再停住。
next 
同樣單步跟蹤,如果有函式呼叫,它不會進入該函式(很像VC等工具中的step over)。後面可以加count也可以不加,不加表示一條條地執行,加表示執行後面的count條指令,然後再停住。
set step-mode
set step-mode on
開啟step-mode模式。在進行單步跟蹤時,程式不會因為沒有debug資訊而不停住。這個引數有很利於檢視機器碼。
set step-mod off
關閉step-mode模式。
finish
執行程式,直到當前函式完成返回。並列印函式返回時的堆疊地址和返回值及引數值等資訊。
until 或 u
當厭倦了在一個迴圈體內單步跟蹤時,這個命令可以執行程式直到退出迴圈體。
stepi 或 si
nexti 或 ni
單步跟蹤一條機器指令。一條程式程式碼有可能由數條機器指令完成,stepi和nexti可以單步執行機器指令。與之一樣有相同功能的命令是display/i $pc,當執行完這個命令後,單步跟蹤會在顯示出程式程式碼的同時顯示出機器指令(也就是彙編程式碼)。
9. 訊號(Signals)
訊號是一種軟中斷,是一種處理非同步事件的方法。
一般來說,作業系統都支援許多訊號,尤其是Linux,比較重要的應用程式一般都會處理訊號。Linux定義了許多訊號,比如SIGINT表示中斷字元訊號,也就是Ctrl+C的訊號,SIGBUS表示硬體故障的訊號;SIGCHLD表示子程序狀態改變訊號;SIGKILL表示終止程式執行的訊號等。訊號量程式設計是UNIX下非常重要的一種技術。
gdb有能力在除錯程式的時候處理任何一種訊號。可以告訴gdb需要處理哪一種訊號;可以要求gdb收到所指定的訊號時,馬上停住正在執行的程式,以供使用者進行除錯。可用gdb的handle命令來完成這一功能。
handle 

例項

進入gdb除錯環境

 

list n | list | list 函式名 
  l n | l | l 函式名

在除錯過程中檢視原始檔,n為原始檔的行號,每次顯示10行。
list可以簡寫為l,不帶任何引數的l表示從當前執行行檢視。

 

注意:在(gdb)中直接回車,表示執行上一條命令。

   

start | s

開始執行程式,並main函式的停在第一條語句處。

 

(gdb)run|r

連續執行程式,直到遇到斷點

 

(gdb)continue|c

繼續執行程式,直到下個斷點

 

(gdb)next|n

執行下一行語句

 

(gdb)step|s

進入正在執行的函式內部

 

(gdb)finish

一直執行到當前函式返回,即跳出當前函式,執行其呼叫函式

變數資訊管理

 

(gdb)info 變數名|i 變數名|i locals

i變數名檢視一個變數的值,i locals檢視所有區域性變數的值
修改變數的值

 

(gdb)set var 變數名=變數值

   

(gdb) print 表示式

打印表達式,通過表示式可以修改變數的值,p 變數名=變數值

 

(gdb)display  變數名

使得程式每次停下來都會顯示變數的值

x/nbx 變數名

檢視從變數名開始的n個位元組,例x/7bx input 表示檢視從變數input開始的7個記憶體單元的內容

檢視函式呼叫棧

 

(gdb)backtrace|bt

檢視其呼叫函式的資訊

 

(gdb)frame n|f n

n為棧的層次,然後可以用其他命令(info)檢視此級別的變數資訊

斷點管理

設定斷點

 

break n|break 函式名|b n| b 函式名|b n(函式名)if 條件

n為行號,新增if表示設定條件斷點,只有條件為真時,才中斷

檢視斷點

 

info breakpoints|i breakpoints

刪除斷點

 

delete breakpoints n

使斷點失效

 

disable breakpoints n

使斷點生效

 

enable breakpoints n
  其中n為斷點的序列號,可以用info breakpoints檢視

觀察點管理

斷點是程式執行到某行程式碼是觸發,觀察點是程式訪問某個記憶體單元時觸發

 

(gdb)watch 變數名

當程式訪問變數名指定的記憶體單元時,停止程式

 

info watchpoints|delete watchpoints
  類似斷點管理

退出gdb環境

 

(gdb)quit | q