GDB詳解

Augusdi發表於2015-04-13


1 簡介

2 生成除錯資訊

3 啟動GDB 的方法

4 程式執行上下文

    4.1 程式執行引數

    4.2 工作目錄

    4.3 程式的輸入輸出

5 設定斷點

    5.1 簡單斷點

    5.2 多檔案設定斷點

    5.3 查詢所有斷點

6 觀察點

7 條件斷點

8 維護停止點

9 為停止點設定執行命令

10 除錯程式碼

11 檢視執行時資料

12 程式變數

13 自動顯示

14 歷史記錄

15 改變程式的執行

    15.1 修改變數值

    15.2 跳轉執行

    15.3 產生訊號量

    15.4 強制函式返回

    15.5 強制呼叫函式

16 顯示原始碼

17 除錯已執行的程式

18 執行緒

19 檢視棧資訊

20 訊號

21 catch

22 指定原始檔的路徑

1 簡介

GDB(GNU Debugger)是GCC的除錯工具。其功能強大,現描述如下:
GDB主要幫忙你完成下面四個方面的功能:
1.啟動你的程式,可以按照你的自定義的要求隨心所欲的執行程式。
2.可讓被除錯的程式在你所指定的調置的斷點處停住。(斷點可以是條件表示式)
3.當程式被停住時,可以檢查此時你的程式中所發生的事。
4.動態的改變你程式的執行環境。

2 生成除錯資訊

一般來說GDB主要除錯的是C/C++的程式。要除錯C/C++的程式,首先在編譯時,我們必須要把除錯資訊加到可執行檔案中。使用編譯器(cc/gcc/g++)的 -g 引數可以做到這一點。如:

gcc -g hello.c -o hello

g++ -g hello.cpp -o hello

如果沒有-g,你將看不見程式的函式名、變數名,所代替的全是執行時的記憶體地址。當你用-g把除錯資訊加入之後,併成功編譯目的碼以後,讓我們來看看如何用gdb來除錯他。

3 啟動GDB 的方法

1、gdb program

program 也就是你的執行檔案,一般在當前目錄下。

2、gdb program core

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

3、gdb program 1234

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

4 程式執行上下文

4.1 程式執行引數

set args 可指定執行時引數。(如:set args 10 20 30 40 50 )

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

run (r) 啟動程式

不指定執行引數 r

指定執行引數r 10 20 30 40 50

4.2 工作目錄

cd 相當於shell的cd命令。

pwd 顯示當前的所在目錄。

4.3 程式的輸入輸出

info terminal 顯示你程式用到的終端的模式。

使用重定向控制程式輸出。如:run > outfile

tty命令可以設定輸入輸出使用的終端裝置。如:tty /dev/tty1

5 設定斷點

5.1 簡單斷點

break 設定斷點,可以簡寫為b

b 10 設定斷點,在源程式第10行

b func 設定斷點,在func函式入口處

5.2 多檔案設定斷點

在進入指定函式時停住:

C++中可以使用class::function或function(type,type)格式來指定函式名。如果有名稱空間,可以使用namespace::class::function或者function(type,type)格式來指定函式名。

break filename:linenum
在原始檔filename的linenum行處停住
break filename:function
在原始檔filename的function函式的入口處停住

break class::function或function(type,type)

在類class的function函式的入口處停住

break namespace::class::function

在名稱空間為namespace的類class的function函式的入口處停住

5.3 查詢所有斷點

info b

6 觀察點

watch 為表示式(變數)expr設定一個觀察點。當表示式值有變化時,馬上停住程式。

rwatch 表示式(變數)expr被讀時,停住程式。

awatch 表示式(變數)的值被讀或被寫時,停住程式。

info watchpoints 列出當前所設定了的所有觀察點。

7 條件斷點

一般來說,為斷點設定一個條件,我們使用if關鍵詞,後面跟其斷點條件。並且,條件設定好後,我們可以用condition命令來修改斷點的條件。並且,條件設定好後,我們可以用condition命令來修改斷點的條件。(只有break 和 watch命令支援if,catch目前暫不支援if)。

設定一個條件斷點

b test.c:8 if intValue == 5

condition 與break if類似,只是condition只能用在已存在的斷點上

修改斷點號為bnum的停止條件為expression

condition bnum expression

清楚斷點號為bnum的停止條件

condition bnum

ignore 忽略停止條件幾次

表示忽略斷點號為bnum的停止條件count次

Ignore bnum count

8 維護停止點

clear 清除所有的已定義的停止點。

clear 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自動刪除。

9 為停止點設定執行命令

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

commands [bnum]

… command-list …

end

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

例如:

break foo if x>0

commands

