九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
GDB高級技巧:邊Debug邊修復(fù)BUG,無需修改代碼,無需重新編譯

引言

程序調(diào)試時,你是否遇到過下面幾種情況:

1、經(jīng)過定位,終于找到了程序中的一個BUG,滿心歡喜地以為找到了root cause,便迫不及待地修改源碼,然后重新編譯,重新部署。但驗證時卻發(fā)現(xiàn),真正的問題并沒有解決,代碼中還隱藏著更多的問題。

2、調(diào)試時,我們找到代碼中一個可疑的地方,但是不能100%確定這真的就是個BUG。要想確定,只能修改源碼、重新編譯、重新部署,然后重新運行驗證。

3、已經(jīng)找到了root cause,但不確定解決方案是否能正常工作,為了驗證,不得不反復(fù)地修改代碼、編譯、部署。

對于大型項目,編譯過程可能需要幾十分鐘,甚至幾個小時,部署過程則更為復(fù)雜漫長!可想而知,如果調(diào)試過程中,不得不反復(fù)的修改源碼,然后重新編譯和部署,會是一項多么繁瑣和浪費時間的事情!

那么,有沒有一種更高效的調(diào)試手段,可以避免反復(fù)修改代碼和編譯呢?

當(dāng)然有!本文將介紹一種GDB調(diào)試技巧,可以一邊調(diào)試,一邊修復(fù)Bug,可以在不修改代碼、不重新編譯的前提下即可修復(fù)BUG,驗證我們的解決方案,大幅提高調(diào)試效率!

本文預(yù)期效果

如下圖,冒泡排序程序中,有三個BUG:

冒泡排序示例

圖中已經(jīng)把三個BUG都標(biāo)注了出來。正常編譯運行時,程序執(zhí)行結(jié)果如下:

程序執(zhí)行異常

不過是普通方式執(zhí)行,還是在GDB中執(zhí)行,程序都異常終止,無法得到正常結(jié)果。

但是,利用本文介紹的調(diào)試技巧,可以利用GDB給這個程序制作一個“熱補丁”,在不修改代碼、不重新編譯的前提下,解決掉程序中的三個BUG,讓程序正常執(zhí)行,并得到預(yù)期結(jié)果!

最終效果,如下圖所示:

打上“熱補丁”后,程序正常執(zhí)行

是不是很有趣呢?下面開始介紹!

GDB Breakpoint Command Lists

GDB支持?jǐn)帱c觸發(fā)后,自動執(zhí)行用戶預(yù)設(shè)的一組調(diào)試命令。使用方法:

commands [bp_id...] command-listend

其中:

  • commands是GDB內(nèi)置關(guān)鍵字

  • bp_id是斷點的ID,也就是info命令顯示出來的斷點Num,可以指定多個,也可以不指定。當(dāng)不指定時,默認(rèn)只對最近一次設(shè)置的那個斷點有效。

  • command-list是用戶預(yù)設(shè)的一組命令,當(dāng)bp_id指定的斷點被觸發(fā)時,GDB會自動執(zhí)行這些命令。

  • end表示結(jié)束。

這個功能適用于各種類型的斷點,如breakpoint、watchpoint、catchpoint等。

適用場景舉例

利用GDB breakpoint commands lists這個特性可以做很多有趣的事情,本文僅列舉其中的幾個。

1、隨時隨地printf,不需修改代碼和重新編譯

看過我之前文章的朋友,應(yīng)該還記得,我介紹過GDB的動態(tài)打印(Dynamic Printf)功能,可以用dprintf命令在代碼的任意地方添加動態(tài)打印斷點,并自動執(zhí)行格式化打印操作,從而無需修改代碼和重新編譯就可以在代碼中任意增加日志打印信息。

利用GDB breakpoint commands lists功能,可以實現(xiàn)一樣的功能,而且除了打印之外,還可以做其它更多的操作,比如dump內(nèi)存,dump寄存器等。

2、修改代碼執(zhí)行邏輯,避免修改代碼和重新編譯

在GDB中可以做很多有趣的事情,比如修改變量、修改寄存器、調(diào)用函數(shù)等,結(jié)合breakpoint command list功能,可以在調(diào)試的同時,修改程序執(zhí)行邏輯,給程序打上“熱補丁”。從而可以在調(diào)試過程中,快速修復(fù)Bug,避免重新修改代碼和重新編譯,大大提高程序調(diào)試的效率!

