GDB除錯命令詳解

大囚長發表於2019-01-03

基礎用法

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
  • 1
  • 2

如果沒有-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
在原始檔filenamelinenum行處停住
break filename:function
在原始檔filenamefunction函式的入口處停住
break class::functionfunction(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
conditionbreak if類似,只是condition只能用在已存在的斷點上
修改斷點號為bnum的停止條件為expression
condition bnum expression
清楚斷點號為bnum的停止條件
condition bnum
ignore 忽略停止條件幾次
表示忽略斷點號為bnum的停止條件count次
Ignore bnum count

8 維護停止點

  1. clear清除所有的已定義的停止點。
  2. clear function清除所有設定在函式上的停止點。
  3. clear linenum清除所有設定在指定行上的停止點。
  4. clear filename:linenum清除所有設定在指定檔案:指定行上的停止點。
  5. delete [breakpoints] [range...]刪除指定的斷點,breakpoints為斷點號。如果不指定斷點號,則表示刪除所有的斷點。range表示斷點號的範圍(如:3-7)。其簡寫命令為d。
  6. 比刪除更好的一種方法是disable停止點,disable了的停止點,GDB不會刪除,當你還需要時,enable即可,就好像回收站一樣。
  7. disable [breakpoints] [range...] disable所指定的停止點,breakpoints為停止點號。如果什麼都不指定,表示disable所有的停止點。簡寫命令是dis.
  8. enable [breakpoints] [range...]enable所指定的停止點,breakpoints為停止點號。
  9. enable [breakpoints] once range…enable所指定的停止點一次,當程式停止後,該停止點馬上被GDB自動disable。
  10. enable [breakpoints] delete range…enable所指定的停止點一次,當程式停止後,該停止點馬上被GDB自動刪除。

9 為停止點設定執行命令

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

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

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

break foo if x>0
commands
printfx is %d “,x
continue
end
  • 1
  • 2
  • 3
  • 4
  • 5

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

10 除錯程式碼

  1. run 執行程式,可簡寫為r
  2. next 單步跟蹤,函式呼叫當作一條簡單語句執行,可簡寫為n
  3. step 單步跟蹤,函式調進入被呼叫函式體內,可簡寫為s
  4. finish 退出函式
  5. until 在一個迴圈體內單步跟蹤時,這個命令可以執行程式直到退出迴圈體,可簡寫為u。
  6. continue 繼續執行程式,可簡寫為c
  7. stepisi, nextini 單步跟蹤一條機器指令,一條程式程式碼有可能由數條機器指令完成,stepi和nexti可以單步執行機器指令。
  8. info program 來檢視程式的是否在執行,程式號,被暫停的原因。

11 檢視執行時資料

  1. print 列印變數、字串、表示式等的值,可簡寫為p
  2. p count 列印count的值
  3. p cou1+cou2+cou3 列印表示式值
  4. print接受一個表示式,GDB會根據當前的程式執行的資料來計算這個表示式,表示式可以是當前程式執行中的const常量、變數、函式等內容。但是GDB不能使用程式中定義的巨集。

12 程式變數

在GDB中,你可以隨時檢視以下三種變數的值:
1. 全域性變數(所有檔案可見的)
2. 靜態全域性變數(當前檔案可見的)
3. 區域性變數(當前Scope可見的)
如果你的區域性變數和全域性變數發生衝突(也就是重名),一般情況下是區域性變數會隱藏全域性變數,也就是說,如果一個全域性變數和一個函式中的區域性變數同名時,如果當前停止點在函式中,用print顯示出的變數的值會是函式中的區域性變數的值。如果此時你想檢視全域性變數的值時,你可以使用“::”操作符:
file::variable
function::variable
可以通過這種形式指定你所想檢視的變數,是哪個檔案中的或是哪個函式中的。例如,檢視檔案f2.c中的全域性變數x的值:

p ‘f2.c’::x
  • 1

當然,“::”操作符會和C++中的發生衝突,GDB能自動識別“::”是否C++的操作符,所以你不必擔心在除錯C++程式時會出現異常。
4. 陣列變數
有時候,你需要檢視一段連續的記憶體空間的值。比如陣列的一段,或是動態分配的資料的大小。你可以使用GDB的“@”操作符,“@”的左邊是第一個記憶體的地址的值,“@”的右邊則你你想檢視記憶體的長度。例如,你的程式中有這樣的語句:

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

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

p *array@len
  • 1

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

13 自動顯示

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

display expr
display/fmt expr
display/fmt addr
  • 1
  • 2
  • 3

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

info display
  • 1

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

undisplay dnums…
delete display dnums…
  • 1
  • 2

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

disable display dnums…
enable display dnums…
  • 1
  • 2

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

14 歷史記錄

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

(gdb)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.

(gdb)show values n
Print ten history values centered on history item number n.

(gdb)show values +
Print ten history values just after the values last printed. If no more values are
available, show values + produces no display.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

15 改變程式的執行

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

15.1 修改變數值

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

(gdb) print x=4
  • 1

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.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

(gdb) set var width=47
  • 1

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

15.2 跳轉執行

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

jump linespec
  • 1

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

jump *address
  • 1

這裡的是程式碼行的記憶體地址。
注意,jump命令不會改變當前的程式棧中的內容,所以,當你從一個函式跳到另一個函式時,當函式執行完返回時進行彈棧操作時必然會發生錯誤,可能結果還是非常奇怪的,甚至於產生程式Core Dump。所以最好是同一個函式中進行跳轉。
熟悉彙編的人都知道,程式執行時,eip暫存器用於儲存當前程式碼所在的記憶體地址。所以,jump命令也就是改變了這個暫存器中的值。於是,你可以使用“set $pc”來更改跳轉執行的地址。如:

set $pc = 0×485
  • 1

15.3 產生訊號量

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

signal signal
  • 1

UNIX的系統訊號量通常從1到15。所以取值也在這個範圍。
single命令和shell的kill命令不同,系統的kill命令發訊號給被除錯程式時,是由GDB截獲的,而single命令所發出一訊號則是直接發給被除錯程式的。

15.4 強制函式返回

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

return
return expression
  • 1
  • 2

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

15.5 強制呼叫函式

call expr
  • 1

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

print expr
  • 1

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

16 顯示原始碼

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

(gdb)list linenum
Print lines centered around line number linenum in the current source file.
(gdb)list function
  • 1
  • 2
  • 3

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

list
  • 1

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

list -
  • 1

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

set listsize count
  • 1

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

show listsize
  • 1

檢視當前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
  • 1
  • 2

linespec指定了斷點設定在的源程式的行號。threadno指定了執行緒的ID,注意,這個ID是GDB分配的,你可以通過“info threads”命令來檢視正在執行程式中的執行緒資訊。如果你不指定‘thread threadno ’則表示你的斷點設在所有執行緒上面。你還可以為某執行緒指定斷點條件。如:

(gdb) break frik.c:13 thread 28 if bartab > lim
  • 1

當你的程式被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
  • 1
  • 2
  • 3
  • 4

從上可以看出函式的呼叫棧資訊:__libc_start_main –> main() –> func()

backtrace n
bt n
  • 1
  • 2

n是一個正整數,表示只列印棧頂上n層的棧資訊。

backtrace -n
bt -n
  • 1
  • 2

-n表一個負整數,表示只列印棧底下n層的棧資訊。

如果你要檢視某一層的資訊,你需要在切換當前的棧,一般來說,程式停止時,最頂層的棧就是當前棧,如果你要檢視棧下面層的詳細資訊,首先要做的是切換當前棧。

frame n
  • 1

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

(gdb)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.

(gdb)up n
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

down n
  • 1

表示向棧的下面移動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

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

gdb高階用法

一:列檔案清單

1.list(l)

  (gdb)   list   line1,line2

二:執行程式

要想執行準備除錯的程式,可使用run(r)命令,在它後面可以跟隨發給該程式的任何引數,包括標準輸入和標準輸出說明符(<和>)和外殼萬用字元(*、?、[、])在內。
如果你使用不帶引數的run命令,gdb就再次使用你給予前一條run命令的引數,這是很有用的。
利用set args命令就可以修改傳送給程式的引數,而使用show args 命令就可以檢視其預設引數的列表。

(gdb)set args –b –x
(gdb)show args
(gdb)backtrace(bt)  //命令為堆疊提供向後跟蹤功能。     
(gdb)Backtrace       //命令產生一張列表,包含著從最近的過程開始的所以有效過程和呼叫這些過程的引數。     

三:顯示資料

1. print(p) 命令可以檢查各個變數的值。

  (gdb)   print   p   //(p為變數名)     

2. whatis 命令可以顯示某個變數的型別

  (gdb)   whatis   p     
  type   =   int   *     

print 是gdb的一個功能很強的命令,利用它可以顯示被除錯的語言中任何有效的表示式。表示式除了包含你程式中的變數外,還可以包含以下內容:

2.1 對程式中函式的呼叫

(gdb)print   find_entry(1,0)     

2.2 資料結構和其他複雜物件

(gdb)print   *table_start
$8={e=reference=’\000’,location=0x0,next=0x0} 

2.3 值的歷史成分

(gdb)print   $1  //($1為歷史記錄變數,在以後可以直接引用   $1   的值)     

2.4 人為陣列

人為陣列提供了一種去顯示儲存器塊(陣列節或動態分配的儲存區)內容的方法。早期的除錯程式沒有很好的方法將任意的指標換成一個陣列。就像對待引數一樣,讓我們檢視記憶體中在變數h後面的10個整數,一個動態陣列的語法如下所示:

base@length

因此,要想顯示在h後面的10個元素,可以使用h@10:

(gdb)print   h@10     
$13=(-1,345,23,-234,0,0,0,98,345,10)     

四:斷點(breakpoint)

1. break命令(可以簡寫為b)

可以用來在除錯的程式中設定斷點,該命令有如下四種形式:

1. break   line-number   使程式恰好在執行給定行之前停止。
2. break   function-name   使程式恰好在進入指定的函式之前停止。
3. break   line-or-function   if   condition   如果condition(條件)是真,程式到達指定行或函式時停止。
4. break   routine-name   在指定例程的入口處設定斷點     

如果該程式是由很多原檔案構成的,你可以在各個原檔案中設定斷點,而不是在當前的原檔案中設定斷點,其方法如下:

(gdb)   break   filename:line-number
(gdb)   break   filename:function-name

要想設定一個條件斷點,可以利用break if命令,如下所示:

(gdb)   break   line-or-function   if   expr

例:

  (gdb)   break   46   if   testsize==100

從斷點繼續執行:countinue(c) 命令

五.斷點的管理

1.顯示當前gdb的斷點資訊:

(gdb)   info   break

他會以如下的形式顯示所有的斷點資訊:

  Num   Type   Disp   Enb   Address   What     
  1   breakpoint   keep   y   0x000028bc   in   init_random   at   qsort2.c:155     
  2   breakpoint   keep   y   0x0000291c   in   init_organ   at   qsort2.c:168     
  (gdb)

2.刪除指定的某個斷點:

(gdb)   delete   breakpoint   

該命令將會刪除編號為1的斷點,如果不帶編號引數,將刪除所有的斷點

(gdb)   delete   breakpoint

3.禁止使用某個斷點

(gdb)   disable   breakpoint   1
  • 1

該命令將禁止斷點 1,同時斷點資訊的 (Enb)域將變為 n

4.允許使用某個斷點

(gdb)   enable   breakpoint   1

該命令將允許斷點 1,同時斷點資訊的 (Enb)域將變為 y

5.清除原檔案中某一程式碼行上的所有斷點

(gdb)clean   number

注:number 為原檔案的某個程式碼行的行號
Clear清除所有的已定義的停止點。
clear <function>clear <filename:function>清除所有設定在函式上的停止點。
clear <linenum>clear <filename:linenum>清除所有設定在指定行上的停止點。

六.變數的檢查和賦值

  1. whatis:識別陣列或變數的型別
  2. ptype:比whatis的功能更強,他可以提供一個結構的定義
  3. set variable:將值賦予變數
  4. print 除了顯示一個變數的值外,還可以用來賦值

七.單步執行

  1. next(n) 不進入的單步執行
  2. step(s) 進入的單步執行
  3. finish 退出該函式返回到它的呼叫函式中

八.函式的呼叫

  1. call name 呼叫和執行一個函式
  (gdb)   call   gen_and_sork(   1234,1,0   )     
  (gdb)   call   printf(“abcd”)     
  $1=4     

九.機器語言工具

有一組專用的gdb變數可以用來檢查和修改計算機的通用暫存器,gdb提供了目前每一臺計算機中實際使用的4個暫存器的標準名字:
1. $pc: 程式計數器
2. $fp: 幀指標(當前堆疊幀)
3. $sp: 棧指標
4. $ps: 處理器狀態

十.訊號

gdb通常可以捕捉到傳送給它的大多數訊號,通過捕捉訊號,它就可決定對於正在執行的程式要做些什麼工作。例如,按CTRL-C將中斷訊號傳送給gdb,通常就會終止gdb。但是你或許不想中斷gdb,真正的目的是要中斷gdb正在執行的程式,因此,gdb要抓住該訊號並停止它正在執行的程式,這樣就可以執行某些除錯操作。

Handle命令可控制訊號的處理,他有兩個引數,一個是訊號名,另一個是接受到訊號時該作什麼。幾種可能的引數是:
1. nostop 接收到訊號時,不要將它傳送給程式,也不要停止程式。
2. stop 接受到訊號時停止程式的執行,從而允許程式除錯;顯示一條表示已接受到訊號的訊息(禁止使用訊息除外)
3. print 接受到訊號時顯示一條訊息
4. noprint 接受到訊號時不要顯示訊息(而且隱含著不停止程式執行)
5. pass 將訊號傳送給程式,從而允許你的程式去處理它、停止執行或採取別的動作。
6. nopass 停止程式執行,但不要將訊號傳送給程式。

例如,假定你截獲SIGPIPE訊號,以防止正在除錯的程式接受到該訊號,而且只要該訊號一到達,就要求該序停止,並通知你。要完成這一任務,可利用如下命令:

(gdb)   handle   SIGPIPE   stop   print
  • 1

請注意,UNIX的訊號名總是採用大寫字母!你可以用訊號編號替代訊號名
如果你的程式要執行任何訊號處理操作,就需要能夠測試其訊號處理程式,為此,就需要一種能將訊號傳送給程式的簡便方法,這就是signal命令的任務。該 命令的引數是一個數字或者一個名字,如SIGINT。假定你的程式已將一個專用的SIGINT(鍵盤輸入,或CTRL-C;訊號2)訊號處理程式設定成採 取某個清理動作,要想測試該訊號處理程式,你可以設定一個斷點並使用如下命令:

(gdb)   signal   2
 continuing   with   signal   SIGINT(2)
  • 1
  • 2

該程式繼續執行,但是立即傳輸該訊號,而且處理程式開始執行.

十一. 原檔案的搜尋

  1. search text:該命令可顯示在當前檔案中包含text串的下一行。
  2. Reverse-search text:該命令可以顯示包含text 的前一行。

十二.UNIX介面

shell 命令可啟動UNIX外殼,CTRL-D退出外殼,返回到 gdb.

十三.命令的歷史

為了允許使用歷史命令,可使用 set history expansion on 命令

(gdb)   set   history   expansion   on     
  • 1

小結:常用的gdb命令

  backtrace   //顯示程式中的當前位置和表示如何到達當前位置的棧跟蹤(同義詞:where)     
  breakpoint  //在程式中設定一個斷點     
  cd   //改變當前工作目錄     
  clear   //刪除剛才停止處的斷點     
  commands   //命中斷點時,列出將要執行的命令     (#add相當於vs的when hit)
  continue   //從斷點開始繼續執行     
  delete   //刪除一個斷點或監測點;也可與其他命令一起使用     
  display   //程式停止時顯示變數和表達時     
  down   //下移棧幀,使得另一個函式成為當前函式     
  frame   //選擇下一條continue命令的幀     
  info   //顯示與該程式有關的各種資訊     
  jump   //在源程式中的另一點開始執行     
  kill   //異常終止在gdb   控制下執行的程式     
  list   //列出相應於正在執行的程式的原檔案內容     
  next   //執行下一個源程式行,從而執行其整體中的一個函式     
  print   //顯示變數或表示式的值     
  pwd   //顯示當前工作目錄     
  ptype   //顯示一個資料結構(如一個結構或C++類)的內容     
  quit   //退出gdb     
  reverse-search   //在原始檔中反向搜尋正規表示式     
  run   //執行該程式     
  search   //在原始檔中搜尋正規表示式     
  set   variable   //給變數賦值     
  signal   //將一個訊號傳送到正在執行的程式     
  step   //執行下一個源程式行,必要時進入下一個函式     
  undisplay   //display命令的反命令,不要顯示錶達式     
  until   //結束當前迴圈     
  up   //上移棧幀,使另一函式成為當前函式     
  watch   //在程式中設定一個監測點(即資料斷點)     
  whatis   //顯示變數或函式型別    

gdb - GNU 偵錯程式

提要
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]]

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

    GDB主要可以做4大類事(加上一些其他的輔助工作),以幫助使用者在程式執行過程中發現bug。

      o  啟動您的程式,並列出可能會影響它執行的一些資訊
      o  使您的程式在特定條件下停止下來
      o  當程式停下來的時候,檢查發生了什麼
      o  對程式做出相應的調整,這樣您就能嘗試糾正一個錯誤並繼續發現其它錯誤

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

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

    您能以無引數無選項的形式執行GDB,不過通常的情況是以一到兩個引數執行GDB,以待除錯的可執行程式名為引數
    gdb 程式名

    您能用兩個引數來執行GDB,可執行程式名與core檔案(譯註:不知道怎麼翻譯好,就不翻譯了)。
    gdb 程式名 core

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

   下面是一些最常用的GDB命令:

   file [filename]
          裝入想要除錯的可執行檔案

   kill [filename]
          終止正在除錯的程式

   break [file:]function
          在(file檔案的)function函式中設定一個斷點

   clear
          刪除一個斷點,這個命令需要指定程式碼行或者函式名作為引數

   run [arglist]
          執行您的程式 (如果指定了arglist,則將arglist作為引數執行程式)

   bt Backtrace: 顯示程式堆疊資訊

   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檢視的時候,也能看到相同的文章