printf “x is %d “,x

continue

end

斷點設定在函式foo中,斷點條件是x>0,如果程式被斷住後,也就是,一旦x的值在foo函式中大於0,GDB會自動列印出x的值,並繼續執行程式。

如果你要清除斷點上的命令序列,那麼只要簡單的執行一下commands命令,並直接在打個end就行了。

10 除錯程式碼

run 執行程式,可簡寫為r

next 單步跟蹤,函式呼叫當作一條簡單語句執行,可簡寫為n

step 單步跟蹤,函式調進入被呼叫函式體內,可簡寫為s

finish 退出函式

until 在一個迴圈體內單步跟蹤時,這個命令可以執行程式直到退出迴圈體,可簡寫為u。

continue 繼續執行程式,可簡寫為c

stepi或si, nexti或ni 單步跟蹤一條機器指令,一條程式程式碼有可能由數條機器指令完成,stepi和nexti可以單步執行機器指令。

info program 來檢視程式的是否在執行,程式號,被暫停的原因。

11 檢視執行時資料

print 列印變數、字串、表示式等的值,可簡寫為p
p count 列印count的值
p cou1+cou2+cou3 列印表示式值

print接受一個表示式,GDB會根據當前的程式執行的資料來計算這個表示式,表示式可以是當前程式執行中的const常量、變數、函式等內容。但是GDB不能使用程式中定義的巨集。

12 程式變數

在GDB中,你可以隨時檢視以下三種變數的值:

1、全域性變數(所有檔案可見的)

2、靜態全域性變數(當前檔案可見的)

3、區域性變數(當前Scope可見的)

如果你的區域性變數和全域性變數發生衝突(也就是重名),一般情況下是區域性變數會隱藏全域性變數,也就是說,如果一個全域性變數和一個函式中的區域性變數同名時,如果當前停止點在函式中,用print顯示出的變數的值會是函式中的區域性變數的值。如果此時你想檢視全域性變數的值時,你可以使用“::”操作符:

file::variable

function::variable

可以通過這種形式指定你所想檢視的變數,是哪個檔案中的或是哪個函式中的。例如,檢視檔案f2.c中的全域性變數x的值:

p ‘f2.c’::x

當然,“::”操作符會和C++中的發生衝突,GDB能自動識別“::”是否C++的操作符,所以你不必擔心在除錯C++程式時會出現異常。

4陣列變數

有時候,你需要檢視一段連續的記憶體空間的值。比如陣列的一段,或是動態分配的資料的大小。你可以使用GDB的“@”操作符,“@”的左邊是第一個記憶體的地址的值,“@”的右邊則你你想檢視記憶體的長度。例如,你的程式中有這樣的語句:

int *array = (int *) malloc (len * sizeof (int));

於是,在GDB除錯過程中,你可以以如下命令顯示出這個動態陣列的取值:

p *array@len

@的左邊是陣列的首地址的值,也就是變數array所指向的內容,右邊則是資料的長度,其儲存在變數len中。

13 自動顯示

你可以設定一些自動顯示的變數,當程式停住時,或是在你單步跟蹤時,這些變數會自動顯示。相關的GDB命令是display。

display expr

display/fmt expr

display/fmt addr

expr是一個表示式,fmt表示顯示的格式,addr表示記憶體地址,當你用display設定好了一個或多個表示式後,只要你的程式被停下來,GDB會自動顯示你所設定的這些表示式的值。

info display

檢視display設定的自動顯示的資訊。

undisplay dnums…

delete display dnums…

刪除自動顯示,dnums意為所設定好了的自動顯式的編號。如果要同時刪除幾個,編號可以用空格分隔,如果要刪除一個範圍內的編號,可以用減號表示(如:2-5)

disable display dnums…

enable display dnums…

disable和enalbe不刪除自動顯示的設定,而只是讓其失效和恢復。

14 歷史記錄

當你用GDB的print檢視程式執行時的資料時,你每一個print都會被GDB記錄下來。GDB會以$1, $2, $3 …..這樣的方式為你每一個print命令編上號。於是,你可以使用這個編號訪問以前的表示式,如$1。這個功能所帶來的好處是,如果你先前輸入了一個比較長的表示式,如果你還想檢視這個表示式的值,你可以使用歷史記錄來訪問,省去了重複輸入。

show values

Print the last ten values in the value history, with their item numbers. This is

like ‘p $$9’ repeated ten times, except that show values does not change the

history.

show values n

Print ten history values centered on history item number n.

show values +

Print ten history values just after the values last printed. If no more values are

available, show values + produces no display.

15 改變程式的執行

