linux下gdb單步調試(中)(轉摘)

來源:互聯網
上載者:User

一、設定斷點( 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]

二、設定觀察點( WatchPoint )

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

watch <expr>
為運算式(變數) expr 設定一個觀察點。一量運算式值有變化時,馬上停住程式。

rwatch <expr>
當運算式(變數) expr 被讀時,停住程式。

awatch <expr>
當運算式(變數)的值被讀或被寫時,停住程式。

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

三、設定捕捉點( 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>
只設定一次捕捉點,當程式停住以後,應點被自動刪除。

四、維護停止點

上面說了如何設定程式的停止點, GDB 中的停止點也就是上述的三類。在 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 自動刪除。

五、停止條件維護

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

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

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

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

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

六、為停止點設定運行命令

我們可以使用 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 就行了。

七、斷點菜單

在 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 表示所有函數都設定斷點。

八、恢複程式運行和單步調試

當程式被停住了,你可以用 continue 命令恢複程式的運行直到程式結束,或下一個斷點到來。也可以使用 step 或 next 命令單步跟蹤程式。

continue [ignore-count]
c [ignore-count]
fg [ignore-count]
恢複程式運行,直到程式結束,或是下一個斷點到來。 ignore-count 表示忽略其後的斷點次數。 continue , c , fg 三個命令都是一樣的意思。

step <count>
單步跟蹤,如果有函數調用,他會進入該函數。進入函數的前提是,此函數被編譯有 debug 資訊。很像 VC 等工具中的 step in 。後面可以加 count 也可以不加,不加表示一條條地執行,加表示執行後面的 count 條指令,然後再停住。

next <count>
同樣單步跟蹤,如果有函數調用,他不會進入該函數。很像 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 ” ,當運行完這個命令後,單步跟蹤會在打出程式碼的同時打出機器指令(也就是彙編代碼)

九、訊號( Signals )

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

GDB 有能力在你偵錯工具的時候處理任何一種訊號,你可以告訴 GDB 需要處理哪一種訊號。你可以要求 GDB 收到你所指定的訊號時,馬上停住正在啟動並執行程式,以供你進行調試。你可以用 GDB 的 handle 命令來完成這一功能。

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 不處理訊號。這表示, GDB 會把這個訊號交給被偵錯工具會處理。
nopass
ignore
當被調試的程式收到訊號時, GDB 不會讓被偵錯工具來處理這個訊號。

info signals
info handle
查看有哪些訊號在被 GDB 檢測中。

十、線程( 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 停住時,所有的運行線程都會被停住。這方便你你查看運行程式的總體情況。而在你恢複程式運行時,所有的線程也會被恢複運行。那怕是主進程在被單步調試時。

查看棧資訊
—————

當程式被停住了,你需要做的第一件事就是查看程式是在哪裡停住的。當你的程式調用了一個函數,函數的地址,函數參數,函數內的局部變數都會被壓入 “ 棧 ” ( Stack )中。你可以用 GDB 命令來查看當前的棧中的資訊。

下面是一些查看函數調用棧資訊的 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
列印出當前的函數中的異常處理資訊。

查看來源程式
—————

一、顯示原始碼

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

list <linenum>
顯示程式第 linenum 行的周圍的來源程式。

list <function>
顯示函數名為 function 的函數的來源程式。

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

list -
顯示當前行前面的來源程式。

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

set listsize <count>
設定一次顯示原始碼的行數。

show listsize
查看當前 listsize 的設定。

list 命令還有下面的用法:

list <first>, <last>
顯示從 first 行到 last 行之間的原始碼。

list , <last>
顯示從當前行到 last 行之間的原始碼。

list +
往後顯示原始碼。

一般來說在 list 後面可以跟以下這們的參數:

<linenum>   行號。
<+offset>   當前行號的正位移量。
<-offset>   當前行號的負位移量。
<filename:linenum> 哪個檔案的哪一行。
<function> 函數名。
<filename:function> 哪個檔案中的哪個函數。
<*address> 程式運行時的語句在記憶體中的地址。

二、搜尋原始碼

不僅如此, GDB 還提供了原始碼搜尋的命令:

forward-search <regexp>
search <regexp>
向前面搜尋。

reverse-search <regexp>
全部搜尋。

其中, <regexp> 就是Regex,也主一個字串的匹配模式,關於Regex,我就不在這裡講了,還請各位查看相關資料。

三、指定源檔案的路徑

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

directory <dirname ... >
dir <dirname ... >
加一個源檔案路徑到當前路徑的前面。如果你要指定多個路徑, UNIX 下你可以使用 “ : ” , Windows 下你可以使用 “ ; ” 。
directory
清除所有的自訂的源檔案搜尋路徑資訊。

show directories
顯示定義了的源檔案搜尋路徑。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.