GDB十分鐘教程

GDB十分鐘教程linux

做者: liigo
原文連接: 
http://blog.csdn.net/liigo/archive/2006/01/17/582231.aspx
日期: 2006年1月16日
小程序

本文寫給主要工做在Windows操做系統下而又需要開發一些跨平臺軟件的程序猿朋友,以及程序愛好者。函數

GDB是一個由GNU開源組織公佈的、UNIX/LINUX操做系統下的、基於命令行的、功能強大的程序調試工具。工具

GDB中的命令當然很是多,但咱們僅僅需掌握當中十個左右的命令,就大體可以完畢平常的主要的程序調試工做。post

 命令  解釋  演示樣例
file <文件名稱> 載入被調試的可運行程序文件。
因爲通常都在被調試程序所在文件夾下運行GDB,於是文本名不需要帶路徑。
(gdb) file gdb-sample
r Run的簡寫,執行被調試的程序。
假設此前沒有下過斷點,則執行完整個程序;假設有斷點,則程序暫停在第一個可用斷點處。
(gdb) r
c Continue的簡寫,繼續運行被調試程序,直至下一個斷點或程序結束。 (gdb) c
b <行號>
b <函數名稱>
b *<函數名稱>
b *<代碼地址>

d [編號]ui

b: Breakpoint的簡寫,設置斷點。兩可以使用「行號」「函數名稱」「運行地址」等方式指定斷點位置。
當中在函數名稱前面加「*」符號表示將斷點設置在「由編譯器生成的prolog代碼處」。假設不瞭解彙編,可以不予理會此使用方法。

d: Delete breakpoint的簡寫,刪除指定編號的某個斷點,或刪除所有斷點。斷點編號從1開始遞增。spa

(gdb) b 8
(gdb) b main
(gdb) b *main
(gdb) b *0x804835c

(gdb) d操作系統

s, n s: 運行一行源程序代碼,假設此行代碼中有函數調用,則進入該函數;
n: 運行一行源程序代碼,此行代碼中的函數調用也一併運行。

s 至關於其餘調試器中的「Step Into (單步跟蹤進入)」;
n 至關於其餘調試器中的「Step Over (單步跟蹤)」。
.net

這兩個命令必須在有源碼調試信息的狀況下才幹夠使用(GCC編譯時使用「-g」參數)。命令行

(gdb) s
(gdb) n
si, ni si命令類似於s命令,ni命令類似於n命令。所不一樣的是,這兩個命令(si/ni)所針對的是彙編指令,而s/n針對的是源碼。 (gdb) si
(gdb) ni
p <變量名稱> Print的簡寫,顯示指定變量(暫時變量或全局變量)的值。 (gdb) p i
(gdb) p nGlobalVar
display ...

undisplay <編號>

display,設置程序中斷後欲顯示的數據及其格式。
好比,假設但願每次程序中斷後可以看到即將被運行的下一條彙編指令,可以使用命令
「display /i $pc」
當中 $pc 表明當前彙編指令,/i 表示以十六進行顯示。當需要關心彙編代碼時,此命令至關實用。

undispaly,取消先前的display設置,編號從1開始遞增。

(gdb) display /i $pc

(gdb) undisplay 1

i Info的簡寫,用於顯示各種信息,詳情請查閱「help i」。 (gdb) i r
q Quit的簡寫,退出GDB調試環境。 (gdb) q
help [命令名稱] GDB幫助命令,提供對GDB名種命令的解釋說明。
假設指定了「命令名稱」參數,則顯示該命令的具體說明;假設沒有指定參數,則分類顯示所有GDB命令,供用戶進一步瀏覽和查詢。
(gdb) help display

 

廢話很少說,如下開始實踐。

先給出一個演示樣例用的小程序,C語言代碼,簡單的不能再簡單了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

//此程序僅做爲「GDB十分鐘教程」的演示樣例代碼, by liigo
//Email:
liigo@sina.com
//blog: http://blog.csdn.net/liigo
//WebSite: www.liigo.com


#include
<stdio.h>

int
nGlobalVar = 0;

int
tempFunction(int a, int b)
{
    printf("tempFunction is called, a = %d, b = %d /n", a, b
);
    return (a + b
);
}


int
main()
{
    int n
;
    n = 1
;
    n
++;
    n
--;

    nGlobalVar += 100
;
    nGlobalVar -= 12
;

    printf("n = %d, nGlobalVar = %d /n", n, nGlobalVar
);

    n = tempFunction(1, 2
);
    printf("n = %d", n
);

    return 0
;
}

