GDB是GNU開源組織發(fā)布的一個(gè)強(qiáng)大的UNIX下的程序調(diào)試工具。它是一種強(qiáng)大的命令行調(diào)試工具。
一般來(lái)說(shuō),調(diào)試器的功能:
能夠運(yùn)行程序,設(shè)置所有能影響程序運(yùn)行的參數(shù);
能夠讓程序在指定條件下停止運(yùn)行;
能夠在程序停止時(shí)檢查所有參數(shù)的情況;
能夠根據(jù)指定條件改變程序的運(yùn)行。
gdb調(diào)試源代碼流程:
1)進(jìn)入GDB #gdb test
只需輸入GDB和要調(diào)試的可執(zhí)行文件即可,在GDB的啟動(dòng)畫面中指出了GDB的版本號(hào)、遵循的許可等信息,接下來(lái)就進(jìn)入了由"(gdb)"開頭的命令行界面了;
2)查看文件 (gdb) l
在GDB中鍵入"l"(list)就可以查看程序的源碼了,GDB列出的源代碼中明確地給出了對(duì)應(yīng)的行號(hào),方便代碼的定位;
3)設(shè)置斷點(diǎn) (gdb) b 6
只需在"b"(break)后加入對(duì)應(yīng)的行號(hào)即可,在GDB中利用行號(hào)設(shè)置斷點(diǎn)是指代碼運(yùn)行到對(duì)應(yīng)行之前暫停;設(shè)置斷點(diǎn)可以使程序執(zhí)行到某個(gè)位置時(shí)暫停,程序員在該位置處可以方便地查看變量的值、堆棧情況等;
一般情況下,源代碼中大家行號(hào)與用戶書寫程序的行號(hào)是一致的,但有時(shí)由于用戶的某些編譯選項(xiàng)會(huì)導(dǎo)致行號(hào)不一致的情況,因此,一定要查看在GDB中的行號(hào);
4)查看斷點(diǎn)處情況 (gdb) info b
可以鍵入"info b"來(lái)查看斷點(diǎn)處情況,可以設(shè)置多個(gè)斷點(diǎn);
5)運(yùn)行代碼 (gdb) r
GDB默認(rèn)從首行開始運(yùn)行代碼,鍵入"r"(run)即可;程序運(yùn)行到斷點(diǎn)處停止。
6)看變量值 (gdb) p n
在程序暫停之后,程序員可以查看斷點(diǎn)處的相關(guān)變量值,在GDB中只需鍵入"p 變量名"(print)即可;
GDB在顯示變量值時(shí)都會(huì)在對(duì)應(yīng)值之前加上"$N"標(biāo)記,它是當(dāng)前變量值的引用標(biāo)記,以后若想再次引用此變量,就可以直接寫作"$N",而無(wú)需寫冗長(zhǎng)的變量名;
7)觀察變量 (gdb) watch n
在某一循環(huán)處,往往希望能夠觀察一個(gè)變量的變化情況,這時(shí)就可以鍵入命令"watch"來(lái)觀察變量的變化情況,GDB在"n"設(shè)置了觀察點(diǎn);
8)單步運(yùn)行 (gdb) n
單步運(yùn)行是指一次只運(yùn)行一條語(yǔ)句,這樣可以方便查看程序運(yùn)行的結(jié)果,在此處只需鍵入"n"(next)即可;隨著程序的單步運(yùn)行,當(dāng)變量n的值發(fā)生變化時(shí),GDB就會(huì)自動(dòng)顯示出n的變化情況。
9)程序繼續(xù)運(yùn)行 (gdb) c
命令"c"(continue)可以使程序繼續(xù)往下運(yùn)行,直到再次遇到斷點(diǎn)或程序結(jié)束;
10)退出GDB (gdb) q
只需使用指令"q"(quit)即可。
設(shè)置/刪除斷點(diǎn)
命令格式 例子 作用
break + 設(shè)置斷點(diǎn)的行號(hào) beak n 用于在程序中對(duì)應(yīng)行設(shè)置斷點(diǎn)
tbreak + 行號(hào)或函數(shù)名 tbreak n/func 設(shè)置臨時(shí)斷點(diǎn),到達(dá)后被自動(dòng)刪除
break + filename + 行號(hào) break main.c:10 用于在指定文件對(duì)應(yīng)行設(shè)置斷點(diǎn)
break + <0x...> break 0x3400a 用于在內(nèi)存某一位置處暫停
break + 行號(hào) + if + 條件 break 10 if i==3 用于設(shè)置條件斷點(diǎn),在循環(huán)中使用非常方便
info breakpoints/watchpoints [n] info break n表示斷點(diǎn)號(hào),查看斷點(diǎn)/觀察點(diǎn)的情況
clear + 要清除的斷點(diǎn)行號(hào) clear 10 用于清除對(duì)應(yīng)行的斷點(diǎn),要給出斷點(diǎn)的行號(hào),清除時(shí)GDB會(huì)給出提示
delete + 要清除的斷點(diǎn)編號(hào) delete 3 用于清除斷點(diǎn)和自動(dòng)顯示的表達(dá)式的命令,要給出斷點(diǎn)的編號(hào),清除時(shí)GDB不會(huì)給出任何提示
disable/enable + 斷點(diǎn)編號(hào) disable 3 讓所設(shè)斷點(diǎn)暫時(shí)失效/使能,如果要讓多個(gè)編號(hào)處的斷點(diǎn)失效/使能,可將編號(hào)之間用空格隔開
awatch/watch + 變量 awatch/watch i 設(shè)置一個(gè)觀察點(diǎn),當(dāng)變量被讀出或?qū)懭霑r(shí)程序被暫停
rwatch + 變量 rwatch i 設(shè)置一個(gè)觀察點(diǎn),當(dāng)變量被讀出時(shí),程序被暫停
catch 設(shè)置捕捉點(diǎn)來(lái)補(bǔ)捉程序運(yùn)行時(shí)的一些事件。如:載入共享庫(kù)(動(dòng)態(tài)鏈接庫(kù))或是C++的異常
tcatch 只設(shè)置一次捕捉點(diǎn),當(dāng)程序停住以后,應(yīng)點(diǎn)被自動(dòng)刪除
斷點(diǎn)與觀察點(diǎn)的區(qū)別:
1.所有使用與breakpoint的操作都適用于watchpoint
2.斷點(diǎn)是CPU到某一地址取指令時(shí)中斷,觀察點(diǎn)是CPU到某一地址讀寫數(shù)據(jù)時(shí)中斷。
在多線程的程序中,觀察點(diǎn)的作用很有限,GDB只能觀察一個(gè)線程中的表達(dá)式的值,如果用戶確信表達(dá)式只被當(dāng)前線程所存取,那么使用觀察點(diǎn)才有效,GDB無(wú)法檢測(cè)一個(gè)非當(dāng)前線程對(duì)表達(dá)式值的改變。
數(shù)據(jù)相關(guān)命令
display +表達(dá)式 display a 用于顯示表達(dá)式的值,每當(dāng)程序運(yùn)行到斷點(diǎn)處都會(huì)顯示表達(dá)式的值
info display 用于顯示當(dāng)前所有要顯示值的表達(dá)式的情況
delete + display 編號(hào) delete 3 用于刪除一個(gè)要顯示值的表達(dá)式,被刪除的表達(dá)式將不被顯示
disable/enable + display 編號(hào) disable/enable 3 使一個(gè)要顯示值的表達(dá)式暫時(shí)失效/使能
undisplay + display 編號(hào) undisplay 3 用于結(jié)束某個(gè)表達(dá)式值的顯示
whatis + 變量 whatis i 顯示某個(gè)表達(dá)式的數(shù)據(jù)類型
print(p) + 變量/表達(dá)式 p n 用于打印變量或表達(dá)式的值
set + 變量 = 變量值 set i = 3 改變程序中某個(gè)變量的值
在使用print命令時(shí),可以對(duì)變量按指定格式進(jìn)行輸出,其命令格式為print /變量名 + 格式
其中常用的變量格式:x:十六進(jìn)制;d:十進(jìn)制;u:無(wú)符號(hào)數(shù);o:八進(jìn)制;c:字符格式;f:浮點(diǎn)數(shù)。
調(diào)試運(yùn)行環(huán)境相關(guān)命令
set args set args arg1 arg2 設(shè)置運(yùn)行參數(shù)
show args show args 參看運(yùn)行參數(shù)
set width + 數(shù)目 set width 70 設(shè)置GDB的行寬
cd + 工作目錄 cd ../ 切換工作目錄
run r/run 程序開始執(zhí)行
step(s) s 進(jìn)入式(會(huì)進(jìn)入到所調(diào)用的子函數(shù)中)單步執(zhí)行,進(jìn)入函數(shù)的前提是,此函數(shù)被編譯有debug信息
next(n) n 非進(jìn)入式(不會(huì)進(jìn)入到所調(diào)用的子函數(shù)中)單步執(zhí)行
finish finish 一直運(yùn)行到函數(shù)返回并打印函數(shù)返回時(shí)的堆棧地址和返回值及參數(shù)值等信息
until + 行數(shù) u 3 運(yùn)行到函數(shù)某一行
continue(c) c 執(zhí)行到下一個(gè)斷點(diǎn)或程序結(jié)束
return <返回值> return 5 改變程序流程,直接結(jié)束當(dāng)前函數(shù),并將指定值返回
call + 函數(shù) call func 在當(dāng)前位置執(zhí)行所要運(yùn)行的函數(shù)
堆棧相關(guān)命令
backtrace/bt bt 用來(lái)打印棧幀指針,也可以在該命令后加上要打印的棧幀指針的個(gè)數(shù),查看程序執(zhí)行到此時(shí),是經(jīng)過哪些函數(shù)呼叫的程序,程序“調(diào)用堆棧”是當(dāng)前函數(shù)之前的所有已調(diào)用函數(shù)的列表(包括當(dāng)前函數(shù))。每個(gè)函數(shù)及其變量都被分配了一個(gè)“幀”,最近調(diào)用的函數(shù)在 0 號(hào)幀中(“底部”幀)
frame frame 1 用于打印指定棧幀
info reg info reg 查看寄存器使用情況
info stack info stack 查看堆棧使用情況
up/down up/down 跳到上一層/下一層函數(shù)
跳轉(zhuǎn)執(zhí)行
一般來(lái)說(shuō),被調(diào)試程序會(huì)按照程序代碼的運(yùn)行順序依次執(zhí)行。GDB提供了亂序執(zhí)行的功能,
也就是說(shuō),GDB可以修改程序的執(zhí)行順序,可以讓程序執(zhí)行隨意跳躍。這個(gè)功能可以由GDB
的jump命令來(lái)完:
jump
指定下一條語(yǔ)句的運(yùn)行點(diǎn)?梢允俏募男刑(hào),可以是file:line格式,可以是+num這種偏
移量格式。表式著下一條運(yùn)行語(yǔ)句從哪里開始。
jump
這里的
是代碼行的內(nèi)存地址。
注意,jump命令不會(huì)改變當(dāng)前的程序棧中的內(nèi)容,所以,當(dāng)你從一個(gè)函數(shù)跳到另一個(gè)函數(shù)
時(shí),當(dāng)函數(shù)運(yùn)行完返回時(shí)進(jìn)行彈棧操作時(shí)必然會(huì)發(fā)生錯(cuò)誤,可能結(jié)果還是非常奇怪的,甚至
于產(chǎn)生程序Core Dump。所以最好是同一個(gè)函數(shù)中進(jìn)行跳轉(zhuǎn)。
熟悉匯編的人都知道,程序運(yùn)行時(shí),有一個(gè)寄存器用于保存當(dāng)前代碼所在的內(nèi)存地址。所以,
jump命令也就是改變了這個(gè)寄存器中的值。于是,你可以使用“set $pc”來(lái)更改跳轉(zhuǎn)執(zhí)行
的地址。如:
set $pc = 0x485
signal命令
信號(hào)是一種軟中斷,是一種處理異步事件的方法,使用singal命令,可以產(chǎn)生一個(gè)信號(hào)量給被調(diào)試程序,如中斷信號(hào)"Ctrl+C"。這非常方便于程序的調(diào)試,可以在程序運(yùn)行的任意位置設(shè)置斷點(diǎn),并在該斷點(diǎn)用GDB產(chǎn)生一個(gè)信號(hào)量,這種精確地在某處產(chǎn)生信號(hào)的方法非常有利于程序的調(diào)試,signal命令語(yǔ)法是:signal <signal>,UNIX的系統(tǒng)信號(hào)量通常從1到15,所以<signal>取值也在這個(gè)范圍。
handle
在GDB中定義一個(gè)信號(hào)處理。信號(hào)可以以SIG開頭或不以SIG開頭,可以用定義一個(gè)要處理信號(hào)的范圍(如:SIGIO-SIGKILL,表示處理從SIGIO信號(hào)到SIGKILL的信號(hào),其中包括SIGIO,SIGIOT,SIGKILL三個(gè)信號(hào)),也可以使用關(guān)鍵字all來(lái)標(biāo)明要處理所有的信號(hào)。一旦被調(diào)試的程序接收到信號(hào),運(yùn)行程序馬上會(huì)被GDB停住,以供調(diào)試。其可以是以下幾種關(guān)鍵字的一個(gè)或多個(gè)。
nostop/stop
當(dāng)被調(diào)試的程序收到信號(hào)時(shí),GDB不會(huì)停住程序的運(yùn)行,但會(huì)打出消息告訴你收到這種信號(hào)/GDB會(huì)停住你的程序
print/noprint
當(dāng)被調(diào)試的程序收到信號(hào)時(shí),GDB會(huì)顯示出一條信息/GDB不會(huì)告訴你收到信號(hào)的信息
pass
noignore
當(dāng)被調(diào)試的程序收到信號(hào)時(shí),GDB不處理信號(hào)。這表示,GDB會(huì)把這個(gè)信號(hào)交給被調(diào)試程序會(huì)處理。
nopass
ignore
當(dāng)被調(diào)試的程序收到信號(hào)時(shí),GDB不會(huì)讓被調(diào)試程序來(lái)處理這個(gè)信號(hào)。
info signals
info handle
可以查看哪些信號(hào)被GDB處理,并且可以看到缺省的處理方式
single命令和shell的kill命令不同,系統(tǒng)的kill命令發(fā)信號(hào)給被調(diào)試程序時(shí),是由GDB截獲的,而single命令所發(fā)出一信號(hào)則是直接發(fā)給被調(diào)試程序的。
GDB中運(yùn)行UNIX的shell程序
在gdb環(huán)境中,你可以執(zhí)行UNIX的shell的命令,使用gdb的shell命令來(lái)完成: shell
調(diào)用UNIX的shell來(lái)執(zhí)行,環(huán)境變量SHELL中定義的UNIX的shell將會(huì)被執(zhí)行,如果SHELL沒有定義,那就使用UNIX的標(biāo)準(zhǔn)shell:/bin/sh。(在Windows中使用Command.com或cmd.exe)
make
可以在gdb中執(zhí)行make命令來(lái)重新build自己的程序。這個(gè)命令等價(jià)于"shell make"
在GDB中運(yùn)行程序
當(dāng)以gdb 方式啟動(dòng)gdb后,gdb會(huì)在PATH路徑和當(dāng)前目錄中搜索的源文件。如要確認(rèn)gdb是否讀到源文件,可使用l或list命令,看看gdb是否能列出源代碼。
在gdb中,運(yùn)行程序使用r或是run命令。
1、程序運(yùn)行參數(shù)。
set args 可指定運(yùn)行時(shí)參數(shù)。(如:set args 10 20 30 40 50)
show args 命令可以查看設(shè)置好的運(yùn)行參數(shù)。
2、運(yùn)行環(huán)境。
path
可設(shè)定程序的運(yùn)行路徑。
show paths 查看程序的運(yùn)行路徑。
set environment varname [=value] 設(shè)置環(huán)境變量。如:set env USER=hchen
show environment [varname] 查看環(huán)境變量。
3、工作目錄。
cd
相當(dāng)于shell的cd命令。
pwd 顯示當(dāng)前的所在目錄。
4、程序的輸入輸出。
info terminal 顯示你程序用到的終端的模式。
使用重定向控制程序輸出。如:run > outfile
tty命令可以指寫輸入輸出的終端設(shè)備。如:tty /dev/ttyb
調(diào)試已運(yùn)行的程序
兩種方法:
1、在UNIX下用ps查看正在運(yùn)行的程序的PID(進(jìn)程ID),然后用gdb PID格式掛接正在運(yùn)行的程序。
2、先用gdb 關(guān)聯(lián)上源代碼,并進(jìn)行g(shù)db,在gdb中用attach命令來(lái)掛接進(jìn)程的PID。并用detach來(lái)取消掛接的進(jìn)程。
暫停 / 恢復(fù)程序運(yùn)行
當(dāng)進(jìn)程被gdb停住時(shí),你可以使用info program 來(lái)查看程序的是否在運(yùn)行,進(jìn)程號(hào),被暫停的原因。
在gdb中,我們可以有以下幾種暫停方式:斷點(diǎn)(BreakPoint)、觀察點(diǎn)(WatchPoint)、捕捉點(diǎn)(CatchPoint)、信號(hào)(Signals)、線程停止(Thread Stops),如果要恢復(fù)程序運(yùn)行,可以使用c或是continue命令。
線程(Thread Stops)
如果程序是多線程,可以定義斷點(diǎn)是否在所有的線程上,或是在某個(gè)特定的線程。
break thread
break thread if ...
linespec指定了斷點(diǎn)設(shè)置在的源程序的行號(hào)。threadno指定了線程的ID,注意,這個(gè)ID是GDB分配的,可以通過“info threads”命令來(lái)查看正在運(yùn)行程序中的線程信息。如果不指定thread 則表示斷點(diǎn)設(shè)在所有線程上面。還可以為某線程指定斷點(diǎn)條件。如:
(gdb) break frik.c:13 thread 28 if bartab > lim
當(dāng)你的程序被GDB停住時(shí),所有的運(yùn)行線程都會(huì)被停住。這方便查看運(yùn)行程序的總體情況。而在你恢復(fù)程序運(yùn)行時(shí),所有的線程也會(huì)被恢復(fù)運(yùn)行。
調(diào)試core文件
Linux環(huán)境下經(jīng)常遇到某個(gè)進(jìn)程掛掉而找不到原因,應(yīng)用程序崩潰,可以通過生成core file文件加上gdb來(lái)定位
Core Dump:Core的意思是內(nèi)存,Dump的意思是扔出來(lái),堆出來(lái)。開發(fā)和使用Unix程序時(shí),有時(shí)程序莫名其妙的down了,卻沒有任何的提示(有時(shí)候會(huì)提示core dumped),這時(shí)候可以查看一下有沒有形如core.進(jìn)程號(hào)的文件生成,這個(gè)文件便是操作系統(tǒng)把程序down掉時(shí)的內(nèi)存內(nèi)容扔出來(lái)生成的, 它可以做為調(diào)試程序的參考
生成Core文件
一般默認(rèn)情況下,core file的大小被設(shè)置為了0,這樣系統(tǒng)就不dump出core file了。
#設(shè)置core大小為無(wú)限
ulimit -c unlimited
#設(shè)置文件大小為無(wú)限
ulimit unlimited
這些需要有root權(quán)限, 在ubuntu下每次重新打開中斷都需要重新輸入上面的第一條命令, 來(lái)設(shè)置core大小為無(wú)限
core文件生成路徑:
輸入可執(zhí)行文件運(yùn)行命令的同一路徑下。
若系統(tǒng)生成的core文件不帶其他任何擴(kuò)展名稱,則全部命名為core。新的core文件生成將覆蓋原來(lái)的core文件。
1)/proc/sys/kernel/core_uses_pid可以控制core文件的文件名中是否添加pid作為擴(kuò)展。文件內(nèi)容為1,表示添加pid作為擴(kuò)展名,生成的core文件格式為core.xxxx;為0則表示生成的core文件同一命名為core。
可通過以下命令修改此文件:
echo "1" > /proc/sys/kernel/core_uses_pid
2)proc/sys/kernel/core_pattern可以控制core文件保存位置和文件名格式。
可通過以下命令修改此文件:
echo "/corefile/core-%e-%p-%t" > core_pattern,可以將core文件統(tǒng)一生成到/corefile目錄下,產(chǎn)生的文件名為core-命令名-pid-時(shí)間戳
以下是參數(shù)列表:
%p - insert pid into filename 添加pid
%u - insert current uid into filename 添加當(dāng)前uid
%g - insert current gid into filename 添加當(dāng)前gid
%s - insert signal that caused the coredump into the filename 添加導(dǎo)致產(chǎn)生core的信號(hào)
%t - insert UNIX time that the coredump occurred into filename 添加core文件生成時(shí)的unix時(shí)間
%h - insert hostname where the coredump happened into filename 添加主機(jī)名
%e - insert coredumping executable name into filename 添加命令名
用gdb查看core文件
下面我們可以在發(fā)生運(yùn)行時(shí)信號(hào)引起的錯(cuò)誤時(shí)發(fā)生core dump了.
發(fā)生core dump之后, 用gdb進(jìn)行查看core文件的內(nèi)容, 以定位文件中引發(fā)core dump的行.
gdb [exec file] [core file]
如:
gdb ./test core
或gdb ./a.out
core-file core.xxxx
gdb后, 用bt命令backtrace或where查看程序運(yùn)行到哪里, 來(lái)定位core dump的文件->行.
待調(diào)試的可執(zhí)行文件,在編譯的時(shí)候需要加-g,core文件才能正常顯示出錯(cuò)信息
1)gdb -core=core.xxxx
file ./a.out
bt
2)gdb -c core.xxxx
file ./a.out
bt
用gdb實(shí)時(shí)觀察某進(jìn)程crash信息
啟動(dòng)進(jìn)程
gdb -p PID
c
運(yùn)行進(jìn)程至crash
gdb會(huì)顯示crash信息
bt