一旦使用GDB掛上被除錯程式,當程式執行起來後,你可以根據自己的除錯思路來動態地在GDB中更改當前被除錯程式的執行線路或是其變數的值,這個強大的功能能夠讓你更好的除錯你的程式,比如,你可以在程式的一次執行中走遍程式的所有分支。

15.1 修改變數值

修改被除錯程式執行時的變數值,在GDB中很容易實現,使用GDB的print命令即可完成。如:

(gdb) print x=4

x=4這個表示式是C/C++的語法,意為把變數x的值修改為4,如果你當前除錯的語言是Pascal,那麼你可以使用Pascal的語法:x:=4。

在某些時候,很有可能你的變數和GDB中的引數衝突,如:

(gdb) whatis width

type = double

(gdb) p width

$4 = 13

(gdb) set width=47

Invalid syntax in expression.

因為,set width是GDB的命令,所以,出現了“Invalid syntax in expression”的設定錯誤,此時,你可以使用set var命令來告訴GDB,width不是你GDB的引數,而是程式的變數名,如:

(gdb) set var width=47

另外,還可能有些情況,GDB並不報告這種錯誤,所以保險起見,在你改變程式變數取值時,最好都使用set var格式的GDB命令。

15.2 跳轉執行

一般來說,被除錯程式會按照程式程式碼的執行順序依次執行。GDB提供了亂序執行的功能,也就是說,GDB可以修改程式的執行順序,可以讓程式執行隨意跳躍。這個功能可以由GDB的jump命令來完:

jump linespec

指定下一條語句的執行點。可以是檔案的行號,可以是file:line格式,可以是+num這種偏移量格式。表示下一條執行語句從哪裡開始。

jump *address

這裡的是程式碼行的記憶體地址。

注意,jump命令不會改變當前的程式棧中的內容,所以,當你從一個函式跳到另一個函式時,當函式執行完返回時進行彈棧操作時必然會發生錯誤,可能結果還是非常奇怪的,甚至於產生程式Core Dump。所以最好是同一個函式中進行跳轉。

熟悉彙編的人都知道,程式執行時,eip暫存器用於儲存當前程式碼所在的記憶體地址。所以,jump命令也就是改變了這個暫存器中的值。於是,你可以使用“set $pc”來更改跳轉執行的地址。如:

set $pc = 0×485

15.3 產生訊號量

使用singal命令,可以產生一個訊號量給被除錯的程式。如:中斷訊號Ctrl+C。這非常方便於程式的除錯,可以在程式執行的任意位置設定斷點,並在該斷點用GDB產生一個訊號量,這種精確地在某處產生訊號非常有利程式的除錯。

語法是:

signal signal

UNIX的系統訊號量通常從1到15。所以取值也在這個範圍。

single命令和shell的kill命令不同,系統的kill命令發訊號給被除錯程式時,是由GDB截獲的,而single命令所發出一訊號則是直接發給被除錯程式的。

15.4 強制函式返回

如果你的除錯斷點在某個函式中,並還有語句沒有執行完。你可以使用return命令強制函式忽略還沒有執行的語句並返回。

return

return expression

使用return命令取消當前函式的執行,並立即返回,如果指定了,那麼該表示式的值會被認作函式的返回值。

15.5 強制呼叫函式

call expr

表示式中可以一是函式,以此達到強制呼叫函式的目的。並顯示函式的返回值,如果函式返回值是void,那麼就不顯示。

print expr

另一個相似的命令也可以完成這一功能——print,print後面可以跟表示式,所以也可以用他來呼叫函式,print和call的不同是,如果函式返回void,call則不顯示,print則顯示函式返回值,並把該值存入歷史資料中。

16 顯示原始碼

GDB 可以列印出所除錯程式的原始碼,當然,在程式編譯時一定要加上 –g 的引數,把源程式資訊編譯到執行檔案中。不然就看不到源程式了。當程式停下來以後, GDB會報告程式停在了那個檔案的第幾行上。你可以用list命令來列印程式的原始碼。預設列印10行,還是來看一看檢視原始碼的GDB命令吧。

list linenum

Print lines centered around line number linenum in the current source file.

list function

顯示函式名為function的函式的源程式。

list

顯示當前行後面的源程式。

list -

顯示當前行前面的源程式。

一般是列印當前行的上5行和下5行,如果顯示函式是是上2行下8行,預設是10行,當然,你也可以定製顯示的範圍,使用下面命令可以設定一次顯示源程式的行數。

set listsize count

設定一次顯示原始碼的行數。(unless the list argument explicitly specifies some other number)

show listsize

檢視當前listsize的設定。

17 除錯已執行的程式

兩種方法:

