|
本系列專題,旨在介紹一些非常實(shí)用,卻不為大多數(shù)人所知的調(diào)試技巧。靈活運(yùn)用這些調(diào)試技巧,能夠輕松解決一些我們經(jīng)常遇到并為之困惑的問題,大大提高程序調(diào)試的效率。
感興趣的朋友,歡迎關(guān)注!
引言 - 程序調(diào)試的痛關(guān)于程序調(diào)試,有人喜歡各種調(diào)試工具,有人喜歡用簡單直接的log打印。兩種方法各有各的優(yōu)勢和不足,大多時(shí)候是可以互補(bǔ)的。
在Linux環(huán)境下,GDB是各種調(diào)試工具中的佼佼者,而printf則是各種日志打印方法中的典型代表。
wrlwbnvr3lx64095708117.jpg (102.86 KB, 下載次數(shù): 0)
下載附件
保存到相冊(cè)
wrlwbnvr3lx64095708117.jpg
2024-11-6 00:22 上傳
調(diào)試問題時(shí)候,你遇到過下面的情況嗎?
代碼添加打印信息進(jìn)行調(diào)試,突然發(fā)現(xiàn)添加打印的位置不對(duì),或者別的地方也需要添加打印信息。于是,重新修改源碼,重新添加打印,重新編譯,重新部署,重新運(yùn)行,重新調(diào)試,重新分析。當(dāng)我們費(fèi)了九牛二虎之力把這些都弄好之后,很不幸地又發(fā)現(xiàn)了新的問題,然后不得不反復(fù)進(jìn)行這些過程。而且,當(dāng)問題定位出來之后,我們之前花費(fèi)很大力氣添加的調(diào)試信息,還必須從程序中刪除掉。
paz2ubpoh4364095708217.jpg (71.79 KB, 下載次數(shù): 1)
下載附件
保存到相冊(cè)
paz2ubpoh4364095708217.jpg
2024-11-6 00:22 上傳
對(duì)于簡單的程序,這些尚可接受。但是,在大型項(xiàng)目中,單是編譯構(gòu)建過程可能就要幾十分鐘,甚至數(shù)個(gè)小時(shí),而部署過程則更為復(fù)雜。
你能想象得出,在這樣的項(xiàng)目中一直重復(fù)這些過程,是一件多么痛苦的事情嗎?
那么,有沒有一種方法,既不需要修改源碼,又能隨時(shí)在程序中任何地方任意添加打印信息呢?
當(dāng)然有!GDB的動(dòng)態(tài)打印功能正式為此而生的!
kfmewpywk2264095708317.jpg (61.25 KB, 下載次數(shù): 1)
下載附件
保存到相冊(cè)
kfmewpywk2264095708317.jpg
2024-11-6 00:22 上傳
GDB Dynamic PrintfGDB提供了Dynamic Printf功能,下文我們稱之為動(dòng)態(tài)打印。利用這個(gè)功能,我們可以在不修改程序源碼的情況下,隨時(shí)在程序的任何地方添加格式化打印。
如此一來,當(dāng)然也就不需要重新編譯和部署的過程了。
我們先看一個(gè)簡單的示例,然后再詳細(xì)介紹它的實(shí)現(xiàn)原理,和相關(guān)命令的用法。
示例一個(gè)簡單示例,如下圖所示:
kslmit40dcd64095708417.jpg (69.33 KB, 下載次數(shù): 1)
下載附件
保存到相冊(cè)
kslmit40dcd64095708417.jpg
2024-11-6 00:22 上傳
先編譯一下:
gcc -g test.c -o test然后用GDB進(jìn)行調(diào)試:
zfqfssnkrta64095708517.jpg (84.87 KB, 下載次數(shù): 1)
下載附件
保存到相冊(cè)
zfqfssnkrta64095708517.jpg
2024-11-6 00:22 上傳
和期望的一樣,程序沒有任何打印輸出。
現(xiàn)在,我們?cè)诘?行、第11行、第14行分別添加一個(gè)動(dòng)態(tài)打印斷點(diǎn),用下面的命令:
dprintf 6,"Hello, World!
"
dprintf 11,"i = %d, a = %d, b = %d
",i,a,b
dprintf 14,"Leaving! Bye bye!
"如下圖:
gcxaybhxznz64095708617.jpg (68.36 KB, 下載次數(shù): 0)
下載附件
保存到相冊(cè)
gcxaybhxznz64095708617.jpg
2024-11-6 00:22 上傳
稍微解釋一下:
第6行的語句會(huì)打印一句“Hello, World!”
第11行會(huì)把i、a、b的值分別打印出來
第14行打印“Leaving! Bye bye!”
設(shè)置好之后,查看一下斷點(diǎn)的信息:
zupp14bddgq64095708717.jpg (74.39 KB, 下載次數(shù): 0)
下載附件
保存到相冊(cè)
zupp14bddgq64095708717.jpg
2024-11-6 00:22 上傳
已經(jīng)設(shè)置成功了。然后,重新運(yùn)行:
v1yixvqqtzo64095708817.jpg (53.83 KB, 下載次數(shù): 0)
下載附件
保存到相冊(cè)
v1yixvqqtzo64095708817.jpg
2024-11-6 00:22 上傳
看到了吧,盡管我們并沒有對(duì)源碼做任何修改,且沒有重新編譯,但程序仍然按照我們的設(shè)置,打印出了我們想要的信息!
是不是很神奇呢?GDB的動(dòng)態(tài)打印功能究竟是如何工作的呢?
GDB 動(dòng)態(tài)打印實(shí)現(xiàn)原理在上面的示例中,在設(shè)置好動(dòng)態(tài)打印的信息之后,我們可以用info break命令查看所設(shè)置的信息。
可見,GDB的動(dòng)態(tài)打印,本質(zhì)上也是一種特殊的斷點(diǎn)。但是,它與一般的斷點(diǎn)又有所區(qū)別。
一般的斷點(diǎn)被觸發(fā)后,會(huì)中斷程序執(zhí)行,然后等待用戶操作,并且用戶必須輸入continue命令讓程序恢復(fù)執(zhí)行。
而動(dòng)態(tài)打印斷點(diǎn)被觸發(fā)后,程序也會(huì)暫時(shí)中斷執(zhí)行,但是不需要等待用戶響應(yīng),而是直接執(zhí)行用戶預(yù)設(shè)的格式化打印語句,并自動(dòng)恢復(fù)程序的執(zhí)行。
GDB動(dòng)態(tài)打印的使用方法設(shè)置動(dòng)態(tài)打印的命令是dprintf,格式如下:
dprintf location,format string,arg1,arg2,...dprintf命令和C語言中的printf的用法很相似,支持格式化打印。
相比printf函數(shù),dprintf命令多了一個(gè)location參數(shù),用于指定動(dòng)態(tài)打印被觸發(fā)的位置。
和break命令設(shè)置斷點(diǎn)時(shí)一樣,location可以是文件名:行號(hào)、函數(shù)名、或者具體的地址等。
除了location外,剩余的幾個(gè)參數(shù),就和printf()函數(shù)一致了。format指定字符串打印的格式,后面幾個(gè)參數(shù)指定打印的數(shù)據(jù)來源。
以上面示例中的命令為例:
dprintf 6,"Hello, World!
"
dprintf 11,"i = %d, a = %d, b = %d
",i,a,b
dprintf 14,"Leaving! Bye bye!
"在功能上等價(jià)于下圖中右側(cè)的代碼:
jie33jc23lr64095708917.jpg (76.33 KB, 下載次數(shù): 1)
下載附件
保存到相冊(cè)
jie33jc23lr64095708917.jpg
2024-11-6 00:22 上傳
到這里,GDB動(dòng)態(tài)打印的最基本功能就介紹完了。
斷點(diǎn)信息丟失怎么辦?在實(shí)際項(xiàng)目的調(diào)試過程中,難免會(huì)由于各種原因而必須要反復(fù)的調(diào)試才能定位出問題的原因,或者徹底理解程序的代碼邏輯。
然而,dprintf本質(zhì)上也是一種斷點(diǎn),因此,當(dāng)調(diào)試結(jié)束后,本次調(diào)試時(shí)設(shè)置的斷點(diǎn)信息就全部丟失了。如果要再次調(diào)試的話,就不得不重新設(shè)置一遍。
如果每次調(diào)試過程中,只需要設(shè)置一兩個(gè)動(dòng)態(tài)打印的話,那倒也簡單。
可是,如果需要設(shè)置十幾個(gè)甚至幾十個(gè)動(dòng)態(tài)打印呢?難道每次調(diào)試都要全部重新設(shè)置一遍嗎?想想都是一件比較麻煩的事情,對(duì)吧?
其實(shí),GDB也有對(duì)應(yīng)的處理方案,很簡單就可以解決!
保存和加載GDB斷點(diǎn)信息為了解決上面提到的問題,GDB很貼心地提供了對(duì)斷點(diǎn)信息保存和加載的功能。
GDB中,可以把當(dāng)前所設(shè)置的各種類型的斷點(diǎn)信息全部保存在一個(gè)腳本文件中。這其中當(dāng)然也包括dprintf設(shè)置的動(dòng)態(tài)打印信息。
只需要執(zhí)行下面的命令即可:
save breakpoints file_name這條命令會(huì)把當(dāng)前所有的斷點(diǎn)信息都保存在file_name指定的文件中。
等下次進(jìn)行調(diào)試時(shí),可以把file_name文件中的斷點(diǎn)信息重新加載起來。有兩種方法:
啟動(dòng)GDB時(shí)使用“-x file_name”參數(shù)。
在GDB中執(zhí)行source file_name命令。
[/ol]下面分別演示一下。
保存斷點(diǎn)信息
我們用GDB重新啟動(dòng)上面示例中的test程序:
用dprintf設(shè)置好斷點(diǎn)。
用info break命令查看一下斷點(diǎn)信息。
執(zhí)行“save breakpoints test.bp”命令,把斷點(diǎn)信息保存在test.bp文件中。
[/ol]
gt3rg4bkjdk64095709017.jpg (141.56 KB, 下載次數(shù): 0)
下載附件
保存到相冊(cè)
gt3rg4bkjdk64095709017.jpg
2024-11-6 00:22 上傳
我們看下一下test.bp中究竟保存了什么內(nèi)容:
tmtpasaclfx64095709117.jpg (93.41 KB, 下載次數(shù): 2)
下載附件
保存到相冊(cè)
tmtpasaclfx64095709117.jpg
2024-11-6 00:22 上傳
原來,就是我們之前執(zhí)行的三條dprintf命令,并且是以文本形式存在test.bp中的。
接下來,我們用兩種方法分別加載test.bp中的斷點(diǎn)信息。
用-x參數(shù)加載斷點(diǎn)信息
uw42bdi1znv64095709217.jpg (146.71 KB, 下載次數(shù): 0)
下載附件
保存到相冊(cè)
uw42bdi1znv64095709217.jpg
2024-11-6 00:22 上傳
可見,指定-x參數(shù)后,GDB在開始調(diào)試程序之前,會(huì)從指定的文件中把斷點(diǎn)信息加載進(jìn)來,并重新設(shè)置在程序中。因此,執(zhí)行run命令后,程序能夠按照我們的預(yù)期正常執(zhí)行動(dòng)態(tài)打印功能。
source命令加載斷點(diǎn)信息
3nrmykuuloc64095709317.jpg (152.57 KB, 下載次數(shù): 1)
下載附件
保存到相冊(cè)
3nrmykuuloc64095709317.jpg
2024-11-6 00:22 上傳
GDB把test加載起來之后,info break并沒有顯示出任何斷點(diǎn)信息。然后,我們執(zhí)行source test.bp命令,GDB會(huì)把斷點(diǎn)信息從test.bp加載進(jìn)來,并重新設(shè)置在test程序中。
結(jié)語由于篇幅所限,本文只是介紹了GDB動(dòng)態(tài)打印的基本功能和使用方法。其實(shí),它還有很多高階的用法和技巧,以后會(huì)再更新文章進(jìn)行講解。
程序調(diào)試是每個(gè)程序員必須要熟練掌握的基本技能,在整個(gè)計(jì)算機(jī)知識(shí)體系結(jié)構(gòu)中,它占據(jù)著非常重要的地位。
猜你喜歡:
WiFi6+藍(lán)牙+星閃,三合一開發(fā)板,真香!
Github上熱門 C 語言項(xiàng)目匯總!
嵌入式,可測試性軟件設(shè)計(jì)!
一些低功耗軟件設(shè)計(jì)的要點(diǎn)!
嵌入式 C 保護(hù)結(jié)構(gòu)體的方式
實(shí)用 | 10分鐘教你通過網(wǎng)頁點(diǎn)燈
談?wù)勄度胧杰浖募嫒菪裕?/strong>
點(diǎn)擊閱讀原文,查看更多分享。 |
|