Linux下gdb的安裝及使用入門
1、安裝gdb。
在root使用者許可權下:
[email protected]:~# apt-get update ...... ...... ...... [email protected]:~# apt-get install gdb ...... ...... ...... Do you want to continue? [Y/n] y ...... ...... ...... [email protected]:~#
安裝好gdb了。
2、gdb的簡單使用。
用root許可權的Terminal(或一般許可權的Terminal)的vi編輯器編寫一個C程式a.c:
1 #include <stdio.h> 2 3 int main() 4 { 5 int a = 1; 6 int b = a; 7 8 printf("a = %d, b =%d\n", a, b); 9 10 return 0; 11 }
(1) 在可執行檔案中加入原始碼資訊
這個過程通過gcc來完成:
gcc –o a a.c -g
-o選項的作用是:對命令輸出結果進行匯入操作,這裡是把gcc –o a a.c -g的操作結果輸出到檔案a(檔名可以自定義)中進行儲存。
-g選項的作用是:在可執行檔案中加入原始碼資訊,比如:可執行檔案中第幾條機器指令對應原始碼的第幾行,但並不是把整個原始檔都嵌入到可執行檔案中,而是在除錯時必須保證gdb能找到原始檔。
(2) 進入gdb
[email protected]:~/2/02# gcc -o a a.c -g [email protected]:~/2/02# gdb a GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1 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 a...done. (gdb)
如下圖所示:
gdb提供一個類似Shell的命令列環境,上面的(gdb)就是提示符,在提示符後面輸入gdb的相應命令就可以實現其對應的功能。
(3) gdb除錯常用命令
[1] start
用start命令開始執行程式:
(gdb) start Temporary breakpoint 1 at 0x40052e: file a.c, line 5. Starting program: /root/2/02/a Temporary breakpoint 1, main () at a.c:5 5 int a = 1; (gdb)
gdb提示準備執行a.c程式的第六行程式碼。然後繼續用(gdb)提示需要輸入的命令。
[2] 單步執行(n)
(gdb) start Temporary breakpoint 1 at 0x40052e: file a.c, line 5. Starting program: /root/2/02/a Temporary breakpoint 1, main () at a.c:5 5 int a = 1; (gdb) n 6 int b = a; (gdb) n 8 printf("a = %d, b = %d\n", a, b); (gdb) n a = 1, b = 1 9 return 0; (gdb) quit A debugging session is active. Inferior 1 [process 22935] will be killed. Quit anyway? (y or n) y [email protected]:~/2/02#
在start命令後,每輸入一個n就能夠單步執行一條語句(輸入一個命令後,直接回車表示最近輸入命令的含義)。當程式執行完時,可以輸入quit命令來退出gdb模式。
[3] gdb斷點除錯
[ breakpoint,continue和display ]
(gdb) start Temporary breakpoint 1 at 0x40052e: file a.c, line 5. Starting program: /root/2/02/a Temporary breakpoint 1, main () at a.c:5 5 int a = 1; (gdb) b 8 Breakpoint 2 at 0x40053b: file a.c, line 8. (gdb) c Continuing. Breakpoint 2, main () at a.c:8 8 printf("a = %d, b = %d\n", a, b); (gdb) display b 1: b = 1 (gdb) n a = 1, b = 1 9 return 0; 1: b = 1 (gdb) 10 } 1: b = 1 (gdb) quit [email protected]:~/2/02#
gdb a會進入a可執行程式的gdb模式,start命令就使程式準備執行程式中的第一條語句。b 8是breakpoint 8的簡寫(breakpoint的引數也可以以是某個函式名,表示在此函式處設定一個斷點),表示在程式第八行設定一個斷點。c是continue的縮寫,表示繼續執行程式,程式會在設定斷點處停下來。displayb表示將b的值顯示出來(undisplay取消對變數的跟蹤),然後再輸入單步除錯命令n(next)就可以使程式繼續執行。
可見斷點有助於快速跳過沒有問題的程式碼,然後在有問題的程式碼上慢慢走慢慢分析,“斷點加單步”是使用偵錯程式的基本方法。至於應該在哪裡設定斷點,怎麼知道哪些程式碼可以跳過,而哪些程式碼要慢慢走,也要通過對錯誤現象的分析和假設來確定,以前我們用printf列印中間結果時,也要分析應該在哪裡插入printf,列印哪些中間結果,除錯的基本思路是一樣的。
[4]info
一次除錯可以設定多個斷點,用info命令可以檢視已經設定的斷點:
[email protected]:~/2/02# gdb a GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1 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 a...done. (gdb) start Temporary breakpoint 1 at 0x40052e: file a.c, line 5. Starting program: /root/2/02/a Temporary breakpoint 1, main () at a.c:5 5 int a = 1; (gdb) b 7 Breakpoint 2 at 0x40053b: file a.c, line 7. (gdb) b 8 Note: breakpoint 2 also set at pc 0x40053b. Breakpoint 3 at 0x40053b: file a.c, line 8. (gdb) i breakpoints Num Type Disp Enb Address What 2 breakpoint keep y 0x000000000040053b in main at a.c:7 3 breakpoint keep y 0x000000000040053b in main at a.c:8 (gdb)
[5]delete
每個斷點都有一個編號(有的斷點行數不一樣,但地址卻一樣,有的地方不能夠設定斷點或者說與上一個設定的斷點等效),可以用編號指定刪除某個斷點。
...... (gdb) b 7 Breakpoint 2 at 0x40053b: file a.c, line 7. (gdb) b 8 Note: breakpoint 2 also set at pc 0x40053b. Breakpoint 3 at 0x40053b: file a.c, line 8. (gdb) i breakpoints Num Type Disp Enb Address What 2 breakpoint keep y 0x000000000040053b in main at a.c:7 3 breakpoint keep y 0x000000000040053b in main at a.c:8 (gdb) delete 3 (gdb) i breakpoints Num Type Disp Enb Address What 2 breakpoint keep y 0x000000000040053b in main at a.c:7 (gdb)
有時候一個斷點暫時不用可以禁用掉而不必刪除,這樣以後想用的時候可以直接啟用,而不必重新從程式碼裡找應該在哪一行設斷點,這個過程用 disable 和 enable 來完成。
[6]條件斷點 (break 和run)
gdb的斷點功能非常靈活,還可以設定斷點在滿足某個條件時才啟用,例如:
...... //先把其餘的斷點刪掉。 (gdb) b 9 if a == 2 Breakpoint 5 at 0x400552: file a.c, line 9. (gdb) i breakpoints Num Type Disp Enb Address What 5 breakpoint keep y 0x0000000000400552 in main at a.c:9 stop only if a == 2 (gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /root/2/02/a a = 1, b = 1 [Inferior 1 (process 22968) exited normally] (gdb)
r表示從頭開始執行程式,在a==2的條件下中斷才有效。a不等於2,所以中斷無效。
[7] gdb的觀察點(watch 和c)
斷點是當程式執行到某一程式碼行時中斷,而觀察點是當程式訪問某個儲存單元時中斷,如果我們不知道某個儲存單元是在哪裡被改動的,這時候觀察點尤其有用。
[email protected]:~/2/02# gdb a GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1 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 a...done. (gdb) start Temporary breakpoint 1 at 0x40052e: file a.c, line 5. Starting program: /root/2/02/a Temporary breakpoint 1, main () at a.c:5 5 int a = 1; (gdb) watch b Hardware watchpoint 2: b (gdb) c Continuing. Hardware watchpoint 2: b Old value = 0 New value = 1 main () at a.c:8 8 printf("a = %d, b = %d\n", a, b); (gdb)
程式執行到b儲存單元,將此執行單元執行前後的值都顯示出來。
[8] 段錯誤
如果程式執行時出現段錯誤,用gdb可以很容易定位到究竟是哪一行引發的段錯誤。在gdb中執行,遇到段錯誤會自動停下來,這時可以用命令檢視當前執行到哪一行程式碼了。
gdb顯示段錯誤出現在 _IO_vfscanf 函式中,用bt命令可以看到是哪一個函式呼叫了它。
[9] gdb基本命令
gdb有許多有用的命令如list(顯示原始碼),這樣就可以結合原始碼與除錯資訊更好的進行除錯。將gdb常用命令摘抄如下表:
命令 |
描述 |
backtrace(bt) |
檢視各級函式呼叫及引數 |
finish |
連續執行到當前函式返回為止,然後停下來等待命令 |
frame(f) 幀編號 |
選擇棧幀 |
info(i) locals |
檢視當前棧幀區域性變數的值 |
list(l) |
列出原始碼,接著上次的位置往下列,每次列十行 |
list 行號 |
列出第幾行開始的原始碼 |
list 函式名 |
列出某個函式的原始碼 |
next(n) |
執行下一行語句 |
print(p) |
打印表達式的值,通過表示式的值可以修改變數的值或者呼叫函式 |
quit(q) |
退出gdb除錯環境 |
set var |
修改變數的值 |
start |
開始執行程式,停在main函式第一行語句前面等待命令 |
step(s) |
執行下一行語句,如果有函式則進入到函式中 |
break(b) 行號 |
在某一行設定斷點 |
break 函式名 |
在某個函式開頭設定斷點 |
break(b)… if… |
設定條件斷點 |
continue(c) |
從當前位置開始連續執行程式 |
delete breakpoints 斷點號 |
刪掉此號的斷點 |
display 變數名 |
跟蹤檢視某個變數,每次停下來都顯示它的值 |
disable breakpoints 斷點號 |
禁用此斷點 |
enable 斷點號 |
啟用此斷點 |
info(i) breakpoints |
檢視當前設定了哪些斷點 |
run(r) |
從頭開始連續執行程式 |
undisplay 跟蹤顯示行號 |
取消跟蹤顯示 |
watch |
設定觀察點 |
info(i) watchpoints |
檢視當前設定了哪些觀察點 |
x |
從某個位置開始列印儲存單元的內容,全部當成位元組來看,而不區分哪個位元組屬於哪個變數 |
disassemble |
反彙編當前函式或者指定的函式,單獨用disassemble命令是反彙編當前函式,如果disassemble命令後面跟函式名或地址則反彙編指定的函式。 |
si |
可以一條指令一條指令地單步除錯。 |
info registers |
可以顯示所有暫存器的當前值。在gdb中表示暫存器名時前面要加個$,例如p $esp可以列印esp暫存器的值。 |
set follow-fork-mode child/parent | 設定gdb在fork之後跟蹤子程序/父程序 |
set args 'command-line' | 給執行的程式傳命令列引數 |
s(stepin) | 進入子函式 |