天天看點

用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   %ebp

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

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

        0x8048456 <func+6>:     movl   $0x0,0xfffffffc(%ebp)

        0x804845d <func+13>:    movl   $0x1,0xfffffff8(%ebp)

        0x8048464 <func+20>:    mov    0xfffffff8(%ebp),%eax

        0x8048467 <func+23>:    cmp    0x8(%ebp),%eax

        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(%ebp),%eax

        0x8048473 <func+35>:    add    %eax,0xfffffffc(%ebp)

        0x8048476 <func+38>:    incl   0xfffffff8(%ebp)

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

        0x804847b <func+43>:    nop

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

        0x8048480 <func+48>:    mov    0xfffffffc(%ebp),%edx

        0x8048483 <func+51>:    mov    %edx,%eax

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

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

        0x8048489 <func+57>:    pop    %ebp

        0x804848a <func+58>:    ret

        End of assembler dump.

繼續閱讀