選項
任何引數而非選項指明瞭一個可執行檔案及core 檔案(或者程式ID);所
遇到的第一個未關聯選項標誌的引數與 ‘-se’ 選項等價,第二個,如果存
在,且是一個檔案的名字,則等價與 ‘-c’ 選項。許多選項都有一個長格式
與短格式;都會在這裡表示出來。如果你把一個長格式截短,只要不引起歧
義,那麼它還是可以被識別。(如果你願意,你可以使用 ‘+’ 而非 ‘-’ 標
記選項引數,不過我們在例子中仍然遵從通常的慣例)

    -help

   -h     列出所有選項,並附簡要說明。  

   -symbols=file

   -s file
          讀出檔案(file)中的符號表。

   -write 
          開通(enable)往可執行檔案和核心檔案寫的許可權。

   -exec=file

   -e file
          在適當時候把File作為可執行的檔案執行,來檢測與core dump結合的資料。
   -se File
          從File讀取符號表並把它作為可執行檔案。
   -core File
   -c File
          把File作為core dump來執行。
   -command=File
   -x File
          從File中執行GDB命令。
   -directory=Directory
   -d Directory
          把Dicrctory加入原始檔搜尋的路徑中。
   -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’字
符作為一個符號為框架來顯示原始碼.
-b Bps
設定行速(波特率或bits/s).在遠端除錯中GDB在任何序列介面中使用的行速.
-tty=Device
使用Device作為你程式執行的標準輸入輸出.

