GDB查看變量值 print和display
GDB 調試程序,最常用的方法是:單步調試或者斷點調試程序,期間通過查看某個變量或者表達式的值,判斷當前程序的執行過程是否正確,不斷縮小異常或 Bug 位於代碼中的范圍,最終找到並修復。
對於在調試期間查看某個變量或表達式的值,GDB 調試器提供有 2 種方法,即使用 print 命令或者 display 命令。本節就對這 2 個命令的功能和用法做詳細的講解,整個講解過程將以調試如下 C 語言程序為例:
#include <stdio.h> int main(){ int num,result=0,i=0; scanf("%d", &num); while(i<=num){ result += i; i++; } printf("result=%d\n", result); return 0; }
GDB print命令
rint 命令的功能就是在 GDB 調試程序的過程中,輸出或者修改指定變量或者表達式的值。print 命令可以縮寫為 p,最常用的語法格式如下所示:
(gdb) print num
(gdb) p num
其中,參數 num 用來代指要查看或者修改的目標變量或者表達式。
示例:
[root@all c]# gdb main -q Reading symbols from /root/c/main...done. (gdb) l 1 #include <stdio.h> 2 int main(){ 3 int num,result=0,i=0; 4 scanf("%d", &num); 5 while(i<=num){ 6 result += i; 7 i++; 8 } 9 printf("result=%d\n", result); 10 return 0; (gdb) 11 } (gdb) b 4 Breakpoint 1 at 0x40054a: file main.c, line 4. (gdb) r Starting program: /root/c/main Breakpoint 1, main () at main.c:4 4 scanf("%d", &num); Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.149.el6.x86_64 (gdb) n 3 5 while(i<=num){ (gdb) p num $1 = 3 (gdb) p num=4 $2 = 4 (gdb) b 9 Breakpoint 2 at 0x400577: file main.c, line 9. (gdb) c Continuing. Breakpoint 2, main () at main.c:9 9 printf("result=%d\n", result); (gdb) p result $3 = 10 (gdb) p result=20 $4 = 20
可以看到,調試 main 的過程中,我們先后使用 p 命令輸出了程序中 num 和 result 變量的值,同時還使用該命令對它們的值做了修改。
GDB display命令
和 print 命令一樣,display 命令也用於調試階段查看某個變量或表達式的值,它們的區別是,使用 display 命令查看變量或表達式的值,每當程序暫停執行(例如單步執行)時,GDB 調試器都會自動幫我們打印出來,而 print 命令則不會。
display 命令沒有縮寫形式,常用的語法格式如下 2 種:
(gdb) display expr
(gdb) display/fmt expr
其中,expr 表示要查看的目標變量或表達式;參數 fmt 用於指定輸出變量或表達式的格式,表 1 羅列了常用的一些 fmt 參數。
/fmt | 功 能 |
---|---|
/x | 以十六進制的形式打印出整數。 |
/d | 以有符號、十進制的形式打印出整數。 |
/u | 以無符號、十進制的形式打印出整數。 |
/o | 以八進制的形式打印出整數。 |
/t | 以二進制的形式打印出整數。 |
/f | 以浮點數的形式打印變量或表達式的值。 |
/c | 以字符形式打印變量或表達式的值。 |
注意,display 命令和 /fmt 之間不要留有空格。以 /x 為例,應寫為 (gdb)display/x expr。
仍以main程序為例:
[root@all c]# gdb main -q Reading symbols from /root/c/main...done. (gdb) b 4 Breakpoint 1 at 0x40054a: file main.c, line 4. (gdb) b 9 Breakpoint 2 at 0x400577: file main.c, line 9. (gdb) r Starting program: /root/c/main Breakpoint 1, main () at main.c:4 4 scanf("%d", &num); Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.149.el6.x86_64 (gdb) display num 1: num = 32767 (gdb) display/t result 2: /t result = 0 (gdb) n 3 5 while(i<=num){ 2: /t result = 0 1: num = 3 (gdb) c Continuing. Breakpoint 2, main () at main.c:9 9 printf("result=%d\n", result); 2: /t result = 110 1: num = 3 (gdb) c Continuing. result=6 Program exited normally.
可以看到,使用 display 命令查看 num 和 result 變量值時,不僅在執行該命令的同時會看到目標變量的值,后續每次程序停止執行時,GDB 調試器都會將目標變量的值打印出來。
事實上,對於使用 display 命令查看的目標變量或表達式,都會被記錄在一張列表(稱為自動顯示列表)中。通過執行info dispaly
命令,可以打印出這張表:
(gdb) info display Auto-display expressions now in effect: Num Enb Expression 2: y /t result 1: y num
其中,各列的含義為:
- Num 列為各變量或表達式的編號,GDB 調試器為每個變量或表達式都分配有唯一的編號;
- Enb 列表示當前各個變量(表達式)是處於激活狀態還是禁用狀態,如果處於激活狀態(用 y 表示),則每次程序停止執行,該變量的值都會被打印出來;反之,如果處於禁用狀態(用 n 表示),則該變量(表達式)的值不會被打印。
- Expression 列:表示查看的變量或表達式。
對於不需要再打印值的變量或表達式,可以將其刪除或者禁用。
1) 通過執行如下命令,即可刪除自動顯示列表中的變量或表達式:
(gdb) undisplay num...
(gdb) delete display num...
參數 num... 表示目標變量或表達式的編號,編號的個數可以是多個。
舉個例子:
(gdb) undisplay 1 (gdb) info display Auto-display expressions now in effect: Num Enb Expression 2: y /t result (gdb)
可以看到,編號為 2 的 result 變量的 Enb 由 y 變成了 n。處於禁用狀態的變量或表達式,程序停止執行時將不再自動打印出它們的值。
當然根據需要,也可以激活當前處於禁用狀態的變量或表達式,執行如下命令即可:
(gdb) enable display num...
參數 num... 表示要激活的變量或表達式的編號,編號的個數可以是多個,表示一次性激活多個變量或表達式。
舉個例子:
(gdb) enable display 2 (gdb) info display Auto-display expressions now in effect: Num Enb Expression 2: y /t result (gdb)
總的來說,每次程序停止執行時,GDB 調試器會將自動顯示列表中處於激活狀態下的變量或表達式的值打印出來,display 命令可以實現在查看目標變量或表達式的值的同時,將其添加到自動顯示列表中,而 print 命令則只會打印出目標變量或表達式的值。
print命令的高級用法
print 命令還有更高級的功能和用法,例如以指定的格式輸出變量或者表達式的值、輸出數組中指定區間內的所有元素等等。和 print 命令最基本的用法相比,該命令的完整語法格式如下所示:
(gdb) print [options --] [/fmt] expr
格式中用 [ ] 括起來的部分是可選的,可以使用也可以省略。其中,各個參數的含義如下:
- options:表示該命令所支持的選項,這些選項可以控制 print 命令輸出指定內容的變量或者表達式的值;
- fmt:指定輸出變量或表達式值時所采用的格式;
- expr:指定要查看的變量或表達式。
1) 首先介紹 options 參數的用法,表 1 羅列了常用的幾個 options 參數值。
options 參數 | 功 能 |
---|---|
-address on|off | 查看某一指針變量的值時,是否同時打印其占用的內存地址,默認值為 on。該選項等同於單獨執行 set print address on|off 命令。 |
-array on|off | 是否以便於閱讀的格式輸出數組中的元素,默認值為 off。該選項等同於單獨執行 set printf array on|off 命令。 |
-array-indexes on|off | 對於非字符類型數組,在打印數組中每個元素值的同時,是否同時顯示每個元素對應的數組下標,默認值為 off。該選項等同於單獨執行 set print array-indexes on|off 命令。 |
-pretty on|off | 以便於閱讀的格式打印某個結構體變量的值,默認值為 off。該選項等同於單獨執行 set print pretty on|off 命令。 |
注意,options 參數和 /fmt 或者 expr 之間,必須用--
( 2 個 - 字符)分隔。此外,options 參數還有很多選項可以使用。
2) 和 display 命令一樣,print 命令可允許自定義輸出格式,表 2 羅列了幾個常用的 /fmt 參數。
/fmt | 功 能 |
---|---|
/x | 以十六進制的形式打印出整數。 |
/d | 以有符號、十進制的形式打印出整數。 |
/u | 以無符號、十進制的形式打印出整數。 |
/o | 以八進制的形式打印出整數。 |
/t | 以二進制的形式打印出整數。 |
/f | 以浮點數的形式打印變量或表達式的值。 |
/c | 以字符形式打印變量或表達式的值。 |
當 print 命令不指定任何 options 參數時,print 和 /fmt 之間不用添加空格,例如以十六進制的形式輸出 num 整形變量的值,執行命令為 (gdb) print/x num。
3) 我們知道,print 命令可以打印指定變量或表達式的值。當指定目標表達式時,除了表達式本身外,GDB 調試器還支持使用@
和::
運算符。
@
運算符用於輸出數組中指定區域的元素,使用格式如下:
(gdb) print first@len
其中,參數 first 用於指定數組查看區域內的首個元素的值;參數 len 用於指令自 first 元素開始查看的元素個數。
假設有一個 array 數組,其定義如下:
int array[5] = {1,2,3,4};
如果我們想查看第 1 個元素和第 2 個元素的值,可以執行如下指令:
(gdb) print array[0]@2 $1 = {1, 2}
當程序中包含多個作用域不同但名稱相同的變量或表達式時,可以借助::
運算符明確指定要查看的目標變量或表達式。::
運算符的語法格式如下:
(gdb) print file::variable
(gdb) print function::variable
其中 file 用於指定具體的文件名,funciton 用於指定具體所在函數的函數名,variable 表示要查看的目標變量或表達式。
舉個例子:
#include <stdio.h> int num = 10; int main(){ int num = 20; return 0; }
假設該程序存儲在 main.c 文件中,則使用 GDB 調試至第 5 行(return 0)處暫停后,通過執行如下命令,即可查看 num 全局變量的值:
(gdb) print 'main.c'::num $1 = 10
而通過執行如下命令,可以查看 num 局部變量的值:
(gdb) p main::num $1 = 20
當然,由於 GDB 調試就暫停在 main() 函數中,因此即便不指明main::
,這里的 num 默認指代的也是 num 局部變量。
為了讓讀者徹底搞清楚 print 命令的用法,這里以一段 C 語言程序為例,為大家演示以上所講 print 命令的用法:
(gdb) l 1 #include <stdio.h> 2 typedef struct website{ 3 char *url; 4 int time; 5 }web; 6 int num = 10; 7 int main(){ 8 int num = 20; 9 int array[10]={1,2,3,4,5,6}; 10 web LanguageC = {"http://c.biancheng.net",6}; (gdb) 11 return 0; 12 } (gdb) b 11 Breakpoint 1 at 0x11cf: file main.c, line 11. (gdb) r Starting program: ~/demo/main.exe Breakpoint 1, main () at main.c:11 11 return 0; (gdb) print -address on -- LanguageC.url <-- 打印 url 指針的同時,輸出其所在的內存地址 $1 = 0x555555556004 "http://c.biancheng.net" (gdb) print -address off -- LanguageC.url <-- 打印 url 指針,但不輸出其所在的內存地址 $2 = "http://c.biancheng.net" (gdb) print -pretty on -- LanguageC <-- 以便於閱讀的方式,輸出結構體的值 $3 = { url = 0x555555556004 "http://c.biancheng.net", time = 6 } (gdb) print LanguageC <-- 以壓縮的格式輸出結構體的值 $4 = {url = 0x555555556004 "http://c.biancheng.net", time = 6} (gdb) print/x num <-- 以十六進制的形式輸出局部變量 num 的值 $5 = 0x14 (gdb) print array[1]@2 <-- 從 array[1] 處開始,輸出 array 數組中后續的 2 個元素的值 $6 = {2, 3} (gdb) print num <-- 輸出局部變量 num 的值 $7 = 20 (gdb) print 'main.c'::num <-- 輸出全局變量 num 的值 $8 = 10 (gdb)