天天看點

gdb學習筆記(一)

這裡隻是一個摘要。具體的細節還需要去看manual。

1 info 用來描述你的程式的狀态,比如info b就是顯示出目前的程式的所有斷點.

2 set 用來設定一些環境變量的值,比如set prompt $.

3 show用來描述gdb自己的狀态.

編譯要用-g選項.

然後用gdb +程式名,或者直接gdb後,用file + 檔案名加載程式.

1 run/r 運作程式.

2 set args 設定程式的參數.

3 path directory 加一個目錄到環境變量path

4 set directory 設定gdb的工作目錄

5 pwd 目前的工作目錄

6 attch process-id 調試 運作的程序

dettach 當調試程序完畢,release 掉gdb的控制.

調試多線程.

1 thread threadno 選擇目前的線程.

2 info thread 檢視目前程式的線程.

debug多程序:

預設情況下,當fork一個子程序之後,gdb會繼續debug父程序,而子程序會運作下去.不過我們能夠改變這個.

set follow-fork-mode mode 這裡mode可以為parent或者child. parent是預設值,而child的話就是gdb繼續debug子程序,而父程序會運作下去.

如果你想要同時debug父子程序,也可以設定:

set detach-on-fork mode 預設是on,也就是隻能debug一個程序,如果改為off則可以同時debug父子程序.

儲存一個書簽稍後傳回.

checkpoint 儲存目前的程式的狀态.

restart checkpoint-id 傳回到checkpoint-id那個點.這個值可以用info checkpoint來檢視

breakpoint,watchpoint以及catchpoint

breakpoint 就是斷點.

watchpoint 就是用來檢測變量的改變,他可以看做是特殊的斷點,也就是當變量改變時停止程式.

catchpoint 是另外一種特殊的斷點,用來監測某一事件的發生,有點類似其它語言中的異常.

1 設定Breakpoint

break location 設定斷點.location可以為行号,函數名或者指令位址.

break 設定斷點,不過這個斷點為目前棧幀的下一條指令.

break location if condition 當condition為真時,程式到達這個斷點才起作用.

tbreaks args, 一次性的斷點.

rbreaks regex 設定斷點在所有與regex比對的函數.這個正規表達式的語義與grep的相同.

2 設定watchpoint

watch expr[thread threadno] 設定檢測變量expr,後面可以跟着改變這個變量的線程.如果跟着線程号,則說明隻有當這個線程改變變量時,程式才會stop.

rwatch expr[thread threadno] 上面是監測變量改變,而這個指令是監測程式讀取變量.

awatch expr[thread threadno] 當expr要麼被讀,要麼被寫時,程式直接break.

3 設定catchpoint

catch event 當event發生的時候程式停止.

event可以是下面的幾種類型: throw ,catch,exception,exception unhandled,assert,exec,fork,vfork.

tcatch event 一次性的監測事件.

删除斷點

clear 删除在目前的棧幀的将要被執行的下一條指令斷點.

clear location 删除location位置的斷點.其實更有用的是下面幾個指令:

clear function,clear filename:function ,clear linenum,clear filename:linenum.

delete [breakpoints][range...] 其實也就是删除多個斷點,如果沒有range,咋就是删除全部斷點.

關閉斷點

一個斷點(包括 watchpoint和catchpoint)可以有下面四種狀态.

打開,關閉,enabled once,enabled for deletion

disable [breakpoints][range..] 關閉指定的斷點或者全部斷點(如果沒有range)

enable [breakpoints][range..] 打開指定的斷點或者全部斷點(如果沒有range)