gdb主要除錯的是C/C++的程式。要除錯C/C++的程式,首先在編譯時,必須要把除錯資訊加到可執行檔案中。使用編譯器(cc/gcc/g++)的 -g 引數即可。如:

[david@DAVID david]$ gcc -g hello.c -o hello

[david@DAVID david]$ g++ -g hello.cpp -o hello

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

啟動gdb的方法有以下幾種:

  1. gdb
    program也就是執行檔案,一般在當前目錄下。

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

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

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

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

gdb啟動時,可以加上一些gdb的啟動開關,詳細的開關可以用gdb -help檢視。下面只列舉一些比較常用的引數:

-symbols

-s

從指定檔案中讀取符號表。

-se file

從指定檔案中讀取符號表資訊,並把它用在可執行檔案中。

-core

-c

除錯時core dump的core檔案。

-directory

-d

加入一個原始檔的搜尋路徑。預設搜尋路徑是環境變數中PATH所定義的路徑。

4.1.1 gdb的命令概貌
gdb的命令很多,gdb將之分成許多種類。help命令只是列出gdb的命令種類,如果要看其中的命令,可以使用help 命令。如:

(gdb) help data

也可以直接用help [command]來檢視命令的幫助。

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

示例一:在進入函式func時,設定一個斷點。可以輸入break func,或是直接輸入b func。

