GDB是GNU開源組織發(fā)布的一個強(qiáng)大的UNIX下的程序調(diào)試工具,GDB主要可幫助工程師完成下面4個方面的功能:
- 啟動程序,可以按照工程師自定義的要求隨心所欲的運(yùn)行程序。
- 讓被調(diào)試的程序在工程師指定的斷點(diǎn)處停住,斷點(diǎn)可以是條件表達(dá)式。
- 當(dāng)程序被停住時,可以檢查此時程序中所發(fā)生的事,并追索上文。
- 動態(tài)地改變程序的執(zhí)行環(huán)境。
不管是調(diào)試Linux內(nèi)核空間的驅(qū)動還是調(diào)試用戶空間的應(yīng)用程序,掌握gdb的用法都是必須。而且,調(diào)試內(nèi)核和調(diào)試應(yīng)用程序時使用的gdb命令是完全相同的,下面以代碼清單22.2的應(yīng)用程序?yàn)槔菔緂db調(diào)試器的用法。
使用命令gcc –g gdb_example.c –o gdb_example編譯上述程序,得到包含調(diào)試信息的二進(jìn)制文件example,執(zhí)行g(shù)db gdb_example命令進(jìn)入調(diào)試狀態(tài):
[root@localhost driver_study]# gdb gdb_example 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)
1、list命令
在gdb中運(yùn)行l(wèi)ist命令(縮寫l)可以列出代碼,list的具體形式包括:
list <linenum> 顯示程序第linenum行周圍的源程序,如:
(gdb) list 15 10 11 int array1[ 10 ] = 12 { 13 48 , 56 , 77 , 33 , 33 , 11 , 226 , 544 , 78 , 90 14 }; 15 int array2[ 10 ] = 16 { 17 85 , 99 , 66 , 0x199 , 393 , 11 , 1 , 2 , 3 , 4 18 }; 19
list <function> 顯示函數(shù)名為function的函數(shù)的源程序,如:
(gdb) list main 2 { 3 return a + b; 4 } 5 6 main() 7 { 8 int sum [ 10 ]; 9 int i; 10 11 int array1[ 10 ] =
list 顯示當(dāng)前行后面的源程序。
list - 顯示當(dāng)前行前面的源程序。
下面演示了使用gdb中的run(縮寫r)、break(縮寫b)、next(縮寫n)命令控制程序的運(yùn)行,并使用print(縮寫p)命令打印程序中的變量sum的過程:
(gdb) break add Breakpoint 1 at 0x80482f7 : file gdb_example.c, line 3 . (gdb) run Starting program: /driver_study/ gdb_example Breakpoint 1 , add (a= 48 , b= 85 ) at gdb_example.c: 3 warning: Source file is more recent than executable. 3 return a + b; (gdb) next 4 } (gdb) next main () at gdb_example.c: 23 23 for (i = 0 ; i < 10 ; i++ ) (gdb) next 25 sum [i] = add(array1[i], array2[i]); (gdb) print sum $ 1 = { 133 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }
2、run命令
在gdb中,運(yùn)行程序使用run命令。在程序運(yùn)行前,我們可以設(shè)置如下4方面的工作環(huán)境:
程序運(yùn)行參數(shù)
set args 可指定運(yùn)行時參數(shù),如: set args 10 20 30 40 50;
show args 命令可以查看設(shè)置好的運(yùn)行參數(shù)。
運(yùn)行環(huán)境
path <dir> 可設(shè)定程序的運(yùn)行路徑;
how paths 可查看程序的運(yùn)行路徑;
set environment varname [=value] 用于設(shè)置環(huán)境變量,如 set env USER=baohua;
show environment [varname] 則用于查看環(huán)境變量。
工作目錄
cd <dir> 相當(dāng)于shell的cd命令;
pwd 顯示當(dāng)前所在的目錄。
程序的輸入輸出
info terminal 用于顯示程序用到的終端的模式;
gdb中也可以使用重定向控制程序輸出,如 run > outfile ;
tty 命令可以指定輸入輸出的終端設(shè)備,如: tty /dev/ttyS1 。
3、break命令
在gdb中用break命令來設(shè)置斷點(diǎn),設(shè)置斷點(diǎn)的方法包括:
break <function>
在進(jìn)入指定函數(shù)時停住,C++中可以使用class::function或function(type, type)格式來指定函數(shù)名。
break <linenum>
在指定行號停住。
break +offset / break -offset
在當(dāng)前行號的前面或后面的offset行停住,offiset為自然數(shù)。
break filename:linenum
在源文件filename的linenum行處停住。
break filename:function
在源文件filename的function函數(shù)的入口處停住。
break *address
在程序運(yùn)行的內(nèi)存地址處停住。
break
break命令沒有參數(shù)時,表示在下一條指令處停住。
break ... if <condition>
“...”可以是上述的
break <linenum>
、
break +offset / break –offset
中的參數(shù),condition表示條件,在條件成立時停住。比如在循環(huán)體中,可以設(shè)置break if i=100,表示當(dāng)i為100時停住程序。
info
查看斷點(diǎn)時,可使用info命令,如 info breakpoints [n] 、 info break [n] (n表示斷點(diǎn)號)。
4、單步命令
在調(diào)試過程中,next命令用于單步執(zhí)行,類似VC++中的step over。next的單步不會進(jìn)入函數(shù)的內(nèi)部,與next對應(yīng)的step(縮寫s)命令則在單步執(zhí)行一個函數(shù)時,會進(jìn)入其內(nèi)部,類似VC++中的step into。下面演示了step命令的執(zhí)行情況,在23行的add()函數(shù)調(diào)用處執(zhí)行step會進(jìn)入其內(nèi)部的“return a+b;”語句:
(gdb) break 25 Breakpoint 1 at 0x8048362 : file gdb_example.c, line 25 . (gdb) run Starting program: /driver_study/ gdb_example Breakpoint 1 , main () at gdb_example.c: 25 25 sum [i] = add(array1[i], array2[i]); (gdb) step add (a = 48 , b= 85 ) at gdb_example.c: 3 3 return a + b;
單步執(zhí)行的更復(fù)雜用法包括:
step <count>
單步跟蹤,如果有函數(shù)調(diào)用,則進(jìn)入該函數(shù)(進(jìn)入函數(shù)的前提是,此函數(shù)被編譯有debug信息)。step后面不加count表示一條條地執(zhí)行,加表示執(zhí)行后面的count條指令,然后再停住。
next <count>
單步跟蹤,如果有函數(shù)調(diào)用,它不會進(jìn)入該函數(shù)。同樣地,next后面不加count表示一條條地執(zhí)行,加表示執(zhí)行后面的count條指令,然后再停住。
set step-mode
set step-mode on用于打開step-mode模式,這樣,在進(jìn)行單步跟蹤時,程序不會因?yàn)闆]有debug信息而不停住,這個參數(shù)的設(shè)置可便于查看機(jī)器碼。set step-mod off用于關(guān)閉step-mode模式。
finish
運(yùn)行程序,直到當(dāng)前函數(shù)完成返回,并打印函數(shù)返回時的堆棧地址和返回值及參數(shù)值等信息。
until (縮寫u)
一直在循環(huán)體內(nèi)執(zhí)行單步,退不出來是一件令人煩惱的事情,until命令可以運(yùn)行程序直到退出循環(huán)體。
stepi(縮寫si)和nexti(縮寫ni)
stepi和nexti用于單步跟蹤一條機(jī)器指令,一條程序代碼有可能由數(shù)條機(jī)器指令完成,stepi和nexti可以單步執(zhí)行機(jī)器指令。 另外,運(yùn)行“display/i $pc”命令后,單步跟蹤會在打出程序代碼的同時打出機(jī)器指令,即匯編代碼。
5、continue命令
當(dāng)程序被停住后,可以使用continue命令(縮寫c,fg命令同continue命令)恢復(fù)程序的運(yùn)行直到程序結(jié)束,或到達(dá)下一個斷點(diǎn),命令格式為:
continue [ignore- count] c [ignore - count] fg [ignore -count]
ignore-count表示忽略其后多少次斷點(diǎn)。 假設(shè)我們設(shè)置了函數(shù)斷點(diǎn)
add()
,并
watch i
,則在continue過程中,每次遇到
add()
函數(shù)或i發(fā)生變化,程序就會停住,如:
(gdb) continue Continuing. Hardware watchpoint 3 : i Old value = 2 New value = 3 0x0804838d in main () at gdb_example.c: 23 23 for (i = 0 ; i < 10 ; i++ ) (gdb) continue Continuing. Breakpoint 1 , main () at gdb_example.c: 25 25 sum [i] = add(array1[i], array2[i]); (gdb) continue Continuing. Hardware watchpoint 3 : i Old value = 3 New value = 4 0x0804838d in main () at gdb_example.c: 23 23 for (i = 0 ; i < 10 ; i++)
6、print命令
在調(diào)試程序時,當(dāng)程序被停住時,可以使用print命令(縮寫為p),或是同義命令inspect來查看當(dāng)前程序的運(yùn)行數(shù)據(jù)。print命令的格式是:
print < expr > print /<f> < expr >
<expr> 是表達(dá)式,是被調(diào)試的程序中的表達(dá)式,
<f> 是輸出的格式,比如,如果要把表達(dá)式按16進(jìn)制的格式輸出,那么就是 /x 。在表達(dá)式中,有幾種GDB所支持的操作符,它們可以用在任何一種語言中, “@” 是一個和數(shù)組有關(guān)的操作符, “::” 指定一個在文件或是函數(shù)中的變量, “{<type>} <addr>” 表示一個指向內(nèi)存地址 <addr> 的類型為type的一個對象。
下面演示了查看
sum[]
數(shù)組的值的過程:
(gdb) print sum $ 2 = { 133 , 155 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } (gdb) next Breakpoint 1 , main () at gdb_example.c: 25 25 sum [i] = add(array1[i], array2[i]); (gdb) next 23 for (i = 0 ; i < 10 ; i++ ) (gdb) print sum $ 3 = { 133 , 155 , 143 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }
當(dāng)需要查看一段連續(xù)內(nèi)存空間的值的時間,可以使用GDB的 “@” 操作符, “@” 的左邊是第一個內(nèi)存地址, “@” 的右邊則是想查看內(nèi)存的長度。例如如下動態(tài)申請的內(nèi)存:
int *array = ( int *) malloc (len * sizeof ( int ));
在GDB調(diào)試過程中這樣顯示出這個動態(tài)數(shù)組的值:
p *array@len
print的輸出格式包括:
- x 按十六進(jìn)制格式顯示變量。
- d 按十進(jìn)制格式顯示變量。
- u 按十六進(jìn)制格式顯示無符號整型。
- o 按八進(jìn)制格式顯示變量。
- t 按二進(jìn)制格式顯示變量。
- a 按十六進(jìn)制格式顯示變量。
- c 按字符格式顯示變量。
- f 按浮點(diǎn)數(shù)格式顯示變量。
我們可用display命令設(shè)置一些自動顯示的變量,當(dāng)程序停住時,或是單步跟蹤時,這些變量會自動顯示。 如果要修改變量,如x的值,可使用如下命令:
print x=
4
當(dāng)用GDB的print查看程序運(yùn)行時的數(shù)據(jù)時,每一個print都會被GDB記錄下來。GDB會以 $1,$2,$3 … 這樣的方式為每一個print命令編號。我們可以使用這個編號訪問以前的表達(dá)式,如 $1 。
7、watch命令
watch一般來觀察某個表達(dá)式(變量也是一種表達(dá)式)的值是否有變化了,如果有變化,馬上停住程序。
我們有下面的幾種方法來設(shè)置觀察點(diǎn):
watch <expr> :為表達(dá)式(變量)expr設(shè)置一個觀察點(diǎn)。一旦表達(dá)式值有變化時,馬上停住程序。
rwatch <expr> :當(dāng)表達(dá)式(變量)expr被讀時,停住程序。
awatch <expr> :當(dāng)表達(dá)式(變量)的值被讀或被寫時,停住程序。
info watchpoints :列出當(dāng)前所設(shè)置了的所有觀察點(diǎn)。 下面演示了觀察i并在連續(xù)運(yùn)行next時一旦發(fā)現(xiàn)i變化,i值就會顯示出來的過程:
(gdb) watch i Hardware watchpoint 3 : i (gdb) next 23 for (i = 0 ; i < 10 ; i++ ) (gdb) next Hardware watchpoint 3 : i Old value = 0 New value = 1 0x0804838d in main () at gdb_example.c: 23 23 for (i = 0 ; i < 10 ; i++ ) (gdb) next Breakpoint 1 , main () at gdb_example.c: 25 25 sum [i] = add(array1[i], array2[i]); (gdb) next 23 for (i = 0 ; i < 10 ; i++ ) (gdb) next Hardware watchpoint 3 : i Old value = 1 New value = 2 0x0804838d in main () at gdb_example.c: 23 23 for (i = 0 ; i < 10 ; i++)
8、examine命令
我們可以使用examine命令(縮寫為x)來查看內(nèi)存地址中的值。examine命令的語法如下所示:
x/<n/f/u> <addr>
<addr>
表示一個內(nèi)存地址。
“x/”
后的n、f、u都是可選的參數(shù),n 是一個正整數(shù),表示顯示內(nèi)存的長度,也就是說從當(dāng)前地址向后顯示幾個地址的內(nèi)容;f 表示顯示的格式,如果地址所指的是字符串,那么格式可以是s,如果地址是指令地址,那么格式可以是i;u 表示從當(dāng)前地址往后請求的字節(jié)數(shù),如果不指定的話,GDB默認(rèn)是4字節(jié)。u參數(shù)可以被一些字符代替:b表示單字節(jié),h表示雙字節(jié),w表示四字節(jié),g表示八 字節(jié)。當(dāng)我們指定了字節(jié)長度后,GDB會從指定的內(nèi)存地址開始,讀寫指定字節(jié),并把其當(dāng)作一個值取出來。n、f、u這3個參數(shù)可以一起使用,例如命令
“x/3uh 0x54320”
表示從內(nèi)存地址0x54320開始以雙字節(jié)為1個單位(h)、16進(jìn)制方式(u)顯示3個單位(3)的內(nèi)存。 ==
譬如下面的例子:
main() { char *c = " hello world " ; printf( " %s\n " , c); }
我們在
char *c = " hello world " ;
下一行設(shè)置斷點(diǎn)后:
可以通過多種方式看C指向的字符串:
方法1:
(gdb) p c $ 1 = 0x100000f2e " hello world "
方法2:
(gdb) x/s 0x100000f2e 0x100000f2e : " hello world "
方法3:
(gdb) p ( char *) 0x100000f2e $ 3 = 0x100000f2e " hello world "
將第一個字符改為大寫:
(gdb) p *( char *) 0x100000f2e = ' H ' $ 4 = 72 ' H '
再看看C:
(gdb) p c $ 5 = 0x100000f2e " Hello world "
9、set命令
修改寄存器:
(gdb) set $v0 = 0x004000000 (gdb) set $epc = 0xbfc00000
修改內(nèi)存:
(gdb) set {unsigned int } 0x8048a51 = 0x0
譬如對于第8節(jié)的例子:
(gdb) set {unsigned int } 0x100000f2e = 0x0 (gdb) x /10cb 0x100000f2e 0x100000f2e : 0 ' \0 ' 0 ' \0 ' 0 ' \0 ' 0 ' \0 ' 111 ' o ' 32 ' ' 119 ' w ' 111 ' o ' 0x100000f36 : 114 ' r ' 108 ' l ' (gdb) p c $ 10 = 0x100000f2e ""
10、jump命令
一般來說,被調(diào)試程序會按照程序代碼的運(yùn)行順序依次執(zhí)行,但是GDB也提供了亂序執(zhí)行的功能,也就是說,GDB可以修改程序的執(zhí)行順序,從而讓程序隨意跳躍。這個功能可以由GDB的jump命令: jump <linespec> 來指定下一條語句的運(yùn)行點(diǎn)。 <linespec> 可以是文件的行號,可以是 file:line 格式,也可以是 +num 這種偏移量格式,表示下一條運(yùn)行語句從哪里開始。 jump <address> 這里的 <address> 是代碼行的內(nèi)存地址。 注意,jump命令不會改變當(dāng)前的程序棧中的內(nèi)容,所以,如果使用jump從一個函數(shù)跳轉(zhuǎn)到另一個函數(shù),當(dāng)跳轉(zhuǎn)到的函數(shù)運(yùn)行完返回,進(jìn)行出棧操作時必然會發(fā)生錯誤,這可能導(dǎo)致意想不到的結(jié)果,所以最好只用jump在同一個函數(shù)中進(jìn)行跳轉(zhuǎn)。
11、signal命令
使用singal命令,可以產(chǎn)生一個信號量給被調(diào)試的程序,如中斷信號 “Ctrl+C” 。這非常方便于程序的調(diào)試,可以在程序運(yùn)行的任意位置設(shè)置斷點(diǎn),并在該斷點(diǎn)用GDB產(chǎn)生一個信號量,這種精確地在某處產(chǎn)生信號的方法非常有利于程序的調(diào)試。 signal命令的語法是: signal <signal> ,UNIX的系統(tǒng)信號量通常從1到15,所以 <signal> 取值也在這個范圍。
12、return命令
如果在函數(shù)中設(shè)置了調(diào)試斷點(diǎn),在斷點(diǎn)后還有語句沒有執(zhí)行完,這時候我們可以使用return命令強(qiáng)制函數(shù)忽略還沒有執(zhí)行的語句并返回。
上述return命令用于取消當(dāng)前函數(shù)的執(zhí)行,并立即返回,如果指定了 <expression> ,那么該表達(dá)式的值會被作為函數(shù)的返回值。
13、call命令
call命令用于強(qiáng)制調(diào)用某函數(shù): call <expr> 表達(dá)式中可以一是函數(shù),以此達(dá)到強(qiáng)制調(diào)用函數(shù)的目的,它會顯示函數(shù)的返回值(如果函數(shù)返回值不是void)。 其實(shí),前面介紹的print命令也可以完成強(qiáng)制調(diào)用函數(shù)的功能。
14、info命令
info命令可以在調(diào)試時用來查看寄存器、斷點(diǎn)、觀察點(diǎn)和信號等信息。
要查看寄存器的值,可以使用如下命令:
info registers (查看除了浮點(diǎn)寄存器以外的寄存器)
info all-registers (查看所有寄存器,包括浮點(diǎn)寄存器)
info registers <regname ...> (查看所指定的寄存器)
info break 查看斷點(diǎn)信息
info watchpoints 列出當(dāng)前所設(shè)置的所有觀察點(diǎn),
i nfo signals info handle 查看有哪些信號正在被GDB檢測,
info line命令來查看源代碼在內(nèi)存中的地址。
info threads可以看多線程。
info line后面可以跟行號、函數(shù)名、文件名:行號、文件名:函數(shù)名等多種形式,例如下面的命令會打印出所指定的源碼在運(yùn)行時的內(nèi)存地址:
info
line tst.c:func
15、set scheduler-locking off|on|step
off 不鎖定任何線程,也就是所有線程都執(zhí)行,這是默認(rèn)值。
on 只有當(dāng)前被調(diào)試程序會執(zhí)行。
step 在單步的時候,除了next過一個函數(shù)的情況以外,只有當(dāng)前線程會執(zhí)行。
與多線程調(diào)試相關(guān)的命令還包括:
thread ID
切換當(dāng)前調(diào)試的線程為指定ID的線程。?
break thread_test.c:123 thread all
在所有線程中相應(yīng)的行上設(shè)置斷點(diǎn)
thread apply ID1 ID2 command
讓一個或者多個線程執(zhí)行GDB命令command。
thread apply all command
讓所有被調(diào)試線程執(zhí)行GDB命令command。
16、disassemble
disassemble命令用于反匯編,它可被用來查看當(dāng)前執(zhí)行時的源代碼的機(jī)器碼,其實(shí)際上只是把目前內(nèi)存中的指令dump出來。下面的示例用于查看函數(shù)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) ... End of assembler dump.
?本文轉(zhuǎn)載自: http://blog.csdn.net/21cnbao/article/details/7385161
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