這是本文重點講解的場景,稍后會演示如何利用這個功能,在GDB調(diào)試的過程中修復(fù)掉上文冒泡排序程序中的三個Bug。

3、實現(xiàn)自動化調(diào)試,提高調(diào)試效率

這個功能,結(jié)合GDB支持的腳本功能,以及自定義命令功能,可以實現(xiàn)調(diào)試自動化。

這涉及到GDB的很多其它知識,篇幅有限,不再展開討論,以后更新專門文章講解!感興趣的童鞋,不妨右上角關(guān)注一下!

給冒泡排序打上“熱補丁”

現(xiàn)在,我們利用GDB breakpoint command lists功能,給文中的冒泡排序程序打上“熱補丁”,演示如何在不修改源碼、不重新編譯的前提下,解決掉程序中的3個BUG。

再看一下示例程序:

編譯一下:

gcc -g bubble.c -o bubble

先用GDB加載運行一下:

程序運行異常,符合我們的預(yù)期。

下面我們依次解決冒泡排序程序中的3個BUG。

1、解決第一個BUG

先解決第22行的BUG,也就是傳遞給了bubble_sort()錯誤的數(shù)組長度。

我們知道,在x64上,函數(shù)參數(shù)優(yōu)先采用寄存器傳遞。那么,我們有這么幾種方式可以選擇:

  1. 把斷點設(shè)置在bubble_sort()入口第一條指令,然后直接修改存放數(shù)組長度n的那個寄存器中的值。

  2. 把斷點設(shè)置在bubble_sort()入口處(不必是第一條指令),在第7行for循環(huán)之前,把存放數(shù)組長度的變量n的值改掉。

  3. 把斷點設(shè)置在main()函數(shù)第22行,也就是調(diào)用bubble_sort()的地方,然后以正確的參數(shù)手動調(diào)用bubble_sort()函數(shù),并利用GDB的jump命令,跳過第22行代碼的執(zhí)行。

考慮到有些童鞋對x64 CPU不是非常了解,或者對GDB的jump命令不熟悉,我們采用第2種方式。而且,這種方式也更簡單通用。

我們先給bubble_sort()函數(shù)設(shè)置斷點,然后利用commands命令預(yù)設(shè)一條命令,把變量n的值修改為10。命令如下:

b bubble_sortcommands 1 set var n=10end

設(shè)置完之后,用run命令開始運行程序。結(jié)果如下:

bubble_sort()處的斷點被觸發(fā)后,程序暫停,用print命令查看變量n的值,已經(jīng)被修改成了正確的值:10。

可見,我們的設(shè)置是有效的。

斷點觸發(fā)后,讓程序自動恢復(fù)執(zhí)行

那么,在bubble_sort()處斷點被觸發(fā),變量n的值被修改之后,如何讓程序自動恢復(fù)執(zhí)行呢?

很簡單,只需要在預(yù)設(shè)的命令中添加一個continue命令就可以了。為了證明我們的設(shè)置確實是生效的,我們在修改變量n的前后,各添加一個格式化打印語句,把變量n的值打印出來:

b bubble_sortcommands 1  printf 'The original value of n is %d\n',n  set var n=10  printf 'Current value of n is %d\n',n  continueend

結(jié)果如下圖:

解決第一個BUG

從運行結(jié)果可以看出,斷點被觸發(fā)后,我們預(yù)設(shè)的語句被正確執(zhí)行,變量n的值被修改為10,然后程序自動恢復(fù)執(zhí)行。

到此,第一個BUG已經(jīng)解決了。

2、解決第二個BUG

下面,我們解決第7行代碼中的數(shù)組訪問越界錯誤:數(shù)組的元素個數(shù)是n,但是bubble_sort()中第一個for循環(huán)的終止條件是i<=n,明顯會造成訪問越界,正確的條件應(yīng)該是i<n。

要解決這個BUG也很簡單,只需要在執(zhí)行第8行代碼之前,判斷如果i的值等于n,就跳出循環(huán)。對于這個簡單的程序,我們直接從bubble_sort()函數(shù)return就可以了。

命令如下:

b 8 if i==ncommand 2 printf 'i = %d, n = %d\n',i,n return continueend

在第8行設(shè)置條件斷點,當(dāng)i==n時斷點被觸發(fā),然后自動把i和n的值打印出來,再行return命令,從bubble_sort()返回,然后continue命令自動恢復(fù)程序執(zhí)行。