(gdb) b func

Breakpoint 1 at 0x804832e: file test.c, line 5.

(gdb)

示例二:輸入b按兩次TAB鍵,你會看到所有b開頭的命令。

(gdb) b

backtrace break bt

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

4.1.2 gdb中執行Linux的shell程式
在gdb環境中,可以執行Linux的shell命令:

shell

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

還有一個gdb命令是make:

make

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

4.1.3 在gdb中執行程式
當以gdb 方式啟動gdb後,gdb會在PATH路徑和當前目錄中搜尋的原始檔。如要確認gdb是否讀到原始檔,可使用l或list命令,看看gdb是否能列出原始碼。

在gdb中,執行程式使用r或是run命令。程式的執行,有可能需要設定下面四方面的事。

  1. 程式執行引數
    set args 可指定執行時引數。如:

set args 10 20 30 40 50

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

  1. 執行環境
    path 可設定程式的執行路徑。

show paths 檢視程式的執行路徑。

set environment varname [=value] 設定環境變數。如:

set env USER=hchen

show environment [varname] 檢視環境變數。

  1. 工作目錄
    cd 相當於shell的cd命令。

pwd 顯示當前的所在目錄。

  1. 程式的輸入輸出
    info terminal 顯示程式用到的終端的模式。

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

run > outfile