1、在UNIX下用ps檢視正在執行的程式的PID(程式ID),然後用gdb PID process-id 格式掛接正在執行的程式。

2、先用gdb 關聯上原始碼,並進行gdb,在gdb中用attach process-id 命令來掛接程式的PID。並用detach來取消掛接的程式。

18 執行緒

如果你程式是多執行緒的話,你可以定義你的斷點是否在所有的執行緒上,或是在某個特定的執行緒。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停住時,所有的執行執行緒都會被停住。這方便你你檢視執行程式的總體情況。而在你恢復程式執行時,所有的執行緒也會被恢復執行。那怕是主程式在被單步除錯時。

19 檢視棧資訊

當程式被停住了,你需要做的第一件事就是檢視程式是在哪裡停住的。當你的程式呼叫了一個函式,函式的地址,函式引數,函式內的區域性變數都會被壓入“棧”(Stack)中。你可以用GDB命令來檢視當前的棧中的資訊。

下面是一些檢視函式呼叫棧資訊的GDB命令:

breacktrace,簡稱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

n是一個從0開始的整數,是棧中的層編號。比如:frame 0,表示棧頂,frame 1,表示棧的第二層。

frame addr

f addr Select the frame at address addr. This is useful mainly if the chaining of stack frames has been damaged by a bug, making it impossible for gdb to assign

numbers properly to all frames. In addition, this can be useful when your program has multiple stacks and switches between them.

up n

表示向棧的上面移動n層,可以不打n,表示向上移動一層。

down n

表示向棧的下面移動n層,可以不打n,表示向下移動一層。

上面的命令,都會列印出移動到的棧層的資訊。如果你不想讓其打出資訊。你可以使用這三個命令:

select-frame 對應於 frame 命令。

up-silently n 對應於 up 命令。

down-silently n 對應於 down 命令。

檢視當前棧層的資訊,你可以用以下GDB命令:

frame 或 f

會列印出這些資訊:棧的層編號,當前的函式名,函式引數值,函式所在檔案及行號,函式執行到的語句。

info frame

info f

20 訊號

訊號是一種軟中斷,是一種處理非同步事件的方法。一般來說,作業系統都支援許多訊號。尤其是UNIX,比較重要應用程式一般都會處理訊號。UNIX定義了許多訊號,比如SIGINT表示中斷字元訊號,也就是Ctrl+C的訊號,SIGBUS表示硬體故障的訊號;SIGCHLD表示子程式狀態改變訊號; SIGKILL表示終止程式執行的訊號,等等。

除錯程式的時候處理訊號:

handle signal [keywords...]

signal可以以SIG開頭或不以SIG開頭,可以用定義一個要處理訊號的範圍(如:SIGIO-SIGKILL,表示處理從 SIGIO訊號到SIGKILL的訊號,其中包括SIGIO,SIGIOT,SIGKILL三個訊號),也可以使用關鍵字all來標明要處理所有的訊號。一旦被除錯的程式接收到訊號,執行程式馬上會被GDB停住,以供除錯。

keywords列表如下:

nostop

當被除錯的程式收到訊號時,GDB不會停住程式的執行,但會打出訊息告訴你收到這種訊號。

stop

當被除錯的程式收到訊號時,GDB會停住你的程式。This implies the print keyword as well.

print

當被除錯的程式收到訊號時,GDB會顯示出一條資訊。

noprint

當被除錯的程式收到訊號時,GDB不會告訴你收到訊號的資訊。This implies the nostop keyword as well.

pass

noignore

當被除錯的程式收到訊號時,GDB不處理訊號。這表示,GDB會把這個訊號交給被除錯程式處理 or else it may terminate if the signal is fatal and not handled.

nopass

ignore

當被除錯的程式收到訊號時,GDB不會讓被除錯程式來處理這個訊號。

info signals

info handle

檢視有哪些訊號在被GDB檢測中。

21catch

當event發生時,停住程式。event可以是下面的內容:
1、throw 一個C++丟擲的異常。(throw為關鍵字)
2、catch 一個C++捕捉到的異常。(catch為關鍵字)

22 指定原始檔的路徑

某些時候,用-g編譯過後的執行程式中只是包括了原始檔的名字,沒有路徑名。GDB提供了可以讓你指定原始檔的路徑的命令,以便GDB進行搜尋。

Directory dirname …

dir dirname …

加一個原始檔路徑到當前路徑的前面。如果你要指定多個路徑,UNIX下你可以使用“:”,Windows下你可以使用“;”。

directory

清除所有的自定義的原始檔搜尋路徑資訊。

show directories

顯示定義了的原始檔搜尋路徑。


http://www.cnblogs.com/ggjucheng/archive/2011/12/14/2288004.html