版權聲明:本文為 cdeveloper 原創文章,可以隨意轉載,但必須在明確位置注明出處!
gdb 簡介
gdb
是 UNIX
及 UNIX-like
下的調試工具,在 Linux 下一般都直接在命令行中用 gdb 來調試程序,相比 Windows 上的集成開發環境 IDE
提供的圖形界面調試,一開始使用 gdb 調試可能會讓你感到生無可戀,但是只要熟悉了 gdb 調試的常用命令,調試出程序會很有成就感,一方面因為這些命令就類似圖形界面調試按鈕背后的邏輯,另一方面用命令行來調試程序,逼格瞬間就上了一個檔次,這次就跟大家分享 gdb 調試的基本技術和 15 個常用調試命令。
在此之前,我們先來回顧下在 Windows 上使用 IDE 的圖形界面調試過程。
IDE 的調試步驟
在 Windows 的 IDE 下調試程序,例如使用 VS,一般都有下面這幾個操作:
- Debug 模式編譯并啟動程序
- 程序運行出錯,打斷點分析出錯的地方
- 單步運行程序,包括:
step over
單步執行;step into
跳入函數;step return
跳出函數 - 還有全速運行,打印或者監視變量,凍結或解凍線程等調試技術
在 IDE 中上面的這些步驟一般都有固定的按鈕提供給我們使用,非常的簡單方便,我們只要多練習練習,在圖形界面調試程序不會很難,但是在 Linux 下用命令來調試程序就比圖形界面要復雜很多了。
其實,你知道真正的調試高手是什么樣的嗎?就是 Ta 對計算機原理和程序本身的邏輯理解非常深刻,在 Ta 的腦海中已經可以模擬程序的運行過程,并且知道可能出錯的地方,這樣連斷點都不用打了,而且 Bug 的命中率也不低,或許這就是真正的大佬吧 :)
我們回到正題,來介紹在 Linux 使用命令行的調試過程。
gdb 的調試步驟
在 Linux 下既然是使用命令行來調試,顧名思義就是手敲命令來調試程序,大體分為下面幾個步驟,后面會詳細介紹:
- 編譯可以調試的程序
- 運行程序,打斷點
- 單步調試,監控變量
- 可視化調試
- 其他調試技術
可以看出,與 IDE 調試過程差不多,但是實際操作起來可是千差萬別,可不是點按鈕了,而是自己敲調試程序的命令,就相當于你正在學習那些調試按鈕背后的原理,把這種方法學會,不用學就會使用 IDE 來調試程序,不管你信不信,我反正是信了 :)
下面開始正式的調試技術介紹。
15 個 gdb 調試基礎命令
下面來正式介紹 gdb 常用的調試技術,都是調試命令,只看不做比較乏味,還是建議你跟我一起動手調試下面的程序,這樣才能真正的學會,這是本次要調試的 hello.c
程序,非常簡單:
#include <stdio.h>
int add(int x, int y) {
return x + y;
}
int main() {
int a = 1;
int b = 2;
printf("a = %d\n", a);
printf("b = %d\n", b);
int c = add(a, b);
printf("%d + %d = %d\n", a, b, c);
return 0;
}
1. 編譯可以調試的程序
我們平常使用 gcc 編譯的程序如果不加 [-g]
選項:
gcc hello.c -o hello
gdb 會提示該可執行文件沒有調試符號,不能調試:
gdb hello
# gdb 提示信息
Reading symbols from a.out...(no debugging symbols found)...done.
如果需要讓程序可以調試,就必須在編譯的時候加上 [-g]
參數:
gcc -g hello.c -o hello
2. 載入要調試的程序
我們在命令行下需要手動載入待調試的程序,有 2 種方法:
方法一 - gdb 可執行文件
使用如下的命令來載入可執行文件 hello
到 gdb 中:
gdb hello
載入成功,gdb 會打印一段提示信息,并且命令行前綴變為 (gdb)
,下面是我的 Ubuntu
打印的信息:
GNU gdb (Ubuntu 7.11.90.20161005-0ubuntu1) 7.11.90.20161005-git
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from hello...done.
(gdb) q
注:按 q
退出 gdb
方法二 - 使用 gdb 提供的 file 命令
第二種方法是在 gdb 環境中使用 file
命令,我們需要先進入 gdb 環境下:
gdb
使用 file hello
載入待調試程序:
...
(gdb) file hello
Reading symbols from hello...done.
(gdb) q
3. 查看調試程序
在 gdb 下查看調試程序使用命令 list
或簡寫 l
,「回車」列出后面程序:
(gdb) list
1 #include <stdio.h>
2
3 int add(int x, int y) {
4 return x + y;
5 }
6
7
8 int main() {
9 int a = 1;
10 int b = 2;
(gdb)
11 printf("a = %d\n", a);
12 printf("b = %d\n", b);
13
14 int c = add(a, b);
15 printf("%d + %d = %d\n", a, b, c);
16 return 0;
17 }
4. 添加斷點
在 gdb 下添加斷點使用命令 break
或簡寫 b
,有下面幾個常見用法(這里統一用 b
):
- b function_name
- b row_num
- b file_name:row_num
- b row_num if condition
比如我們以第一個為例,在 main
函數上添加斷點:
(gdb) b main
Breakpoint 1 at 0x6e8: file hello.c, line 4.
打印的信息告訴我們在 hello.c
文件的第 4 行,地址 0x6e8
處添加了一個斷點,那如何查看斷點呢?
5. 查看斷點
在 gdb 下查看斷點使用命令 info break
或簡寫 i b
,比如查看剛才打的斷點:
(gdb) i b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000000006e8 in main at hello.c:4
可以看到打印出剛才添加的 main
函數的斷點信息:編號,類型,顯示狀態,是否啟用,地址,其他信息,那又如何刪除這個斷點呢?
6. 禁用斷點
在 gdb 下禁用斷點使用命令 disable Num
,比如禁用剛才打的斷點:
(gdb) disable 1
(gdb) i b
Num Type Disp Enb Address What
1 breakpoint keep n 0x00000000000006e8 in main at hello.c:4
可以看到字段「Enb」已經變為 n
,表示這個斷點已經被禁用了。
7. 刪除斷點
在 gdb 下刪除斷點使用命令 delete 斷點 Num
或簡寫 d Num
,比如刪除剛才的 Num = 1
的斷點:
(gdb) d 1
(gdb) i b
No breakpoints or watchpoints.
刪除后再次查看斷點,提示當前沒有斷點,說明刪除成功啦,下面來運行程序試試。
8. 運行程序
在 gdb 下使用命令 run
或簡寫 r
來運行當前載入的程序:
(gdb) r
Starting program: /home/orange/Desktop/gdb/hello
a = 1
b = 2
1 + 2 = 3
[Inferior 1 (process 10415) exited normally]
我這次沒有添加斷點,程序全速運行,然后正常退出了。
9. 單步執行下一步
在 gdb 下使用命令 next
或簡寫 n
來單步執行下一步,假設我們在 main
打了斷點:
(gdb) b main
Breakpoint 1 at 0x6e8: file hello.c, line 4.
(gdb) r
Starting program: /home/orange/Desktop/gdb/hello
Breakpoint 1, main () at hello.c:4
4 int a = 1;
(gdb) n
5 printf("a = %d\n", a);
可以看到當前停在 int a = 1;
這一行,按 n
執行了下一句代碼 printf("a = %d\n", a);
10. 跳入,跳出函數
在 gdb 下使用命令 step
或簡寫 s
來跳入一個函數,使用 finish
來跳出一個函數,我們在第 14 行 int c = add(a, b);
添加一個斷點:
(gdb) b 14
Breakpoint 1 at 0x6f6: file hello.c, line 14.
(gdb) r
Starting program: /home/orange/Desktop/gdb/hello
a = 1
b = 2
Breakpoint 1, main () at hello.c:14
14 int c = add(a, b);
(gdb) s
add (x=1, y=2) at hello.c:4
4 return x + y;
(gdb) finish
Run till exit from #0 add (x=1, y=2) at hello.c:4
0x0000555555554705 in main () at hello.c:14
14 int c = add(a, b);
Value returned is $1 = 3
(gdb) n
15 printf("%d + %d = %d\n", a, b, c);
這個過程是這樣的:
- 在 14 行
int c = add(a, b);
添加斷點 - 程序運行并停到
int c = add(a, b);
這一行 -
s
跳入 add 函數 -
finish
跳出 add 函數,并輸出一些函數返回的信息
11. 打印變量
在 gdb 中使用命令 print var
或簡寫 p var
來打印一個變量或者函數的返回值,我們在第 10 行 int b = 2;
添加一個斷點:
(gdb) b 10
Breakpoint 1 at 0x6c3: file hello.c, line 10.
(gdb) r
Starting program: /home/orange/Desktop/gdb/hello
Breakpoint 1, main () at hello.c:10
10 int b = 2;
(gdb) p a
$1 = 1
我們打印出變量 a 的值為 1,在調試中比較頻繁的操作是「監視變量」,在 gdb 中如何做呢?
12. 監控變量
在 gdb 中使用命令 watch var
來監控一個變量,使用 info watch
來查看監控的變量,我們這里來監控變量 c
:
(gdb) b 14
Breakpoint 1 at 0x6f6: file hello.c, line 14.
(gdb) r
Starting program: /home/orange/Desktop/gdb/hello
a = 1
b = 2
Breakpoint 1, main () at hello.c:14
14 int c = add(a, b);
(gdb) watch c
Hardware watchpoint 2: c
(gdb) info watch
Num Type Disp Enb Address What
2 hw watchpoint keep y c
注意:程序必須要先運行才能監控。
13. 查看變量類型
在 gdb 下使用命令 whatis
查看一個變量的類型:
(gdb) b 10
Breakpoint 1 at 0x6c3: file hello.c, line 10.
(gdb) r
Starting program: /home/orange/Desktop/gdb/hello
Breakpoint 1, main () at hello.c:10
10 int b = 2;
(gdb) whatis b
type = int
這里變量 b
是 int
類型。
14. 在 gdb 中進入 shell
在 gdb 下使用命令 shell
啟動 shell :
(gdb) shell
orange@ubuntu:~/Desktop/gdb$ exit
exit
(gdb)
使用 exit
會再次退回到 gdb 中。
15. 在 gdb 中實現可視化調試
誰說 gdb 只能在命令行調試呢?gdb 也支持「圖形界面」,不過這里的圖形界面都是用字符顯示的,當然不如 VS 那種好看,不過使用可視化相比直接看命令行更加直觀了。
在 gdb 下使用 wi
啟動可視化調試:
(gdb) wi
效果如下圖所示,上面是代碼效果,下面是命令界面:
有了圖形界面,就再對照著圖形界面將前面的命令再練習練習,看看自己手敲的命令背后到底做了些什么,加深下影響。
結語
這篇博客主要介紹了 gdb 基本的調試技術,一篇文章不可能面面俱到,還有很多命令沒有介紹,如果你有興趣的話,這里還有一份比較好的 gdb 快速學習指南 送給愛學習的你,不用客氣,叫我雷鋒就好。
最后,感謝你的閱讀,我們下次再見 :)