tty命令可以指寫輸入輸出的終端裝置。如:

tty /dev/ttyb

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

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

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

4.1.5 暫停/恢復程式執行
除錯程式中,暫停程式執行是必需的,gdb可以方便地暫停程式的執行。可以設定程式在哪行停住,在什麼條件下停住,在收到什麼訊號時停往等,以便於使用者檢視執行時的變數,以及執行時的流程。

當程式被gdb停住時,可以使用info program 來檢視程式是否在執行、程式號、被暫停的原因。

在gdb中,有以下幾種暫停方式:斷點(BreakPoint)、觀察點(WatchPoint)、捕捉點(CatchPoint)、訊號(Signals)及執行緒停止(Thread Stops)。

如果要恢復程式執行,可以使用c或是continue命令。

  1. 設定斷點(BreakPoint)
    用break命令來設定斷點。有下面幾種設定斷點的方法:

break

在進入指定函式時停住。C++中可以使用class::function或function(type,type)格式來指定函式名。

break

在指定行號停住。

break +offset

break -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]

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

watch

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

rwatch

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

awatch

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

info watchpoints

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

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

catch

當event發生時,停住程式。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下有用)。

tcatch

只設定一次捕捉點,當程式停住以後,應點被自動刪除。

  1. 維護停止點
    上面說了如何設定程式的停止點,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自動刪除。

  1. 停止條件維護
    前面在介紹設定斷點時,提到過可以設定一個條件,當條件成立時,程式自動停止。這是一個非常強大的功能,這裡,專門介紹這個條件的相關維護命令。

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