執(zhí)行結(jié)果如下圖:

解決第二個BUG

3、解決第三個BUG

下面,解決最后一個BUG,第23行數(shù)組訪問越界錯誤。

命令如下:

b 24 if i==10commands 3  printf 'i=%d, exit from for loop!\n',i  jump 26  continueend

與第二個BUG類似,在第24行設(shè)置條件斷點,當(dāng)==10時觸發(fā)斷點,然后退出循環(huán),讓程序跳轉(zhuǎn)到第26行繼續(xù)執(zhí)行。

執(zhí)行結(jié)果如下圖所示:

解決第三個BUG

從圖中可以看出,三個斷點全部被觸發(fā),并且預(yù)設(shè)的命令都正常執(zhí)行。

我們終于得到了正確的執(zhí)行結(jié)果!

雖然,現(xiàn)在程序可以正常執(zhí)行了,但是每次手動輸入命令還是比較麻煩的。我之前文章介紹過,GDB支持調(diào)試腳本,從腳本中加載并執(zhí)行調(diào)試命令。

下面,我們利用GDB腳本,來制作我們的“熱補丁”腳本。

制作“熱補丁”腳本

我們把上文中用來解決三個BUG的命令保存在一個腳本文件中:

vi bubble.fix

腳本內(nèi)容如下圖:

bubble.fix 熱補丁腳本

bubble.fix腳本中的命令,與上文在GDB中直接輸入的命令有幾個區(qū)別:

  1. 刪除了格式化打印信息。

  2. 刪除了commands后面的斷點ID。上文講過,commands后面的斷點ID可以省略,表示對最近一次設(shè)置的斷點有效。為了讓腳本更加通用,每個commands都緊跟在break命令之后,因此直接省略了斷點ID。

GDB的腳本可以通過兩種方式執(zhí)行:

  1. 啟動GDB時,用-x參數(shù)指定要執(zhí)行的腳本文件。

  2. 啟動GDB后,執(zhí)行source命令執(zhí)行指定的腳本。

下面,我們用第二種方式演示一下,如下圖所示:

執(zhí)行bubble.fix腳本

使用source命令加載并執(zhí)行bubble.fix,然后用run命令執(zhí)行程序,三個斷點均被觸發(fā),且預(yù)設(shè)的命令全部被正確執(zhí)行,最后程序運行正常,得到期望的結(jié)果!

我們現(xiàn)在可以利用我們制作的“熱補丁”腳本,在不修改代碼、不重新編譯和部署的前提下,成功修復(fù)程序中的BUG!是不是很有趣呢?

不過,做到這種程度,還不算完美!

盡管得到了正確的結(jié)果,但程序執(zhí)行時,總是會打印我們設(shè)置的斷點信息,看起來還是有些視覺干擾的。

最后,我們來解決這個問題,讓我們的“熱補丁”更加完美!

優(yōu)化“熱補丁”腳本,隱藏斷點信息

在預(yù)設(shè)的命令中,如果第一條命令是silent,斷點被觸發(fā)的打印信息會被屏蔽掉。

我們把bubble.fix做些修改,把silent命令加進去,如下圖所示:

最終版bubble.fix 腳本

然后,重新執(zhí)行一下:

這樣,看起來,清爽多了!

到此,我們終于實現(xiàn)了本文的目標(biāo):一邊debug,一邊修復(fù)BUG,避免反復(fù)修改代碼、重新編譯和部署、提高調(diào)試效率!

結(jié)語

本文重點介紹了如何利用GDB breakpoint command lists功能,制作“調(diào)試熱補丁”,修改代碼BUG。還可以利用這個功能,快速驗證我們的猜想和解決方案,避免反復(fù)修改代碼和重新編譯。

巧用GDB breakpoint command lists功能,可以做很多有趣的事情,如實現(xiàn)調(diào)試自動化,提高調(diào)試效率等。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
在 Linux 中如何使用 gdb 調(diào)試 C 程序
用GDB調(diào)試程序(二)
gcc和gdb
GDB高級技巧:同一個Bug,5種解決方案,不修改源碼,不重新編譯
C語言:當(dāng)GDB遇到復(fù)雜數(shù)據(jù)結(jié)構(gòu),兩分鐘帶你掌握四個高效調(diào)試技巧
除錯專家---程序調(diào)試的利器GDB
更多類似文章 >>
生活服務(wù)
熱點新聞
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服