Linux下調試工具gdb的使用詳解

來源:互聯網
上載者:User

前提:廢話不多講,無法跟蹤進入調試,講得再多也是廢話。

如何進入調試?

1.編譯加上 -g 選項

2.執行 gdb a.out    (注意:a.out一定要存在,或者是指定路徑,這樣gdb才能導入並進行詳細分析)

3.查看源檔案並設置斷點(設置斷點可以設置函數斷點,比如 b myfuncname)

4.執行,run arguments (需要加上a.out的參數)

 

GDB 概述
————
GDB 是 GNU 開源組織發布的一個強大的 UNIX 下的程式調試工具。或許,各位比較喜歡那種圖形介面方式的,像 VC 、 BCB 等 IDE 的調試,但如果你是在 UNIX 平台下做軟體,你會發現 GDB 這個調試工具有比 VC 、 BCB 的圖形化調試器更強大的功能。所謂 “ 寸有所長,尺有所短 ” 就是這個道理。

一般來說, GDB 主要幫忙你完成下面四個方面的功能: 
    1 、啟動你的程式,可以按照你的自訂的要求隨心所欲的運行程式。
    2 、可讓被調試的程式在你所指定的調置的斷點處停住。(斷點可以是條件運算式)
    3 、當程式被停住時,可以檢查此時你的程式中所發生的事。
    4 、動態改變你程式的執行環境。

從上面看來, GDB 和一般的調試工具沒有什麼兩樣,基本上也是完成這些功能,不過在細節上,你會發現 GDB 這個調試工具的強大,大家可能比較習慣了圖形化的調試工具,但有時候,命令列的調試工具卻有著圖形化工具所不能完成的功能。讓我們一一看來。

一個調試樣本
——————
來源程式: tst.c

#include <stdio.h>int func(int n){   int sum=0,i;   for(i=0; i<n; i++)   {           sum+=i;   }   return sum;}main(){   int i;   long result = 0;   for(i=1; i<=100; i++)   {          result += i;   }   printf("result[1-100] = %d \n", result );   printf("result[1-250] = %d \n", func(250) );}

編譯產生執行檔案:( Linux 下)
gcc -g -o tst tst.c

使用 GDB 調試:
hchen/test> gdb tst  <---------- 啟動 GDB
GNU gdb 5.1.1
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-suse-linux"...

(gdb) l     <-------------------- l 命令相當於 list ,從第一行開始例出原碼。

#include <stdio.h>

int func(int n)
{
   int sum=0,i;
   for(i=0; i<n; i++)
   {
       sum+=i;
   }
   return sum;

(gdb)       <-------------------- 直接斷行符號表示,重複上一次命令
}

main()
{
   int i;
   long result = 0;
   for(i=1; i<=100; i++)
   {
     result += i;

(gdb) break 16    <-------------------- 設定斷點,在來源程式第 16 行處。

Breakpoint 1 at 0x8048496: file tst.c, line 16.

(gdb) break func  <-------------------- 設定斷點,在函數 func() 入口處。

Breakpoint 2 at 0x8048456: file tst.c, line 5.

(gdb) info break  <-------------------- 查看斷點資訊。

Num Type           Disp Enb Address    What

1   breakpoint     keep y   0x08048496 in main at tst.c:16

2   breakpoint     keep y   0x08048456 in func at tst.c:5

(gdb) r           <--------------------- 運行程式, run 命令簡寫

Starting program: /home/hchen/test/tst

Breakpoint 1, main () at tst.c:17    <---------- 在斷點處停住。

17               long result = 0;

(gdb) n          <--------------------- 單條語句執行, next 命令簡寫。

18               for(i=1; i<=100; i++)

(gdb) n

20                       result += i;

(gdb) n

18               for(i=1; i<=100; i++)

(gdb) n

20                       result += i;

(gdb) c          <--------------------- 繼續運行程式, continue 命令簡寫。

Continuing.

result[1-100] = 5050       <---------- 程式輸出。

Breakpoint 2, func (n=250) at tst.c:5

5                int sum=0,i;

(gdb) n

6                for(i=1; i<=n; i++)

(gdb) p i        <--------------------- 列印變數 i 的值, print 命令簡寫。

$1 = 134513808

(gdb) n

8                        sum+=i;

(gdb) n

6                for(i=1; i<=n; i++)

(gdb) p sum

$2 = 1

(gdb) n

8                        sum+=i;

(gdb) p i

$3 = 2

(gdb) n

6                for(i=1; i<=n; i++)

(gdb) p sum

$4 = 3

(gdb) bt        <--------------------- 查看函數堆棧。

#0  func (n=250) at tst.c:5

#1  0x080484e4 in main () at tst.c:24

#2  0x400409ed in __libc_start_main () from /lib/libc.so.6

(gdb) finish    <--------------------- 退出函數。

Run till exit from #0  func (n=250) at tst.c:5

0x080484e4 in main () at tst.c:24

24              printf("result[1-250] = %d \n", func(250) );

Value returned is $6 = 31375

(gdb) c     <--------------------- 繼續運行。

Continuing.

result[1-250] = 31375    <---------- 程式輸出。

Program exited with code 027. <-------- 程式退出,調試結束。

(gdb) q     <--------------------- 退出 gdb 。

hchen/test>

好了,有了以上的感性認識,還是讓我們來系統地認識一下 gdb 吧。

在 gdb 中,我們可以有以下幾種暫停方式:斷點( BreakPoint )、觀察點( WatchPoint )、捕捉點( CatchPoint )、訊號( Signals )、線程停止( Thread Stops )。如果要恢複程式運行,可以使用 c 或是 continue 命令。

一、設定斷點( 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_r>
        修改斷點號為 bnum 的停止條件為 expression_r 。
    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 語言環境的命令:

    show language
        查看當前的語言環境。如果 GDB 不能識為你所調試的程式設計語言,那麼, C 語言被認為是預設的環境。

    info frame
        查看當前函數的程式語言。

    info source
        查看當前檔案的程式語言。

如果 GDB 沒有檢測出當前的程式語言,那麼你也可以手動設定當前的程式語言。使用 set language 命令即可做到。
   當 set language 命令後什麼也不跟的話,你可以查看 GDB 所支援的語言種類:

        (gdb) set language
        The currently understood settings are:
        local or auto    Automatic setting based on source file

        c                Use the C language

        c++              Use the C++ language

        asm              Use the Asm language

        chill            Use the Chill language

        fortran          Use the Fortran language

        java             Use the Java language

        modula-2         Use the Modula-2 language

        pascal           Use the Pascal language

        scheme           Use the Scheme language
    於是你可以在 set language 後跟上被列出來的程式語言名,來設定當前的語言環境。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.