condition

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

condition

清除斷點號為bnum的停止條件。

還有一個比較特殊的維護命令ignore,可以指定程式執行時,忽略停止條件幾次。

ignore

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

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

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就行了。

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

GDB除錯Go程式 實踐

說明:作為一門靜態語言,似乎支援除錯是必須的,而且,Go初學者喜歡問的問題也是:大家都用什麼IDE?怎麼除錯?

其實,Go是為多核和併發而生,真正的專案,你用單步除錯,原本沒問題的,可能會調出有問題。更好的除錯方式是跟PHP這種語言一樣,用列印的方式(日誌或print)。

當然,簡單的小程式,如果單步除錯,可以看到一些內部的執行機理,對於學習還是挺有好處的。下面介紹一下用GDB除錯Go程式:(目前IDE支援除錯Go程式,用的也是GDB。要求GDB 7.1以上)

以下內容來自雨痕的《Go語言學習筆記》(下載Go資源):

預設情況下,編譯過的二進位制檔案已經包含了 DWARFv3 除錯資訊,只要 GDB7.1 以上版本都可以進行除錯。 在OSX下,如無法執行除錯指令,可嘗試用sudo方式執行gdb。

刪除除錯符號:go build -ldflags “-s -w”

-s: 去掉符號資訊。
-w: 去掉DWARF除錯資訊。
關閉內聯優化:go build -gcflags “-N -l”

