電子產(chǎn)業(yè)一站式賦能平臺(tái)

PCB聯(lián)盟網(wǎng)

搜索
查看: 80|回復(fù): 0
收起左側(cè)

GDB動(dòng)態(tài)打印,不需重新編譯

[復(fù)制鏈接]

493

主題

493

帖子

3141

積分

四級(jí)會(huì)員

Rank: 4

積分
3141
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2024-11-5 11:38:00 | 只看該作者 |只看大圖 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
本系列專題,旨在介紹一些非常實(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則是各種日志打印方法中的典型代表。

調(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)試信息,還必須從程序中刪除掉。


    對(duì)于簡單的程序,這些尚可接受。但是,在大型項(xiàng)目中,單是編譯構(gòu)建過程可能就要幾十分鐘,甚至數(shù)個(gè)小時(shí),而部署過程則更為復(fù)雜。
    你能想象得出,在這樣的項(xiàng)目中一直重復(fù)這些過程,是一件多么痛苦的事情嗎?
    那么,有沒有一種方法,既不需要修改源碼,又能隨時(shí)在程序中任何地方任意添加打印信息呢?
    當(dāng)然有!GDB的動(dòng)態(tài)打印功能正式為此而生的!

    GDB Dynamic PrintfGDB提供了Dynamic Printf功能,下文我們稱之為動(dòng)態(tài)打印。利用這個(gè)功能,我們可以在不修改程序源碼的情況下,隨時(shí)在程序的任何地方添加格式化打印。
    如此一來,當(dāng)然也就不需要重新編譯和部署的過程了。
    我們先看一個(gè)簡單的示例,然后再詳細(xì)介紹它的實(shí)現(xiàn)原理,和相關(guān)命令的用法。
    示例一個(gè)簡單示例,如下圖所示:

    先編譯一下:
    gcc -g test.c -o test然后用GDB進(jìn)行調(diào)試:

    和期望的一樣,程序沒有任何打印輸出。
    現(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!
    "如下圖:

    稍微解釋一下:
    第6行的語句會(huì)打印一句“Hello, World!”
    第11行會(huì)把i、a、b的值分別打印出來
    第14行打印“Leaving! Bye bye!”
    設(shè)置好之后,查看一下斷點(diǎn)的信息:

    已經(jīng)設(shè)置成功了。然后,重新運(yùn)行:

    看到了吧,盡管我們并沒有對(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è)的代碼:

    到這里,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]

    我們看下一下test.bp中究竟保存了什么內(nèi)容:

    原來,就是我們之前執(zhí)行的三條dprintf命令,并且是以文本形式存在test.bp中的。
    接下來,我們用兩種方法分別加載test.bp中的斷點(diǎn)信息。
    用-x參數(shù)加載斷點(diǎn)信息

    可見,指定-x參數(shù)后,GDB在開始調(diào)試程序之前,會(huì)從指定的文件中把斷點(diǎn)信息加載進(jìn)來,并重新設(shè)置在程序中。因此,執(zhí)行run命令后,程序能夠按照我們的預(yù)期正常執(zhí)行動(dòng)態(tài)打印功能。
    source命令加載斷點(diǎn)信息

    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)擊閱讀原文,查看更多分享。
  • 發(fā)表回復(fù)

    本版積分規(guī)則


    聯(lián)系客服 關(guān)注微信 下載APP 返回頂部 返回列表