請將此代碼複製出來並保存到文件 gdb-sample.c 中,而後切換到此文件所在文件夾,用GCC編譯之:

gcc gdb-sample.c -o gdb-sample -g

在上面的命令行中,使用 -o 參數指定了編譯生成的可運行文件名稱爲 gdb-sample,使用參數 -g 表示將源碼信息編譯到可運行文件裏。假設不使用參數 -g,會給後面的GDB調試形成不便。固然,假設咱們沒有程序的源碼,天然也無從使用 -g 參數,調試/跟蹤時也僅僅能是彙編代碼級別的調試/跟蹤。

如下「gdb」命令啓動GDB,將首先顯示GDB說明,不管它:

GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
Copyright 2003 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-redhat-linux-gnu".
(gdb) 

上面最後一行「(gdb) 」爲GDB內部命令引導符,等待用戶輸入GDB命令。

如下使用「file」命令加載被調試程序 gdb-sample(這裏的 gdb-sample 即前面 GCC 編譯輸出的可運行文件):

(gdb) file gdb-sample
Reading symbols from gdb-sample...done.

上面最後一行提示已經載入成功。

如下使用「r」命令運行(Run)被調試文件,因爲還沒有設置不論什麼斷點,將直接運行到程序結束:

(gdb) r
Starting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sample
n = 1, nGlobalVar = 88
tempFunction is called, a = 1, b = 2
n = 3
Program exited normally.

如下使用「b」命令在 main 函數開頭設置一個斷點(Breakpoint):

(gdb) b main
Breakpoint 1 at 0x804835c: file gdb-sample.c, line 19.

上面最後一行提示已經成功設置斷點,並給出了該斷點信息:在源文件 gdb-sample.c 第19行處設置斷點;這是本程序的第一個斷點(序號爲1);斷點處的代碼地址爲 0x804835c(此值可能僅在本次調試過程當中有效)。回過頭去看源碼,第19行中的代碼爲「n = 1」,剛好是 main 函數中的第一個可運行語句(前面的「int n;」爲變量定義語句,並非可運行語句)。

再次使用「r」命令運行(Run)被調試程序:

(gdb) r
Starting program: /home/liigo/temp/gdb-sample

Breakpoint 1, main () at gdb-sample.c:19
19 n = 1;

程序中斷在gdb-sample.c第19行處,即main函數是第一個可運行語句處。

上面最後一行信息爲:下一條將要運行的源碼爲「n = 1;」,它是源碼文件gdb-sample.c中的第19行。

如下使用「s」命令(Step)運行下一行代碼(即第19行「n = 1;」):

(gdb) s
20 n++;

上面的信息表示已經運行完「n = 1;」,並顯示下一條要運行的代碼爲第20行的「n++;」。

既然已經運行了「n = 1;」,即給變量 n 賦值爲 1,那咱們用「p」命令(Print)看一下變量 n 的值是否是 1 :

(gdb) p n
$1 = 1

果真是 1。($1大體是表示這是第一次使用「p」命令——再次運行「p n」將顯示「$2 = 1」——此信息應該沒有什麼用處。)

如下咱們分別在第26行、tempFunction 函數開頭各設置一個斷點(分別使用命令「b 26」「b tempFunction」):

(gdb) b 26
Breakpoint 2 at 0x804837b: file gdb-sample.c, line 26.
(gdb) b tempFunction
Breakpoint 3 at 0x804832e: file gdb-sample.c, line 12.

使用「c」命令繼續(Continue)運行被調試程序,程序將中斷在第二個斷點(26行),此時全局變量 nGlobalVar 的值應該是 88;再一次運行「c」命令,程序將中斷於第三個斷點(12行,tempFunction 函數開頭處),此時tempFunction 函數的兩個參數 a、b 的值應各自是 1 和 2:

(gdb) c
Continuing.

Breakpoint 2, main () at gdb-sample.c:26
26 printf("n = %d, nGlobalVar = %d /n", n, nGlobalVar);
(gdb) p nGlobalVar
$2 = 88
(gdb) c
Continuing.
n = 1, nGlobalVar = 88

Breakpoint 3, tempFunction (a=1, b=2) at gdb-sample.c:12
12 printf("tempFunction is called, a = %d, b = %d /n", a, b);
(gdb) p a
$3 = 1
(gdb) p b
$4 = 2