除錯相關函式:

runtime.Breakpoint():觸發偵錯程式斷點。
runtime/debug.PrintStack():顯示除錯堆疊。
log:適合替代 print顯示除錯資訊。
GDB 除錯支援:

引數載入:gdb -d $GCROOT 。
手工載入:source pkg/runtime/runtime-gdb.py。
更多細節,請參考: http://golang.org/doc/gdb

除錯演示:(OSX 10.8.2, Go1.0.3, GDB7.5.1)

package main

import (
    "fmt"
    "runtime"
)
func test(s string, x int) (r string) {
    r = fmt.Sprintf("test: %s %d", s, x)
    runtime.Breakpoint()
    return r
}
func main() {
    s := "haha"
    i := 1234
    fmt.println(test(s, i))
}
$ go build -gcflags “-N -l” // 編譯,關閉內聯優化。
$ sudo gdb demo // 啟動 gdb 偵錯程式,手工載入 Go Runtime 。
GNU gdb (GDB) 7.5.1
Reading symbols from demo…done.
(gdb) source /usr/local/go/src/pkg/runtime/runtime-gdb.py
Loading Go Runtime support.
(gdb) l main.main   // 以 .方式檢視原始碼。
    r = fmt.Sprintf(“test: %s %d”, s, x)
    runtime.Breakpoint()
    return r
}
func main() {
    s := “haha”
    i := 1234
    fmt.println(test(s, i))
}
(gdb) l main.go:8 // 以 :方式檢視原始碼。
import (
  “fmt”
  “runtime”
)
func test(s string, x int) (r string) {
    r = fmt.Sprintf(“test: %s %d”, s, x)
    runtime.Breakpoint()
    return r
}
(gdb) b main.main // 以 .方式設定斷點。
Breakpoint 1 at 0×2131: file main.go, line 14.

(gdb) b main.go:17 // 以 :方式設定斷點。
Breakpoint 2 at 0×2167: file main.go, line 17.

(gdb) info breakpoints // 檢視所有斷點。
Num Type Disp Enb Address What
1 breakpoint keep y 0×0000000000002131 in main.main at main.go:14
2 breakpoint keep y 0×0000000000002167 in main.main at main.go:17

