文章目錄
- 一、前言
- 二、gdb 斷點
- 三、本節用到的 gdb 基本指令
一、前言
當需要調試的程式非常大的時候,錯誤有可能隻是一個很小的地方,這時如果隻能一步一步去調試的話會很消耗時間,gdb 提供了斷點來解決這個問題。設定斷點能夠讓程式一直運作直到程式到達斷點處。這樣可以幫助我們将錯誤定位到一個範圍内的,接下來可以通過單步執行來找到具體問題。
二、gdb 斷點
test.c 如下
#include <stdio.h>
int main()
{
int i, sum = 0;
char input[10];
while (1)
{
scanf("%s", input);
for (i = 0; input[i]; i++)
{
sum = sum * 10 + input[i] - '0';
}
printf("sum = %d\n", sum);
}
return 0;
}
該程式的作用是将輸入的字元串轉換成數字并輸出。
我們先運作程式看看程式的效果:
[[email protected] haha]$ ./test
435
sum = 435
46
sum = 43546
^C
顯然程式并沒有按照我們預想的一樣運作,接下來使用 gdb 對程式進行調試找到問題所在,gdb 基本操作在 [Linux] gdb 單步執行和跟蹤函數調用 中有過介紹。
[[email protected] haha]$ gdb test
...
(gdb) start
emporary breakpoint 1 at 0x4005a5: file test.c, line 5.
Starting program: /home/MyCode/test
Temporary breakpoint 1, main () at test.c:5
5 int i, sum = 0;
Missing separate debuginfos, use: debuginfo-install glibc-2.17-260.el7_6.3.x86_64
(gdb)
開始運作後如果想要知道目前棧幀局部變量的值可以使用之前介紹的 info(或 i) locals ,但是使用 display 可以跟蹤顯示某個變量的值,即每進行一步調試都會顯示該變量的值,具體如下:
(gdb) display i
1: i = 0
(gdb) display sum
2: sum = 0
(gdb) n
10 scanf("%s", input);
2: sum = 0
1: i = 0
使用 display 變量編号 可以取消追蹤,i 的編号是 1 ,我們可以試着取消 i 的追蹤:
(gdb) undisplay 1
(gdb) n
123 (回車)
11 for (i = 0; input[i]; i++)
2: sum = 0
我們已經知道第一次輸出沒有問題,此時可以通過 break 行數 指令(簡寫為 b)設定斷點,再用 continue 指令(簡寫為 c)讓程式一直運作到斷點處。break 的參數也可以是函數名。
(gdb) l
6 char input[10];
7
8 while (1)
9 {
10 scanf("%s", input);
11 for (i = 0; input[i]; i++)
12 {
13 sum = sum * 10 + input[i] - '0';
14 }
15 printf("sum = %d\n", sum);
(gdb) break 10
Breakpoint 2 at 0x4005ac: file test.c, line 10.
(gdb) c
Continuing.
sum = 123
Breakpoint 2, main () at test.c:10
10 scanf("%s", input);
2: sum = 123
現在我們已經跳過了第一次運作,目前也沒有發現問題,接下來繼續第二次輸入字元串:
(gdb) n
45 (回車)
11 for (i = 0; input[i]; i++)
2: sum = 123
我們注意到此時 sum 的值仍為上次循環的結果,顯然問題是我們并沒有及時的對 sum 進行清零。到這裡就展現出斷點的作用,可以跳過沒有問題的部分,讓我們專心于程式的細節。我們需要做的是确定問題出現的範圍,然後利用調試找到具體問題。具體在哪裡設定斷點還需要靠我們去分析确定。
調試中可以設定多個斷點并通過 info 指令檢視已經設定的斷點:
(gdb) b 15
Breakpoint 4 at 0x400600: file test.c, line 15.
(gdb) i breakpoints
Num Type Disp Enb Address What
2 breakpoint keep y 0x00000000004005ac in main at test.c:10
breakpoint already hit 2 times
4 breakpoint keep y 0x0000000000400600 in main at test.c:15
每個斷點都有一個編号,可以用編号指定删除某個斷點:
(gdb) delete breakpoints 4
(gdb) i breakpoints
Num Type Disp Enb Address What
2 breakpoint keep y 0x00000000004005ac in main at test.c:10
breakpoint already hit 2 times
有時候一個斷點暫時不用可以禁用掉而不必删除,這樣以後想用的時候可以直接啟用,而不必重新從代碼裡找應該在哪一行設斷點:
(gdb) disable breakpoints 2
(gdb) i breakpoints
Num Type Disp Enb Address What
2 breakpoint keep n 0x00000000004005ac in main at test.c:10
breakpoint already hit 2 times
(gdb) enable 2
(gdb) i breakpoints
Num Type Disp Enb Address What
2 breakpoint keep y 0x00000000004005ac in main at test.c:10
breakpoint already hit 2 times
(gdb) delete breakpoints
Delete all breakpoints? (y or n) y
(gdb) i breakpoints
No breakpoints or watchpoints.
gdb 的斷點功能非常靈活,還可以設定斷點在滿足某個條件時才激活,例如我們仍然在循環開頭設定斷點,但是僅當 sum 不等于0時才中斷,然後用 run 指令(簡寫為 r)重新從程式開頭連續運作:
(gdb) break 10 if sum != 0
Breakpoint 5 at 0x4005ac: file test.c, line 10.
(gdb) i breakpoints
Num Type Disp Enb Address What
5 breakpoint keep y 0x00000000004005ac in main at test.c:10
stop only if sum != 0
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/MyCode/test
123
sum = 123
Breakpoint 5, main () at test.c:10
10 scanf("%s", input);
2: sum = 123
結果是第一次執行 scanf 之前沒有中斷,第二次卻中斷了。
三、本節用到的 gdb 基本指令
指令 | 描述 |
---|---|
break(或b) 行号 | 在某一行設定斷點 |
break 函數名 | 在某個函數開頭設定斷點 |
break … if … | 設定條件斷點 |
continue(或c) | 從目前位置開始連續運作程式 |
delete breakpoints 斷點号 | 删除斷點 |
display 變量名 | 跟蹤檢視某個變量,每次停下來都顯示它的值 |
disable breakpoints 斷點号 | 禁用斷點 |
enable 斷點号 | 啟用斷點 |
info(或i) breakpoints | 檢視目前設定了哪些斷點 |
run(或r) | 從頭開始連續運作程式 |
undisplay | 跟蹤顯示号 取消跟蹤顯示 |