上面反饋的信息一切都在咱們預料之中,哈哈~~~

再一次運行「c」命令(Continue),因爲後面再也沒有其餘斷點,程序將一直運行到結束:

(gdb) c
Continuing.
tempFunction is called, a = 1, b = 2
n = 3
Program exited normally.

 

有時候需要看到編譯器生成的彙編代碼,以進行彙編級的調試或跟蹤,又該怎樣操做呢?

這就要用到display命令「display /i $pc」了(此命令前面已有詳解):

(gdb) display /i $pc
(gdb) 

此後程序再中斷時,就可以顯示出彙編代碼了:

(gdb) r
Starting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sample

Breakpoint 1, main () at gdb-sample.c:19
19 n = 1;
1: x/i $pc 0x804835c <main+16>: movl $0x1,0xfffffffc(%ebp)

看到了彙編代碼,「n = 1;」相應的彙編代碼是「movl $0x1,0xfffffffc(%ebp)」。

並且之後程序每次中斷都將顯示下一條彙編指定(「si」命令用於運行一條彙編代碼——差異於「s」運行一行C代碼):

(gdb) si
20 n++;
1: x/i $pc 0x8048363 <main+23>: lea 0xfffffffc(%ebp),%eax
(gdb) si
0x08048366 20 n++;
1: x/i $pc 0x8048366 <main+26>: incl (%eax)
(gdb) si
21 n--;
1: x/i $pc 0x8048368 <main+28>: lea 0xfffffffc(%ebp),%eax
(gdb) si
0x0804836b 21 n--;
1: x/i $pc 0x804836b <main+31>: decl (%eax)
(gdb) si
23 nGlobalVar += 100;
1: x/i $pc 0x804836d <main+33>: addl $0x64,0x80494fc

 

接下來咱們試一下命令「b *<函數名稱>」。

爲了更簡明,有必要先刪除眼下所有斷點(使用「d」命令——Delete breakpoint):

(gdb) d
Delete all breakpoints? (y or n) y
(gdb)

當被詢問是否刪除所有斷點時,輸入「y」並按回車鍵就能夠。

如下使用命令「b *main」在 main 函數的 prolog 代碼處設置斷點(prolog、epilog,分別表示編譯器在每個函數的開頭和結尾自行插入的代碼):

(gdb) b *main
Breakpoint 4 at 0x804834c: file gdb-sample.c, line 17.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sample

Breakpoint 4, main () at gdb-sample.c:17
17 {
1: x/i $pc 0x804834c <main>: push %ebp
(gdb) si
0x0804834d 17 {
1: x/i $pc 0x804834d <main+1>: mov %esp,%ebp
(gdb) si
0x0804834f in main () at gdb-sample.c:17
17 {
1: x/i $pc 0x804834f <main+3>: sub $0x8,%esp
(gdb) si
0x08048352 17 {
1: x/i $pc 0x8048352 <main+6>: and $0xfffffff0,%esp
(gdb) si
0x08048355 17 {
1: x/i $pc 0x8048355 <main+9>: mov $0x0,%eax
(gdb) si
0x0804835a 17 {
1: x/i $pc 0x804835a <main+14>: sub %eax,%esp
(gdb) si
19 n = 1;
1: x/i $pc 0x804835c <main+16>: movl $0x1,0xfffffffc(%ebp)

此時可以使用「i r」命令顯示寄存器中的當前值———「i r」即「Infomation Register」:

(gdb) i r
eax 0xbffff6a4 -1073744220
ecx 0x42015554 1107383636
edx 0x40016bc8 1073834952
ebx 0x42130a14 1108544020
esp 0xbffff6a0 0xbffff6a0
ebp 0xbffff6a8 0xbffff6a8
esi 0x40015360 1073828704
edi 0x80483f0 134513648
eip 0x8048366 0x8048366
eflags 0x386 902
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x33 51

固然也可以顯示隨意一個指定的寄存器值:

(gdb) i r eax
eax 0xbffff6a4 -1073744220

 

最後一個要介紹的命令是「q」,退出(Quit)GDB調試環境:

(gdb) q
The program is running. Exit anyway? (y or n) y

 

版權所有:liigo.com

轉載請事先徵得做者liigo容許。

liigo@sina.com
www.liigo.com
http://blog.csdn.net/liigo/
QQ: 175199125

相關文章
相關標籤/搜索