enable [breakpoints] once range 臨時打開指定的斷點(也就是說是一次性的,.

enable [breakpoints] delete range 臨時打開指定的斷點并隻工作一次,也就是一次之後這個斷點将會被删除.

break condition

contidition bnum expression 當expression為真的時候,程式到達這個斷點才會停止.

contidition bnum 從斷點bnum删除掉一個condition.

ignore bnum count 設定一個斷點bnum的忽略次數為count.也就是隻有count次數後,這個斷點才會起作用.

斷點指令清單

這個主要是用來當到達這個斷點,程式停止後,你想要執行一連串的指令.格式為:

command [bnum]
... command-list ..
end
           

指定一堆指令給斷點bnum.如果想删除指令的話就把command-list置為空就行了.

如果沒有bnum,則這個command指的是最後設定的一個斷點.

這裡有個例子:

break 403
commands
///不輸出任何東西
silent
///改變x的值
set x = y + 4
///然後continue
cont
end
           

接下來來看continue和step

continue表示讓程式繼續執行,直到下一個斷點或者執行完畢。

step表示讓程式執行一行代碼或者說一條機器指令(依賴于你選擇的指令)。

下面來看指令:

continue [ignore-count]
c [ignore-count]
fg [ignore-count]
           

這幾個指令都是resume一個程式,然後參數ignore-count表示忽略目前這個斷點的次數。

step [count]

繼續運作程式直到抵達一個新的代碼行(它會跟入函數).這裡要記住step隻會停止在source line的第一條指令。

如果加上count參數則表示它會step count次。如果遇到斷點則會停止。

next [count]

和step很類似,差別就是不會跟進函數。

set step-mode
set step-mode on
set step-mode off
           

on就可以使step停止在沒有debug資訊的函數的第一條指令上。off則是直接執行完這個函數。

這裡要注意上面的指令都隻是跳一行代碼。而不是一條指令。

util 繼續運作直到source line通過了目前的行。這個指令主要是針對循環語句中的step。比如你在循環結尾設定util,則隻有當循環退出時才會在這個斷點停止。而不是每次都停止。

util location 繼續運作直到指定的location,或者目前的棧幀傳回。

advance location 繼續運作直到給定的location,這個相比與上面的指令,它就象全局的。

stepi [arg]

執行一條機器指令。 arg表示次數。

nexti [arg]

和next類似隻不過執行的是一條機器指令。

下來來看信号。

gdb可以監測在你的程式中的任何信号。

來看指令。

handle signal [keywords...]

這個指令用來改變信号signal(名字或者數字)在gdb中的行為。

其中關鍵就是keywords.在這裡keywords可以為下面幾種類型:

1 nostop gdb接收到信号不會停止程式,而隻是列印出一段message

2 stop 和上面類似隻不過會停止程式。

3 print 當信号發生必須列印一條消息通知。

4 noprint 信号發生,gdb将不會列印任何東西。

5 pass和noignore 這兩個是同義的。表示信号對你的程式是可見的。

6 nopass和ignore 這兩個也是同義的。和上面相反。。

在gdb中,當你的程式由于一個信号而停止後,直到你繼續執行,否則信号對你的程式是不可見得。也就是說當gdb捕捉到信号,我們可以用nopass或者ignore來使信号對我們的程式為不可見。

最後來看下多線程程式的調試。

首先來看多線程調試的幾種模式。

1 all-stop模式。

在這種模式中,當你的程式在gdb由于任何原因而停止,此時所有的線程都會停止。而不僅僅是目前的線程。一般來說gdb不能單步所有的線程。因為線程排程gdb是無法控制的。無論什麼時候當gdb停止你的程式,它都會自動切換到觸發斷點的那個線程。

在一些os中我們可以通過lock線程排程器,進而達到隻有一個線程在運作。

set scheduler-locking mode

設定模式,如果mode是off,則表示沒有lock,則任何線程在任何時候都有可能在運作。當mode為on的時候,鎖定其他的線程,也就是隻有目前線程在執行。也就是你單步的時候其他線程是不會運作的。這個對我們隻關注本線程比較重要。

預設情況下,當你鍵入step或者next指令時,gdb隻允許目前程序的線程運作。我們可以通過指令來修改這個預設值。

set schedule-multiple mode

當mode為on則所有程序的所有線程都勻許運作。否則隻有目前的程序的線程能夠resume。

2 none-stop模式。

顧名思義,當程式在gdb中停止,隻有目前的線程會被停止,而其他的線程将會繼續運作。

這個時候step,next這些指令就隻對目前的線程起作用。

我們要打開這個模式需要這樣操作:

# Enable the async interface.
set target-async 1
# If using the CLI, pagination breaks non-stop.
set pagination off
# Finally, turn it on!
set non-stop on
           

這裡要注意打開這個模式必須得在你attach或運作這個程式或者程序之前才能進行。

Background Execution

gdb執行指令有兩種類型:前台的(同步)和背景(異步)的。

差別很簡單,前台的話,gdb在輸出提示符之前會等待程式report一些線程已經終止的資訊。而異步的則是直接傳回。

我們需要顯式打開異步模式。

set target-async on

下面就是支援異步的指令:

run 
attach 
step 
stepi 
next 
nexti 
continue 
finish 
until
           

通過上面我們可以看到異步模式主要用在none-stop模式中。

如果你想停止背景運作的程式,那麼使用interrupt

在all-stop模式中interrupt将會停止所有的線程。而在none-stop中隻會停止目前線程。interrupt -a此時就能停止所有線程。’

當你有多個線程,你此時隻想給某個線程設定斷點,這個時候可以用這個指令:

break linespec thread threadno

break linespec thread threadno if ...

linespec為源碼行号,threadno為線程id。

最後來看下多線程調試中可能會遇到的一個問題:

如果一個線程在一個斷點,或者由于其他什麼原因停止,此時另外的線程阻塞在一個系統調用。這個時候這個系統調用就有可能會過早的傳回。是以我們在調用系統調用,最好都要檢測它的傳回值。

舉個例子:

sleep (10);

這個我們應該改成這樣:

int unslept = 10;
while (unslept > 0)
unslept = sleep (unslept);