GDB使用二

五、停止條件維護

 

前面在說到設置斷點時,我們提到過可以設置一個條件,當條件成立時,程序自動停止,這是一個非常強大的功能,這裏,我想專門說說這個條件的相關維護命令。一般來說,爲斷點設置一個條件,我們使用if關鍵詞,後面跟其斷點條件。並且,條件設置好後,我們可以用condition命令來修改斷點的條件。(只有breakwatch命令支持ifcatch目前暫不支持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函數中大於0GDB會自動打印出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 0xb 26c: 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命令恢復程序的運行直到程序結束,或下一個斷點到來。也可以使用stepnext命令單步跟蹤程序。

 

    continue [ignore-count]

    c [ignore-count]

    fg [ignore-count]

        恢復程序運行,直到程序結束,或是下一個斷點到來。ignore-count表示忽略其後的斷點次數。continuecfg三個命令都是一樣的意思。

 

 

    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

        單步跟蹤一條機器指令!一條程序代碼有可能由數條機器指令完成,stepinexti可以單步執行機器指令。與之一樣有相同功能的命令是“display/i $pc ,當運行完這個命令後,單步跟蹤會在打出程序代碼的同時打出機器指令(也就是彙編代碼)

 

 

九、信號(Signals

信號是一種軟中斷,是一種處理異步事件的方法。一般來說,操作系統都支持許多信號。尤其是UNIX,比較重要應用程序一般都會處理信號。UNIX定義了許多信號,比如SIGINT表示中斷字符信號,也就是Ctrl+C的信號,SIGBUS表示硬件故障的信號;SIGCHLD表示子進程狀態改變信號;SIGKILL表示終止程序運行的信號,等等。信號量編程是UNIX下非常重要的一種技術。

 

GDB有能力在你調試程序的時候處理任何一種信號,你可以告訴GDB需要處理哪一種信號。你可以要求GDB收到你所指定的信號時,馬上停住正在運行的程序,以供你進行調試。你可以用GDBhandle命令來完成這一功能。

 

    handle <signal> <keywords...>

        GDB中定義一個信號處理。信號<signal>可以以SIG開頭或不以SIG開頭,可以用定義一個要處理信號的範圍(如:SIGIO-SIGKILL,表示處理從SIGIO信號到SIGKILL的信號,其中包括SIGIOSIGIOTSIGKILL三個信號),也可以使用關鍵字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,注意,這個IDGDB分配的,你可以通過“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>就是正則表達式,也主一個字符串的匹配模式,關於正則表達式,我就不在這裏講了,還請各位查看相關資料。

 

 

三、指定源文件的路徑

 

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

 

    directory <dirname ... >

    dir <dirname ... >

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

    directory

        清除所有的自定義的源文件搜索路徑信息。

  

    show directories

        顯示定義了的源文件搜索路徑。

      

 

四、源代碼的內存

 

你可以使用info line命令來查看源代碼在內存中的地址。info line後面可以跟“行號”,“函數名”,“文件名:行號”,“文件名:函數名”,這個命令會打印出所指定的源碼在運行時的內存地址,如:

 

        (gdb) info line tst.c:func

        Line 5 of "tst.c" starts at address 0x8048456 <func+6> and ends at 0x804845d <func+13>.

 

還有一個命令(disassemble)你可以查看源程序的當前執行時的機器碼,這個命令會把目前內存中的指令dump出來。如下面的示例表示查看函數func的彙編代碼。

 

        (gdb) disassemble func

        Dump of assembler code for function func:

        0x8048450 <func>:       push   �p

        0x8048451 <func+1>:     mov    %esp,�p

        0x8048453 <func+3>:     sub    $0x18,%esp

        0x8048456 <func+6>:     movl   $0x0,0xfffffffc(�p)

        0x804845d <func+13>:    movl   $0x1,0xfffffff8(�p)

        0x8048464 <func+20>:    mov    0xfffffff8(�p),�x

        0x8048467 <func+23>:    cmp    0x8(�p),�x

        0x804846a <func+26>:    jle    0x8048470 <func+32>

        0x804846c <func+28>:    jmp    0x8048480 <func+48>

        0x804846e <func+30>:    mov    %esi,%esi

        0x8048470 <func+32>:    mov    0xfffffff8(�p),�x

        0x8048473 <func+35>:    add    �x,0xfffffffc(�p)

        0x8048476 <func+38>:    incl   0xfffffff8(�p)

        0x8048479 <func+41>:    jmp    0x8048464 <func+20>

        0x804847b <func+43>:    nop

        0x804847c <func+44>:    lea    0x0(%esi,1),%esi

        0x8048480 <func+48>:    mov    0xfffffffc(�p),�x

        0x8048483 <func+51>:    mov    �x,�x

        0x8048485 <func+53>:    jmp    0x8048487 <func+55>

        0x8048487 <func+55>:    mov    �p,%esp

        0x8048489 <func+57>:    pop    �p

        0x804848a <func+58>:    ret

        End of assembler dump.

 

 

查看運行時數據

———————

  

    在你調試程序時,當程序被停住時,你可以使用print命令(簡寫命令爲p),或是同義命令inspect來查看當前程序的運行數據。print命令的格式是:

  

    print <expr>

    print /<f> <expr>

        <expr>是表達式,是你所調試的程序的語言的表達式(GDB可以調試多種編程語言),<f>是輸出的格式,比如,如果要把表達式按16進制的格式輸出,那麼就是/x

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章