GDB调试教程:1小时玩转Linux gdb命令
原文鏈接:http://c.biancheng.net/gdb/
GDB 入門教程
本教程以下面的代碼為例,在 Linux 系統下來講解 GBD 的調試流程:
int main (void) {unsigned long long int n, sum;n = 1;sum = 0;while (n <= 100){sum = sum + n;n = n + 1;}return 0; }將代碼保存到 ~/demo/main.c,~表示當前用戶的主目錄,也即 home 目錄。
首先我們使用 GCC 來編譯 main.c,編譯的時候使用-g選項,它的目的是向可執行程序中加入調試信息,包括源代碼、符號表等,GDB 需要這些額外的信息來完成調試工作。
此外還建議關閉編譯器的程序優化選項。編譯器的程序優化選項一般有五個級別,從 O0 ~ O4, O0 表示不優化,從 O1 ~ O4 優化級別越來越高,O4 最高。這樣做的目的是為了調試的時候,符號文件顯示的調試變量等能與源代碼完全對應起來。
使用 GCC 編譯源文件:
打開 demo 文件夾,發現多了一個 mian.out,說明編譯成功。
1) 啟動 GDB 調試器
接下來啟動 GDB 并調試 main.out:
$ gdb main.out -silent Reading symbols from /home/mozhiyan/demo/main.out...done.選項-silent用于屏蔽 GDB 的前導信息,否則它會在屏幕上打印一堆免責條款。
啟動 GDB 后,它輸出的信息表明已經讀入了 mian.out 的符號表。接下來,GDB 會顯示自己的提示符(gbd),提示并等待你輸入調試命令。
2) gdb -b 選項:設置斷點
調試一個程序的時候,應該在我們關注的地方,或者在故障點的前邊設置一個斷點(Breakpoint),讓程序執行到這里停下來,這樣我們就可以慢慢地用別的調試命令進行觀察。
在 GDB 中,設置斷點的方法很多,包括在指定的內存地址處設置斷點、在源代碼的某一行設置斷點,或者在某個函數的入口處設置斷點,等等。設置斷點的命令是b或者break,在這里我 們是將 main 函數的入口處作為斷點:
b 命令在執行后返回了斷點的具體信息,也就是說,斷點(main 函數的入口位置)的內存地址為 0x4004f4,對應于源文件的第 5 行(也就是說,main 函數位于源文件的第 5 行)。
如果我們用內存地址的方式來設置這個斷點,則可以是:
b * 0x4004f4
星號*意味著是以內存地址作為斷點的。
如果用源代碼行的形式設置這個斷點,則可以是
b 5
3) gdb -r 選項:執行程序
一旦設置了斷點,下一步就是用r或者run命令執行被調試的程序,執行后會自動在第一個斷點處停下來:
Starting program: /home/mozhiyan/demo/main.out [New Thread 1500.0x1e34] [New Thread 1500.0x2fb8]Thread 1 hit Breakpoint 1, main () at main.c:5 5 n = 1;在運行了被調試的程序后,GDB 的輸出信息顯示程序己經啟動,下一個將要執行的語句是第 5 行的n = 1;。
注意,這條語句并沒有執行,而僅僅是告訴你,再繼續執行程序的話,執行的語句會是它。
4) gdb -p 選項:打印變量的值
在當前位置,變量 n 和 sum 己經分配,但并沒有開始賦值。此時,這兩個變量的值會是多少呢?我們可以使用p或者print命令來分別顯示:
(gdb) p n $1 = 24 (gdb) p sum $2 = 140737488347344GDB 的 p 命令用于打印一個表達式的值,在這里是表達式 n 和 sum。
GDB 先計算表達式的值,并把它保存在一個存儲區中,存儲區的名字用$外加數字來表示,并且這個數字會隨著調試過程的進行而不斷遞增(這意味著存儲區也是不斷開辟的)。以上,第一個 p 命令執行后,GDB 的回應是$1 = 16,意思是表達式 n 的值保存在 $1 中,其內容為 16。
注意,在你的計算機上,變量 n 和 sum 的當前值可能和這里顯示的不同。這很好理解,內存是反復使用的,當一個程序終止后,它占用的內存會分配給其他程序使用;當一個變量不再使用后,它占用的內存也會重新分配,并成為另一個變量。因為變量 n 和 sum 剛剛分配,還沒有往里面保存任何數值,故它們的內容是隨機的,是其他程序或者變量用過的垃圾值。
順便說一下,既然 $1 是 GDB 用于保存計算結果的內部存儲區的名字,那么我們也可以用 p 命令來打印它:
5) gdb -n 選項:單步調試
下面,我們將通過單步執行程序,來看一看變量 n 和 sum 賦值后的值。調試命令n或者next用于繼續執行源文件中的下一行。
(gdb) n 6 sum = 0;執行 n 命令后,實際執行的是第 5 行n = 1;,GDB 顯示下一個即將執行的源代碼行,也就是第 6 行的sum = 0;。
因為此時己經往變量 n 寫入了 1,所以我們可繼續用 p 命令來觀察它現在的存儲值:
顯然,經賦值后,變量 n 的值己經變成 1。
繼續執行下一條指令,實際執行的是第 6 行sum = 0。執行后,GDB 停下并顯示下一條即將執行的源代碼行,也即第 8 行的while (n <= 100),第 7 行為空行,所以直接跳過了:
剛才執行的語句是往變量 sum 保存數值 0,故我們可以再次用 p 命令來觀察變量 sum 現在的存儲值,可發現它己經變成 0:
(gdb) p sum $5 = 0繼續用 n 命令執行下一個源代碼行,則將計算 while 語句的控制表達式,并根據該表達式的值決定是否進入循環體,執行后 GDB? 顯示下一條即將執行的源代碼行是第 10 行:
(gdb) n 10 sum = sum + n;進入循環體之后,我們想再看看變量 n 和 sum 的當前值。但這次使用 p 命令的方法不一樣,這次是用花括號將表達式 n 和 sum 圍住以形成一個集合。GDB 允許用這種方式來一次性地打印多個表達式的值:
(gdb) p {n, sum} $6 = {1, 0}顯然,變量 n 和 sum 此時的值依然分別為 1 和 0。
繼續用 n 命令執行第 10 行,執行后 GDB 停留在即將執行的第 11 行:
注意,第 10 行己經執行完畢,但第 11 行還沒有執行。猜猜看,變量 n 和 sum 此時的值是多少?猜測之后,用 p 命令看看結果是否如你所想:
(gdb) p {n, sum} $7 = {1, 1}繼續用 n 命令執行下一個源代碼行,這將執行第 11 行的n = n + 1;,執行后控制又回到了循環的起始處,也即第 8 行:
(gdb) n 8 while (n <= 100)此時,變量 n 和 sum 的值各自會是多少?使用 p 命令打印一下就知道了:
(gdb) p {n, sum} $8 = {2, 1}因為現在處于一個循環體內,如果繼續用 n 命令往下執行,則其過程與前面相比大同小異。前面己經循環過一次,本次循環完整的調試過程如下:
(gdb) n 10 sum = sum + n; (gdb) n 11 n = n + 1; (gdb) n 8 while (n <= 100) (gdb) p {n, sum} $9 = {3, 3}顯然,第二次循環過后,變量 n 的值為 3,變量 sum 的值也是 3。
你可能己經發現了,我們現在進退維谷:如果繼續用 n 命令執行,則將陷入循環,直到變量 n 的值等于 101。好在這也算不上什么大的問題,我們可以在循環語句的后面設置斷點,然后命令程序一直執行,直至到達這個斷點。
6) gdb -l 選項:列出源文件內容
為了搞清楚 while 語句的下一條語句的行號,我們需要列出源文件的內容,這需要使用l或者list命令:
(gdb) l 3 unsigned long long int n, sum; 4 5 n = 1; 6 sum = 0; 7 8 while (n <= 100) 9 { 10 sum = sum + n; 11 n = n + 1; 12 }l 命令默認每次顯示 10 行源代碼,但我們關心的那一行顯然還沒有出來。為此,可繼續使用 l 命令來顯示后面的行:
(gdb) l 13 14 return 0; 15 }好了,我們己經知道 while 語句之后是 return 語句,它的行號是 14,現在就可以用 b 命令設置一個新的斷點:
(gdb) b 14 Breakpoint 2 at 0x40051a: file main.c, line 14.7) gdb -c 選項:繼續執行程序
現在,可以用一個新的命令c或者continue來持續執行程序,直至遇到斷點或者程序結束。因為己經設置斷點,故程序將持續執行,在第 14 行處停下:
(gdb) c Continuing.Breakpoint 2, main () at main.c:14 14 return 0;非常好,既然己經退出了 while 循環,說明累加過程己經成功結束,變量 sum 的值就是累加結果。我們來看看它到底是多少:
(gdb) p {n, sum} $10 = {101, 5050}顯然,變量 n 的當前值是 101,變量 sum 的當前值是 5050,和數學計算的結果一模 一樣。
8) gdb -q 選項:退出調試
本次調試即將結束,我們可以先用 c 命令讓程序“跑完全程”,然后再用q或者quit結束本次調試工作,這將使得調試器 GDB 結束運行并返回到操作系統:
(gdb) c Continuing. [Inferior 1 (process 2814) exited normally] (gdb) q [c.biancheng.net demo]$好了,GDB 調試過程到此結束。
總結
GDB 是非常強大的工具,它的用法可以寫一本厚厚的書,上述 GDB 調試教程雖然是蜻蜓點水、走馬觀花,但對于初學者來說已經足夠了,大家應該能夠發現 99% 的邏輯錯誤了。
總結
以上是生活随笔為你收集整理的GDB调试教程:1小时玩转Linux gdb命令的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 成都欢乐谷给了门票可以玩几项
- 下一篇: shell将命令执行的结果赋值给 变量