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