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