GDB(GNU Debugger)是GCC的調試工具。其功能強大,現描述如下:
GDB主要幫忙你完成下面四個方面的功能:
1.啟動你的程式,可以按照你的自訂的要求隨心所欲的運行程式。
2.可讓被調試的程式在你所指定的調置的斷點處停住。(斷點可以是條件運算式)
3.當程式被停住時,可以檢查此時你的程式中所發生的事。
4.動態改變你程式的執行環境。
如果不開啟-g或者-ggdb(GDB專用)調試開關,GCC編譯時間不會加入調試資訊,因為這會增大產生代碼的體積。GCC採用了分級調試,通過在-g選項後附加數字1、2或3來指定在代碼中加入調試資訊量。
預設的層級是2(-g2),此時調試資訊包括擴充的符號表、行號、局部或外部變數資訊。
層級3(- g3)包含層級2中的調試資訊和原始碼中定義的宏。
層級1(- g1)不包含局部變數和與行號有關的調試資訊,只能用於回溯跟蹤和堆棧轉儲之用。[ 回溯跟蹤指的是監視程式在運行過程中的函數調用曆史,堆棧轉儲則是一種以原始的十六進位格式儲存程式執行環境的方法,兩者都是經常用到的調試手段。] 。
例如編譯foo.c時產生有調試資訊的執行檔案:
$ gcc –g –o foo foo.c
然後我們就可以使用GDB來跟蹤了。GDB使用方法如下:
1. GDB的啟動:
1、 gdb <program>
program也就是你的執行檔案,一般在目前的目錄下。
2、 gdb <program> core
用gdb同時調試一個運行程式和core檔案,core是程式非法執行後core dump後產生的檔案。
3、 gdb <program> <進程ID>
如果你的程式是一個服務程式,那麼你可以指定這個服務程式運行時的進程ID。gdb會自動attach上去,並調試他。program應該在PATH環境變數中搜尋得到。
GDB啟動時,可以加上一些GDB的啟動開關,詳細的開關可以用gdb -help查看。下面先說幾條常用的命令:
直接按下斷行符號表示上一條命令
break 設定斷點,
break 10 設定斷點,在來源程式第10行
break func 設定斷點,在func函數入口處
info break 查看斷點資訊
run 運行程式,可簡寫為r
next 單步跟蹤,函數調用當作一條簡單語句執行,可簡寫為n
step 單步跟蹤,函數調進入被調用函數體內,可簡寫為s
stepi 或 si 單步跟蹤一條機器指令
nexti 或 ni 單步跟蹤一條機器指令
continue 繼續運行程式,可簡寫為 c
print 列印變數、字串、運算式等的值,可簡寫為p
p count 列印count的值
p cou1+cou2+cou3 列印運算式值
bt 查看函數堆棧
finish 退出函數
quit 退出GDB
shell 不退出GDB就使用shell命令
make <make-args> 不退出GDB就重新編譯器
set args 指定運行時參數。(如:set args 10 20 30 40 50)
show args 查看設定好的運行參數。
path <dir> 設定程式的運行路徑。
show paths 查看程式的運行路徑。
set environment varname [=value] 設定環境變數。如:set env USER=hchen
show environment [varname] 查看環境變數。
cd <dir> 相當於shell的cd命令。
pwd 顯示當前的所在目錄。
info terminal 顯示程式用到的終端模式。
tty 指寫輸入輸出的終端裝置。如:tty /dev/ttyb
until 在一個迴圈體內單步跟蹤時,該命令運行程式到退出迴圈體。簡寫u
2. 調試已啟動並執行程式
兩種方法:
1、 在UNIX下用ps查看正在啟動並執行程式的PID(進程ID),然後用gdb <program> PID格式掛接正在啟動並執行程式。
2、 先用gdb <program>關聯上原始碼,並進行gdb,在gdb中用attach命令來掛接進程的PID。並用detach來取消掛接的進程。
3. 暫停 / 恢複程式運行
GDB可以方便地設定程式在哪行停住,在什麼條件下停住,在收到什麼訊號時停往等等。以便於查看運行時的變數,以及運行時的流程。
當進程被gdb停住時使用info program 來查看程式的是否在運行,進程號,被暫停原因。
GDB有以下幾種暫停方式:
斷點(BreakPoint)、觀察點(WatchPoint)、捕捉點(CatchPoint)、訊號(Signals)、線程停止(Thread Stops)。要恢複程式運行,使用continue(簡寫c)命令。詳細介紹如下:
1). 設定斷點(BreakPoint)
用break命令來設定斷點。設定斷點的方法如下:
break <function> 在進入指定函數時停住。C++中可以使用class::function或function(type,type)格式來指定函數名。
break <linenum> 在指定行號停住。
break +offset
break -offset 在當前行號的前面或後面的offset行停住。offiset為自然數。
break filename:linenum 在源檔案filename的linenum行處停住。
break filename:function 在源檔案filename的function函數的入口處停住。
break *address 在程式啟動並執行記憶體位址處停住。
break break命令沒有參數時,表示在下一條指令處停住。
break ... if <condition>
...可以是上述的參數,condition表示條件,在條件成立時停住。比如在迴圈體中,可以設定break if i=100,表示當i為100時停住程式。
查看斷點使用info命令,如下所示:(註:n表示斷點號)
info breakpoints [n]
info break [n]
2). 設定觀察點(WatchPoint)
觀察點一般用來觀察某個運算式(變數也是一種運算式)的值是否發生了變化,如果有變化,馬上停住程式。設定觀察點方法如下:
watch <expr> 為運算式(變數)expr設定一個觀察點。當運算式值有變化時,馬上停住程式。
rwatch <expr> 運算式(變數)expr被讀時,停住程式。
awatch <expr> 運算式(變數)的值被讀或被寫時,停住程式。
info watchpoints 列出當前所設定了的所有觀察點。
3). 設定捕捉點(CatchPoint)
設定捕捉點來捕捉程式運行時的一些事件。如:載入共用庫(動態連結程式庫)或是C++的異常。設定捕捉點的格式如下:
catch <event>
當event發生時,停住程式。event可以是下面的內容:
1、throw 一個C++拋出的異常。(throw為關鍵字)
2、catch 一個C++捕捉到的異常。(catch為關鍵字)
3、exec 調用系統調用exec時。(exec為關鍵字,目前此功能只在HP-UX下有用)
4、fork 調用系統調用fork時。(fork為關鍵字,目前此功能只在HP-UX下有用)
5、vfork 調用系統調用vfork時。(vfork為關鍵字,目前此功能只在HP-UX下有用)
6、load 或 load <libname> 載入共用庫(動態連結程式庫)時。(load為關鍵字,目前此功能只在HP-UX下有用)
7、unload 或 unload <libname> 卸載共用庫(動態連結程式庫)時。(unload為關鍵字,目前此功能只在HP-UX下有用)
tcatch <event>
只設定一次捕捉點,當程式停住以後,應點被自動刪除。
4). 維護停止點
GDB中的停止點只有上述的三類。當已定義好的停止點沒有用時可以使用
delete、clear、disable、enable這幾個命令來進行維護。詳細如下:
clear 清除所有的已定義的停止點。
clear <function>
clear <filename:function>
清除所有設定在函數上的停止點。
clear <linenum>
clear <filename:linenum>
清除所有設定在指定行上的停止點。
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自動刪除。
5). 停止條件維護
前
面在說到設定斷點時,提到過可以設定一個條件,當條件成立時,程式自動停止,這是一個非常強大的功能,這裡專門說說這個條件的相關維護命令。一般來說,為
斷點設定一個條件,我們使用if關鍵詞,後面跟其斷點條件。並且,條件設定好後,我們可以用condition命令來修改斷點的條件。(只有break和
watch命令支援if,catch目前暫不支援if)
condition <bnum> <expression>
修改斷點號為bnum的停止條件為expression。
condition <bnum>
清除斷點號為bnum的停止條件。
ignore <bnum> <count>
指定程式運行時,忽略斷點號為bnum的停止條件count次。
6). 為停止點設定運行命令
GDB提供command命令來設定停止點的運行命令。也就是說,當啟動並執行程式被停止住時,可以讓其自動運行一些別的命令,這很有利行自動化調試。對基於GDB的自動化調試是一個強大的支援。詳細如下:
commands [bnum]
... command-list ...
end
為斷點號bnum指寫一個命令列表。當程式被該斷點停住時,gdb會依次運行命令列表中的命令。
例如:
break foo if x>0
commands
printf "x is %d\n",x
continue
end
斷點設定在函數foo中,斷點條件是x>0,如果程式被斷住後,也就是,一旦x的值在foo函數中大於0,GDB會自動列印出x的值,並繼續運行程式。
如果你要清除斷點上的命令序列,那麼只要簡單的執行一下commands命令,並直接在打個end就行了。
7). 斷點菜單
在
C++中,可能會重複出現同一個名字的函數若干次(函數重載),在這種情況下,break
<function>不能告訴GDB要停在哪個函數的入口。這時可以使用break
<function(type)>也就是把函數的參數類型告訴GDB,以指定一個函數。否則的話,GDB會給你列出一個斷點菜單供你選擇你所
需要的斷點。你只要輸入你菜單列表中的編號就可以了。如:
(gdb) b String::after
[0] cancel
[1] all
[2] file:String.cc; line number:867
[3] file:String.cc; line number:860
[4] file:String.cc; line number:875
[5] file:String.cc; line number:853
[6] file:String.cc; line number:846
[7] file:String.cc; line number:735
> 2 4 6
Breakpoint 1 at 0xb26c: file String.cc, line 867.
Breakpoint 2 at 0xb344: file String.cc, line 875.
Breakpoint 3 at 0xafcc: file String.cc, line 846.
Multiple breakpoints were set.
Use the "delete" command to delete unwanted
breakpoints.
(gdb)
可見,GDB列出了所有after的重載函數,你可以選一下列表編號就行了。0表示放棄設定斷點,1表示所有函數都設定斷點。
8). 恢複程式運行和單步調試
程式被停住後用continue命令恢複程式的運行直到程式結束,或下一個斷點到來。也可以使用step或next命令單步跟蹤程式。
continue [ignore-count]
c [ignore-count]
fg [ignore-count]
恢複程式運行,直到程式結束,或是下一個斷點到來。ignore-count表示忽略其後的斷點次數。continue,c,fg三個命令都是一樣的意思。
step <count>
單步跟蹤,有函數調用時進入該函數。進入函數的前提是,此函數被編譯有debug資訊。後面可以加count也可以不加,不加表示一條條地執行,加表示執行後面的count條指令,然後再停住。
next <count>
單步跟蹤,有函數調用不會進入該函數。後面可以加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)
訊號是一種非強制中斷,是一種處理非同步事件的方法。一般來說,作業系統都支援許多訊號。比較重要應用程式一般都會處理訊號。UNIX定義了許多訊號,比如
SIGINT表示中斷字元訊號,也就是Ctrl+C的訊號,SIGBUS表示硬體故障的訊號;SIGCHLD表示子進程狀態改變訊號;SIGKILL表示
終止程式啟動並執行訊號,等等。訊號量編程是UNIX下非常重要的一種技術。GDB調試可以處理任何一種訊號,可以讓GDB收到所指定的訊號時,馬上停住正在
啟動並執行程式。詳細如下:
handle <signal> <keywords...>
在GDB
中定義一個訊號處理。訊號<signal>可以以SIG開頭或不以SIG開頭。可以定義一個需要處理訊號的範圍(如:SIGIO-
SIGKILL,表示處理從SIGIO訊號到SIGKILL的訊號,其中包括SIGIO,SIGIOT,SIGKILL三個訊號),也可以使用關鍵字
all來標明要處理所有的訊號。一旦被調試的程式接收到訊號,GDB會馬上停止運行程式,以供調試。其<keywords>可以是以下幾種關
鍵字的一個或多個:
nostop 當被調試的程式收到訊號時,GDB不會停住程式的運行,但會打出訊息告知收到這種訊號。
stop 當被調試的程式收到訊號時,GDB停住程式。
print 當被調試的程式收到訊號時,GDB顯示出一條資訊。
noprint 當被調試的程式收到訊號時,GDB不顯示收到訊號的資訊。
pass
noignore
當被調試的程式收到訊號時,GDB不處理訊號但會把這個訊號交給被偵錯工具處理。
nopass
ignore
被調試的程式收到訊號時,GDB不會讓被偵錯工具處理這個訊號。
info signals
info handle
查看有哪些訊號在被GDB檢測中。
10). 線程(Thread Stops)
對於多線程程式,GDB可以定義斷點是否在所有的線程上,或是在某個特定的線程。詳細如下:
break <linespec> thread <threadno>
break <linespec> thread <threadno> if ...
linespec 指定斷點所在來源程式的行號。threadno指定了線程的ID。這個ID是GDB分配的,通過“info
threads”命令來查看正在運行程式中的線程資訊。如果不指定thread
<threadno>則表示斷點設在所有線程上面。還可以為某線程指定斷點條件。如:
(gdb) break frik.c:13 thread 28 if bartab > lim
當程式被GDB停住時,所有的運行線程都會被停住。這方便查看運行程式的總體情況。恢複程式運行時,所有的線程也會被恢複運行。那怕是主進程在被單步調試時。
11). 查看棧資訊
程式被停住後需要查看程式是哪裡停住的。程式調用了一個函數時函數的地址、參數和函數內的局部變數都會被壓入“棧”(Stack)中。用GDB命令查看當前的棧中資訊的命令如下:
backtrace 簡潔bt
列印當前的函數調用棧的所有資訊。如:
(gdb) bt
#0 func (n=250) at tst.c:6
#1 0x08048524 in main (argc=1, argv=0xbffff674) at tst.c:30
#2 0x400409ed in __libc_start_main () from /lib/libc.so.6
從上可以看出函數的調用棧資訊:
__libc_start_main --> main() --> func()
backtrace <n>
bt <n>
n是一個正整數,表示只列印棧頂上n層的棧資訊。
backtrace <-n>
bt <-n>
-n表一個負整數,表示只列印棧底下n層的棧資訊。
要查看某一層的資訊需要切換當前的棧,一般來說,程式停止時,最頂層的棧就是當前棧。如果要查看棧下面層的詳細資料,首先要做的是切換當前棧。
frame <n>
f <n>
n是一個從0開始的整數,是棧中的層編號。
比如:frame 0,表示棧頂,frame 1,表示棧的第二層。
up <n>
向棧的上面移動n層,可以不打n,表示向上移動一層。
down <n>
向棧的下面移動n層,可以不打n,表示向下移動一層。
上面的命令都會列印出移動到的棧層的資訊。如果你不想讓其打出資訊則用這三個命令:
select-frame <n> 對應於 frame 命令。
up-silently <n> 對應於 up 命令。
down-silently <n> 對應於 down 命令。
查看當前棧層的資訊,你可以用以下GDB命令:
frame 或 f 列印出棧的層編號,當前的函數名,函數參數值,函數所在檔案及行號,函數執行到的語句。
info frame
info f
列印出更為詳細的當前棧層的資訊,只不過,大多數都是運行時的內地址。比如:函數地址,調用函數的地址,被調用函數的地址,目前的函數是由什麼樣的程式語言寫成的、函數參數地址及值、局部變數的地址等等。如:
(gdb) info f
Stack level 0, frame at 0xbffff5d4:
eip = 0x804845d in func (tst.c:6); saved eip 0x8048524
called by frame at 0xbffff60c
source language c.
Arglist at 0xbffff5d4, args: n=250
Locals at 0xbffff5d4, Previous frame's sp is 0x0
Saved registers:
ebp at 0xbffff5d4, eip at 0xbffff5d8
info args 列印出當前函數的參數名及其值。
info locals 列印出當前函數中所有局部變數及其值。
info catch 列印出當前的函數中的異常處理資訊。
很不錯的文章, 太多了, 用到那學到那吧, 多練習! 來源: http://blog.21ic.com/user1/2177/archives/2006/28497.html
==================
添加一點:
(gdb)up
查看當前運行函數的上一個函數是?
---
在gdb中執行shell中的命令:
(gdb)shell ls
(gdb)shell file aa.tar