(gdb) r // 啟動程式,觸發第一個斷點。
Starting program: demo
[New Thread 0x1c03 of process 4088]
[Switching to Thread 0x1c03 of process 4088]
Breakpoint 1, main.main () at main.go:14
14 func main() {

(gdb) info goroutines // 檢視 goroutines 資訊。
* 1 running runtime.gosched
* 2 syscall runtime.entersyscall

(gdb) goroutine 1 bt // 檢視指定序號的 goroutine 呼叫堆疊。
\#0 0x000000000000f6c0 in runtime.gosched () at pkg/runtime/proc.c:927
\#1 0x000000000000e44c in runtime.main () at pkg/runtime/proc.c:244
\#2 0x000000000000e4ef in schedunlock () at pkg/runtime/proc.c:267
\#3 0×0000000000000000 in ?? ()

(gdb) goroutine 2 bt // 這個 goroutine 貌似跟 GC 有關。
\#0 runtime.entersyscall () at pkg/runtime/proc.c:989
\#1 0x000000000000d01d in runtime.MHeap_Scavenger () at pkg/runtime/mheap.c:363
\#2 0x000000000000e4ef in schedunlock () at pkg/runtime/proc.c:267
\#3 0×0000000000000000 in ?? ()

(gdb) c / / 繼續執行,觸發下一個斷點。
Continuing.
Breakpoint 2, main.main () at main.go:17
17! ! println(test(s, i))

(gdb) info goroutines // 當前 goroutine 序號為 1。
* 1 running runtime.gosched
2 runnable runtime.gosched

(gdb) goroutine 1 bt // 當前 goroutine 呼叫堆疊。
\#0 0x000000000000f6c0 in runtime.gosched () at pkg/runtime/proc.c:927
\#1 0x000000000000e44c in runtime.main () at pkg/runtime/proc.c:244
\#2 0x000000000000e4ef in schedunlock () at pkg/runtime/proc.c:267
\#3 0×0000000000000000 in ?? ()

(gdb) bt // 檢視當前調⽤堆疊,可以與當前 goroutine 呼叫堆疊對比。
\#0 main.main () at main.go:17
\#1 0x000000000000e44c in runtime.main () at pkg/runtime/proc.c:244
\#2 0x000000000000e4ef in schedunlock () at pkg/runtime/proc.c:267
\#3 0×0000000000000000 in ?? ()

(gdb) info frame // 堆疊幀資訊。
Stack level 0, frame at 0x442139f88:
rip = 0×2167 in main.main (main.go:17); saved rip 0xe44c
called by frame at 0x442139fb8
source language go.
Arglist at 0x442139f28, args:
Locals at 0x442139f28, Previous frame’s sp is 0x442139f88
Saved registers:
rip at 0x442139f80

(gdb) info locals // 檢視區域性變數。
i = 1234
s = “haha”

(gdb) p s // 以 Pretty-Print 方式檢視變數。
$1 = “haha”

(gdb) p $len(s) // 獲取物件長度($cap)
\$2 = 4

(gdb) whatis i // 檢視物件型別。
type = int

(gdb) c // 繼續執行,觸發 breakpoint() 斷點。
Continuing.
Program received signal SIGTRAP, Trace/breakpoint trap.
runtime.breakpoint () at pkg/runtime/asm_amd64.s:81
81 RET

(gdb) n // 從 breakpoint() 中出來,執行原始碼下一行程式碼。
main.test (s=”haha”, x=1234, r=”test: haha 1234″) at main.go:11
11 return r

(gdb) info args // 從引數資訊中,我們可以看到命名返回引數的值。
s = “haha”
x = 1234
r = “test: haha 1234″

(gdb) x/3xw &r // 檢視 r 記憶體資料。(指標 8 + 長度 4)
0x442139f48: 0×42121240 0×00000000 0x0000000f
(gdb) x/15xb 0×42121240 // 檢視字串位元組陣列
0×42121240: 0×74 0×65 0×73 0×74 0x3a 0×20 0×68 0×61
0×42121248: 0×68 0×61 0×20 0×31 0×32 0×33 0×34

(gdb) c // 繼續執行,程式結束。
Continuing.
test: haha 1234
[Inferior 1 (process 4088) exited normally]

(gdb) q // 退出 GDB

相關文章