文章目錄
一、gdb簡介
二、調(diào)試前的準(zhǔn)備
1、生成調(diào)試文件
2、啟動 gdb?
三、gdb 使用方法?
1、查看源代碼
2、設(shè)置 / 查看斷點(多種方式設(shè)置斷點)
方法一
方法二
方法三
3、run?
4、刪除斷點、斷點無效
5、逐過程調(diào)試(以函數(shù)為單位)
6、逐語句調(diào)試
7、查看調(diào)用鏈
8、查看變量值
單次查看
長顯示
9、指定運行到某行
10、 執(zhí)行完當(dāng)前函數(shù)
11、從一個斷點運行到另一個斷點
四、gdb 指令總結(jié)
一、gdb簡介
? ? ? ??GDB 全稱“GNU symbolic debugger”,是 Linux 下常用的程序調(diào)試器。發(fā)展至今,GDB 已經(jīng)迭代了諸多個版本,當(dāng)下的 GDB 支持調(diào)試多種編程語言編寫的程序,包括 C、C++、Go 等等。實際場景中,GDB 更常用來調(diào)試 C 和 C++ 程序。一般來說,GDB主要幫助我們完成以下四個方面的功能:
- 啟動你的程序,可以按照你的自定義的要求隨心所欲的運行程序。
- 在某個指定的地方或條件下暫停程序。
- 當(dāng)程序被停住時,可以檢查此時你的程序中所發(fā)生的事。
- 在程序執(zhí)行過程中修改程序中的變量或條件,將一個bug產(chǎn)生的影響修正從而測試其他bug。
二、調(diào)試前的準(zhǔn)備
程序源代碼如下:
1 #include<stdio.h>
2
3 int add(int x)
4 {
5 int ret=0;
6 printf("start\n");
7
8 for(int i=0;i<x;i++)
9 {
10 ret+=i;
11 }
12 printf("end\n");
13 return ret;
14 }
15
16 int main()
17 {
18 int x=100;
19 int ret=add(x);
20 printf("result:%d",ret);
21 return 0;
22 }
Makefile配置文件如下:
1 test:test.c
2 gcc -o test test.c -g -std=c99
3 .PHONY:clean
4 clean:
5 rm -f test
1、生成調(diào)試文件
? ? ? ? 不調(diào)試的情況下,使用 gcc 指令編譯文件一般是:gcc test.c -o test? 但是,如果要調(diào)試,那么就必須要加上?-g 指令。其原理用 Visual Studio Code 環(huán)境下來類比,使用該 IDE 熟悉的話便知道 發(fā)行一個程序的時候,可以有 release 版本和 debug 版本,只有我們選擇 debug 版本的時候,程序才可以被調(diào)試,否則不行。這里也類似,加上 -g 就相當(dāng)于生成 debug 版本的程序,不加就默認是 release 版本的。其正確指令如下:
gcc test.c -o test?-g -std=c99
? ? ? ? 同時,我們可以思考一下為什么 gcc 默認發(fā)行?release 版本的程序,其實是因為 debug 版本由于需要一些調(diào)試信息,所以就需要更大的內(nèi)存,運行起來會更慢。我們可以分別生成 release 版本和 debug 版本的文件,進行對比,確實 debug 版本所占空間更大。
? ? ? ? 當(dāng)然,我們也可以通過查看生成的兩個二進制文件,其里面是否包含調(diào)試信息。readefl 指令可以查看二進制文件里的一些信息。下圖也可以看出,確實 debug 版本里包含調(diào)試信息。
? ? ? ? 此外,這里要特別說明一下,配置文件中,第一個依賴方法里面? -std=c99 的含義。c99 是 C 語言的一個標(biāo)準(zhǔn),在這個標(biāo)準(zhǔn)里規(guī)定了 可以在循環(huán)控制條件里面定義變量,如 for(int i=0;i<10;i++) 。如果不使用 ? -std=c99 ?那么就會造成編譯出錯,如下,所以必須加上。
?
2、啟動 gdb?
? ? ? ? 啟動 gdb 的指令非常簡單,如下即可啟動生成的 test 文件。
gdb test
? ? ? ? 如下,出現(xiàn)這樣的界面,就代表進入gdb調(diào)試,就可以使用各種指令進行調(diào)試:
三、gdb 使用方法?
1、查看源代碼
l? 行號:( l 是 list 的簡寫),顯示源代碼,接著上次的位置往下列,每次列10行。在后面加上行號可以指定查看該行號附近的代碼。
? ? ? ? 測試效果如下:
(gdb) l
9 {
10 ret+=i;
11 }
12 printf("end\n");
13 return ret;
14 }
15
16 int main()
17 {
18 int x=100;
(gdb) l 1
1 #include<stdio.h>
2
3 int add(int x)
4 {
5 int ret=0;
6 printf("start\n");
7
8 for(int i=0;i<x;i++)
9 {
10 ret+=i;
(gdb) l
11 }
12 printf("end\n");
13 return ret;
14 }
15
16 int main()
17 {
18 int x=100;
19 int ret=add(x);
20 printf("result:%d",ret);
(gdb) list
21 return 0;
22 }
2、設(shè)置 / 查看斷點(多種方式設(shè)置斷點)
方法一
b? 行號 :(b 是 break 的簡寫),在某一行打上斷點。
info b? ?: 查看斷點。
? ? ? ? 可以看到,斷點里面有一些信息,大部分都不難理解,比如 Num是編號,Type是種類,Address是斷點在內(nèi)存的位置,What是斷點在程序中的位置。Disp 和 Enb 可能比較難理解。
Disp:斷點執(zhí)行一次之后是否有效 keep:有效 dis:無效
Enb: 當(dāng)前斷點是否有效 y:有效 n:無效
方法二
b 文件名:行數(shù)? ? :在指定的文件里面打斷點。
? ? ? ? 如下,這種情況可以在多文件調(diào)試的時候使用。
方法三
b? (文件名 : )函數(shù)名? ? : 指定函數(shù)打斷點。
? ? ? ? 如下,可以指定函數(shù)名打斷點,斷點生成的地方就是該函數(shù)開始執(zhí)行的地方。
? ? ? ? ?在多文件的時候,可以指定文件名打斷點。如下,刪除兩個斷點之后,利用指定文件名的方式在 main函數(shù)開始處 添加斷點。
3、run?
r :(run的縮寫),如果有斷點,執(zhí)行到斷點處停,如果沒有斷點,執(zhí)行結(jié)束。(相當(dāng)于 VS 里面的 F5 。)
? ? ? ? 如果執(zhí)行 r 之后,再查看斷點就會發(fā)現(xiàn)不一樣。如下,info b 顯示的信息里面,多了一行 “breakpoint already hit 1 time” 說明當(dāng)前斷點被命中。
4、刪除斷點、斷點無效
disable breakpoint 斷點編號: 使斷點無效。
enable breakpoint 斷點編號:?使斷點有效。
? ? ? ? ?如下,使用 disable 指令之后,info 信息里的 Enb 變成了 n,代表該斷點不可用。使用 enable 之后,變成了 y ,表示該斷點可用。當(dāng)然 breakpoint 可以簡寫為 b 。
d? 斷點編號:(delete 的縮寫)刪除 對應(yīng)編號的斷點。這里不是輸入行號,是斷點的編號。
? ? ? ? 如下,之前的斷點編號為1,刪除之后,使用 info 指令查看是沒有斷點的。
5、逐過程調(diào)試(以函數(shù)為單位)
?n? :(next 的縮寫)? ? 一步步調(diào)試,但是不會進入函數(shù)內(nèi)部。? ( 相當(dāng)于 VS 下的 F10,遇到函數(shù)調(diào)用不會進入函數(shù)。)
? ? ? ? 如下,設(shè)置 19 行的斷點之后,按下 r 運行代碼,在斷點處停下,然后 n 調(diào)試,顯然直接執(zhí)行完了 add 函數(shù)內(nèi)部的內(nèi)容,打印了 start 和 end。
6、逐語句調(diào)試
s? ?:(step 的縮寫)? 一步步調(diào)試,遇到函數(shù)調(diào)用的時候,進入函數(shù)。? ? (?相當(dāng)于 VS 下的F11,可以進入函數(shù)內(nèi)部調(diào)試。)
? ? ? ?如下,重新調(diào)試后,又回到了 19?行設(shè)置的斷點處,此時使用 s 調(diào)試,進入了 add 函數(shù)內(nèi)部,一步步執(zhí)行下去,也打印了 start (紅色箭頭指向處)。
7、查看調(diào)用鏈
bt? ? : 查看函數(shù)調(diào)用過程。函數(shù)調(diào)用是一個壓棧的過程,可以使用bt指令來查看。
? ? ? ? 在進入add 函數(shù)之后,使用 bt 指令查看如下,可以看到,先調(diào)用了main 函數(shù),然后add 函數(shù)壓棧。
8、查看變量值
單次查看
p? ?變量:(printf 的縮寫) 打印變量的值,但是只能單次打印。?
? ? ? ? 如下,在循環(huán)執(zhí)行到某一步的時候,查看一些變量、地址的值。但是由于不能一直顯示,顯得非常麻煩。
長顯示
display? 變量名: 查看某變量的值(內(nèi)置類型,結(jié)構(gòu)體等都可以),類似于 VS 里面的監(jiān)視,一直會顯示。
undisplay? 編號: 取消顯示某變量。
? ? ? ? 如下,查看了 ret 的值和地址,執(zhí)行 s 之后,確實會顯示這兩個值。然后不想看 ret 的值之后,不可以直接 undisplay ret? ,必須要 undisplay 編號,如下紅色箭頭。
9、指定運行到某行
until? 行數(shù) :使程序運行到指定行數(shù),在函數(shù)內(nèi)部使用。(VS 里面沒有相關(guān)功能按鍵。)
? ? ? ? 比如在一個函數(shù)內(nèi)部,有循環(huán)要執(zhí)行很多次,就非常麻煩,此時可以使用 until 直接跳過循環(huán)到達指定行數(shù)。如下,until 12 直接跳過了 add 函數(shù)里的循環(huán)(由于 i 是在循環(huán)內(nèi)部定義的,所以出了循環(huán)就監(jiān)視不到了。)
10、 執(zhí)行完當(dāng)前函數(shù)
finishi? :只執(zhí)行完當(dāng)前函數(shù),然后停下來。
? ? ? ? 如下,當(dāng)前在 add 函數(shù)里面,執(zhí)行 finishi 指令,add函數(shù)直接執(zhí)行完了,等待下一步指令。
11、從一個斷點運行到另一個斷點
c? ?:(continue 的縮寫) 當(dāng)前處于某函數(shù)內(nèi)部的斷點 A,下一個該函數(shù)內(nèi)部的斷點是 B,執(zhí)行continue,就會運行到斷點B處。
? ? ? ? 如下,重新 r 之后,進入了第一個斷點,然后continue,執(zhí)行到了第二個斷點處。
四、gdb 指令總結(jié)
? ? ? ? 如下,是一些gdb常用指令總結(jié),有一些是在上面有所展示,有一些沒有。文章來源:http://www.zghlxwxcb.cn/news/detail-787808.html
list/l 行號:顯示binFile源代碼,接著上次的位置往下列,每次列10行。
list/l 函數(shù)名:列出某個函數(shù)的源代碼。
r 或 run:運行程序。
n 或 next:單條執(zhí)行。
s或step:進入函數(shù)調(diào)用
break(b) 行號:在某一行設(shè)置斷點
break 函數(shù)名:在某個函數(shù)開頭設(shè)置斷點
info break :查看斷點信息。
finish:執(zhí)行到當(dāng)前函數(shù)返回,然后挺下來等待命令
p 變量:打印變量值。
set var:修改變量的值
continue(或c):從當(dāng)前位置開始連續(xù)而非單步執(zhí)行程序
run(或r):從開始連續(xù)而非單步執(zhí)行程序
delete breakpoints:刪除所有斷點
delete breakpoints n:刪除序號為n的斷點
disable breakpoints:禁用斷點
enable breakpoints:啟用斷點
info(或i) breakpoints:參看當(dāng)前設(shè)置了哪些斷點
display 變量名:跟蹤查看一個變量,每次停下來都顯示它的值
undisplay:取消對先前設(shè)置的那些變量的跟蹤
until X行號:跳至X行
breaktrace(或bt):查看各級函數(shù)調(diào)用及參數(shù)
info(i) locals:查看當(dāng)前棧幀局部變量的值
quit:退出gdb文章來源地址http://www.zghlxwxcb.cn/news/detail-787808.html
到了這里,關(guān)于【Linux】gdb調(diào)試器的使用的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!