【調試】GDB 使用總結

啓動

在 shell 下敲 gdb 命令即可啓動 gdb,啓動後會顯示下述信息,出現 gdb 提示符。

➜  example gdb                                
GNU gdb (Ubuntu 8.1.1-0ubuntu1) 8.1.1
Copyright (C) 2018 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".
(gdb)

測試代碼

#include <stdio.h>
int minus(int a,int b){
   printf("In minus():\n"); 
   int c = a-b;
    return c;
}
int sum(int a, int b) {
    printf("In sum():\n");
 int c = a+b;
    return c;
}
void print(int xx, int *xxptr) {
  printf("In print():\n");
  printf("   xx is %d and is stored at %p.\n", xx, &xx);
  printf("   ptr points to %p which holds %d.\n", xxptr, *xxptr);
  int c = sum(2,3);
  int d = minus(3,2);
}

int main(void) {
  int x = 10;
  
  int *ptr = &x;
  printf("In main():\n");
  printf("   x is %d and is stored at %p.\n", x, &x);
  printf("   ptr points to %p which holds %d.\n", ptr, *ptr);
  
  print(x, ptr);
  return 0;
}

設置斷點

可以在函數名和行號等上設置斷點。程序運行後,到達斷點就會自動暫停運行。此時可以查看該時刻的變量值、顯示棧幀、重新設置斷點或重新運行等。斷點命令(break)可以簡寫爲 b。

格式

break 斷點

舉例

(gdb) b main
Breakpoint 1 at 0x758: file gdb_example.c, line 9.

格式

break 函數名
break 行號
break 文件名:行號
break 文件名:函數名
break  + 偏移量
break  - 偏移量
break  * 地址

舉例

(gdb) b print
Breakpoint 2 at 0x709: file gdb_example.c, line 4.
(gdb) b gdb_example.c:5
Breakpoint 3 at 0x715: file gdb_example.c, line 5.
(gdb) b +3
Note: breakpoint 2 also set at pc 0x709.
Breakpoint 4 at 0x709: file gdb_example.c, line 4.
(gdb) b *0x709
Note: breakpoints 2 and 4 also set at pc 0x709.
Breakpoint 5 at 0x709: file gdb_example.c, line 4.
(gdb)

上面的例子分別對 print 函數,gdb_example.c 第 5 行,現在暫停位置往後第 3 行,地址 0x709 設置斷點。

設置好的斷點可以通過 info break 確認

(gdb) info break
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000000758 in main at gdb_example.c:9
2       breakpoint     keep y   0x0000000000000709 in print at gdb_example.c:4
3       breakpoint     keep y   0x0000000000000715 in print at gdb_example.c:5
4       breakpoint     keep y   0x0000000000000709 in print at gdb_example.c:4
5       breakpoint     keep y   0x0000000000000709 in print at gdb_example.c:4

顯示棧幀

backtrace 命令可以在遇到斷點而暫停執行時顯示棧幀。該命令簡寫爲 bt。此外, backtrace 的別名還有 where 和 info stack(簡寫爲 info s)。

backtrace
bt

顯示所有棧幀

backtrace N
bt N

只顯示開頭 N 個棧幀

backtrace -N
bt -N

只顯示最後 N 個棧幀

backtrace full
bt full
backtrace full N
bt full N
backtrace full -N
bt full -N

舉例

(gdb) b 4
Breakpoint 1 at 0x714: file gdb_example.c, line 4.
(gdb) r
Starting program: /home/zhongyi/code/example/gdb_example 
In main():
   x is 10 and is stored at 0x7fffffffe2fc.
   ptr points to 0x7fffffffe2fc which holds 10.
In print():
   xx is 10 and is stored at 0x7fffffffe2cc.
   ptr points to 0x7fffffffe2fc which holds 10.
In sum():
In minus():

Breakpoint 1, minus (a=3, b=2) at gdb_example.c:4
4          int c = a-b;
# 顯示棧幀
(gdb) bt
#0  minus (a=3, b=2) at gdb_example.c:4
#1  0x00005555555547c0 in print (xx=10, xxptr=0x7fffffffe2fc) at gdb_example.c:17
#2  0x0000555555554841 in main () at gdb_example.c:28
#只顯示前2個棧幀
(gdb) bt 2
#0  minus (a=3, b=2) at gdb_example.c:4
#1  0x00005555555547c0 in print (xx=10, xxptr=0x7fffffffe2fc) at gdb_example.c:17
(More stack frames follow...)
# 從外向內顯示2個棧幀,及其局部變量
(gdb) bt full -2
#1  0x00005555555547c0 in print (xx=10, xxptr=0x7fffffffe2fc) at gdb_example.c:17
        c = 5
        d = 21845
#2  0x0000555555554841 in main () at gdb_example.c:28
        x = 10
        ptr = 0x7fffffffe2fc
(gdb)

顯示棧幀後,就可以確認程序在何處停止,及程序的調用路徑。

顯示變量

格式

print 變量

舉例

(gdb) p x
$1 = 10
(gdb) p ptr
$2 = (int *) 0x7fffffffe2fc
(gdb)

顯示寄存器

舉例
(gdb) info reg
rax            0xc      12
rbx            0x0      0
rcx            0x7ffff7af2104   140737348837636
rdx            0x7ffff7dcf8c0   140737351841984
rsi            0x555555756260   93824994337376
rdi            0x1      1
rbp            0x7fffffffe310   0x7fffffffe310
rsp            0x7fffffffe2f0   0x7fffffffe2f0
r8             0x7ffff7fe14c0   140737354011840
r9             0x0      0
r10            0x0      0
r11            0x246    582
r12            0x5555555545f0   93824992232944
r13            0x7fffffffe3f0   140737488348144
r14            0x0      0
r15            0x0      0
rip            0x555555554841   0x555555554841 <main+123>
eflags         0x202    [ IF ]
cs             0x33     51
ss             0x2b     43
ds             0x0      0
es             0x0      0
fs             0x0      0
gs             0x0      0

寄存器前加 $, 可以顯示寄存器的內容。

(gdb) p $rdi
$7 = 1
(gdb) p $rax
$8 = 12
(gdb)

顯示寄存器可以用以下格式

p / 格式 變量

usmOQ1

顯示內存

x 命令可以顯示內存的內容

格式
x/格式 地址
舉例
(gdb) x $r12
   0x5555555545f0 <_start>:     xor    %ebp,%ebp
(gdb) x $r8
   0x7ffff7fe14c0:      rclb   $0xf7,(%rsi,%rdi,8)
(gdb)

x/i 可以顯示彙編指令。一般用 x 命令時,格式爲 x/NFU ADDR。此處 ADDR 爲希望顯示的地址,N 爲重複次數。F 爲前面講過的格式,u 代表的單位如下。

zeiDIM

下面顯示從 rsp 開始的 10 條指令。

(gdb) x/10i $rsp
   0x7fffffffe2f0:      (bad)  
   0x7fffffffe2f1:      rex.W push %rbp
   0x7fffffffe2f3:      push   %rbp
   0x7fffffffe2f4:      push   %rbp
   0x7fffffffe2f5:      push   %rbp
   0x7fffffffe2f6:      add    %al,(%rax)
   0x7fffffffe2f8:      lock rex.RB push %r13
   0x7fffffffe2fb:      push   %rbp
   0x7fffffffe2fc:      or     (%rax),%al
   0x7fffffffe2fe:      add    %al,(%rax)

顯示反彙編

格式
disassemble
disassemble 程序計數器
disassemble 開始地址 結束地址

格式 1 爲反彙編當前整個函數,2 爲反彙編程序計數器所在函數的整個函數。3 爲反彙編從開始地址到結束地址的部分。

(gdb) disassemble 
Dump of assembler code for function sum:
   0x0000555555554722 <+0>:     push   %rbp
   0x0000555555554723 <+1>:     mov    %rsp,%rbp
   0x0000555555554726 <+4>:     sub    $0x20,%rsp
   0x000055555555472a <+8>:     mov    %edi,-0x14(%rbp)
   0x000055555555472d <+11>:    mov    %esi,-0x18(%rbp)
   0x0000555555554730 <+14>:    lea    0x1bd(%rip),%rdi        # 0x5555555548f4
   0x0000555555554737 <+21>:    callq  0x5555555545b0 <puts@plt>
=> 0x000055555555473c <+26>:    mov    -0x14(%rbp),%edx
   0x000055555555473f <+29>:    mov    -0x18(%rbp),%eax
   0x0000555555554742 <+32>:    add    %edx,%eax
   0x0000555555554744 <+34>:    mov    %eax,-0x4(%rbp)
   0x0000555555554747 <+37>:    mov    -0x4(%rbp),%eax
   0x000055555555474a <+40>:    leaveq 
   0x000055555555474b <+41>:    retq   
End of assembler dump.

單步執行

執行源代碼中的一行:next
進入函數內部執行:step
逐條執行彙編指令:nexti,stepi

繼續運行

格式

continue
continue 次數

指定次數可以忽略斷點,例如,continue 5 則 5 次遇到斷點不會停止,第 6 次遇到斷點纔會停止。

監視點

格式

watch <表達式>

<表達式> 發生變化時暫停運行,< 表達式 > 意思是常量或變量

awatch <表達式>

<表達式> 被訪問,改變時暫停運行

rwatch <表達式>

<表達式> 被訪問時暫停運行

舉例

(gdb) watch c
Hardware watchpoint 2: c
(gdb) c
Continuing.

Hardware watchpoint 2: c

Old value = 21845
New value = 5
sum (a=2, b=3) at gdb_example.c:10
10          return c;
(gdb)

格式

刪除斷點和監視點

delete <編號>

<編號> 指的是斷點或監視點

舉例

(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x000055555555473c in sum at gdb_example.c:9
        breakpoint already hit 1 time
2       hw watchpoint  keep y                      c
        breakpoint already hit 1 time
(gdb) delete  2
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x000055555555473c in sum at gdb_example.c:9
        breakpoint already hit 1 time
(gdb)

改變變量的值

格式

set variable <變量>=<表達式>

舉例

(gdb) p c
$1 = 5
(gdb) set variable c=0
(gdb) p c
$2 = 0
(gdb)

生成內核轉儲文件

(gdb) generate-core-file 
warning: Memory read failed for corefile section, 4096 bytes at 0xffffffffff600000.
Saved corefile core.2380

有了內核轉儲文件,即使退出了 GDB 也能查看生成轉儲文件時的運行歷史。

gcore 'pidof gdb_example'

該命令無需停止正在運行的程序,可以直接從命令行直接生成轉儲文件。當需要在其他機器上單獨分析問題原因時,或者是分析客戶現場問題時十分有用。

條件斷點

break 斷點 if 條件

如果條件爲真,則暫停運行

condition 斷點編號
condition 斷點編號 條件

第一條指令刪除指定斷點編號的觸發條件,第二條指令給斷點添加觸發條件

反覆執行

ignore 斷點編號 次數

在編號指定的斷點,監視點忽略指定的次數

continue 與 ignore 一樣,也可以指定次數,達到指定次數前,執行到斷點時不暫停。

continue次數
step 次數
stepi 次數
next 次數
nexti 次數
finish
until
until 地址

finish 執行完當前函數後暫停,until 命令執行完當前函數等代碼塊後暫停,常用於跳出循環。、

刪除斷點或禁用斷點

clear 
clear 函數名
clear 行號
clear 文件名:行號
clear 文件名:函數名
delete [breakpoints] 斷點編號

clear 用於刪除已定義的斷點

disable [breakpoints] 
disable [breakpoints] 斷點編號
disable display 顯示編號
disable mem 內存區域

disable 臨時禁用斷點。第 3 種格式禁用 display 命令定義的自動顯示,第 4 種格式禁用 mem 命令定義的內存區域。

enable
enable [breakpoints] 斷點編號
enable [breakpoints] once 斷點編號
enable [breakpoints] delete 斷點編號
enable disable display 顯示編號
enable  mem 內存區域

once 使指定的斷點只啓用一次。delete 表示在運行暫停後刪除斷點。

斷點命令

格式

commands 斷點編號
 命令
 ...
 end

程序在指定的斷點處暫停,就會自動執行命令。

舉例

(gdb) b 17
Breakpoint 3 at 0x5555555547b1: file gdb_example.c, line 17.
(gdb) command 3
Type commands for breakpoint(s) 3, one per line.
End with a line saying just "end".
>p c
>end
(gdb) r
Starting program: /home/zhongyi/code/example/gdb_example -e 'p 1'
In main():
   x is 10 and is stored at 0x7fffffffe2ec.
   ptr points to 0x7fffffffe2ec which holds 10.
In print():
   xx is 10 and is stored at 0x7fffffffe2bc.
   ptr points to 0x7fffffffe2ec which holds 10.
In sum():

Breakpoint 3, print (xx=10, xxptr=0x7fffffffe2ec) at gdb_example.c:17
17        int d = minus(3,2);
$1 = 5

上例表示在 17 行暫停後打印 c 的值。

與前面的條件斷點組合使用,可以在斷點暫停時執行復雜的動作。

舉例

break 17 if c==5
 commands
 silent
 printf “x is %d\n”,x
 cont
 end

常用命令及其縮略形式

la37U9

值的歷史

通過 print 命令顯示過的值會記錄在內部的值歷史中,這些值可以在其他表達式中使用。

舉例

(gdb) b 16
Breakpoint 1 at 0x79f: file gdb_example.c, line 16.
(gdb) b 17
Breakpoint 2 at 0x7b1: file gdb_example.c, line 17.
(gdb) b 29
Breakpoint 3 at 0x841: file gdb_example.c, line 29.
(gdb) r
Starting program: /home/zhongyi/code/example/gdb_example 
In main():
   x is 10 and is stored at 0x7fffffffe2fc.
   ptr points to 0x7fffffffe2fc which holds 10.
In print():
   xx is 10 and is stored at 0x7fffffffe2cc.
   ptr points to 0x7fffffffe2fc which holds 10.

Breakpoint 1, print (xx=10, xxptr=0x7fffffffe2fc) at gdb_example.c:16
16        int c = sum(2,3);
(gdb) p c
$1 = 1431651824
(gdb) c
Continuing.
In sum():

Breakpoint 2, print (xx=10, xxptr=0x7fffffffe2fc) at gdb_example.c:17
17        int d = minus(3,2);
(gdb) p c
$2 = 5
(gdb) c
Continuing.
In minus():

Breakpoint 3, main () at gdb_example.c:29
29        return 0;

最後的值可以使用 $ 訪問。

通過 show values 可以顯示歷史中的最後 10 個值

舉例

(gdb) show values 
$1 = 1431651824
$2 = 5
$3 = 10
$4 = 10
(gdb)

值的歷史的訪問變量和說明

D9LvzS

可以隨意定義變量。變量以 $ 開頭,有英文和數字組成。

舉例

(gdb) set $i=0
(gdb) p $i
$5 = 0
(gdb)

命令歷史

可以把命令保存在文件中,保存命令歷史後,就可以在其他調試會話中使用。默認命令歷史文件位於./.gdb_history

set history expansion
show history expansion

可以使用 csh 風格的! 字符

set history filename 文件名
show history filename

可將命令歷史保存到文件中,可以通過環境變量 GDBHISTFILE 改變默認文件。

set history save
show history save

啓用命令歷史保存到文件和恢復的功能。

set history size 數字
show history size

設置保存到命令歷史中的命令數量,默認爲 256。

初始化文件(.gdbinit)

Linux 下 gdb 初始化文件爲. gdbinit。如果存在. gdbinit 文件,GDB 在啓動之前將其作爲命令文件運行。

順序如下:

  1. $HOME/.gdbinit

  2. 運行命令行選項

  3. ./.gdbinit

  4. 加載通過 - x 選項給出的命令文件

命令定義

用 define 可以自定義命令,用 document 可以給自定義的命令加說明,利用 help 命令名可以查看定義的命令。

define 格式:

define 命令名
 命令
 …………
 end

document 格式:

document 命令名
 說明
 end

help 格式:

help 命令名

以下示例定義了名爲 li 的命令。

舉例

(gdb) define li
Type commands for definition of "li".
End with a line saying just "end".
>x/10i $rbp
>end
(gdb) document li
Type documentation for "li".
End with a line saying just "end".
>list machine instruction
>end
(gdb) li
   0x7fffffffe310:      (bad)  
   0x7fffffffe311:      rex.W push %rbp
   0x7fffffffe313:      push   %rbp
   0x7fffffffe314:      push   %rbp
   0x7fffffffe315:      push   %rbp
   0x7fffffffe316:      add    %al,(%rax)
   0x7fffffffe318:      xchg   %edi,(%rax,%riz,4)
   0x7fffffffe31b:      idiv   %edi
   0x7fffffffe31d:      jg     0x7fffffffe31f
   0x7fffffffe31f:      add    %al,(%rcx)
(gdb) help li
list machine instruction

還可以把各種設置寫在文件中,運行調試器時讀取這些文件。

source 文件名

總結

本文只是對 gdb 命令腳本做了一個粗淺的介紹,旨在起到拋磚引玉的效果。如果大家想更深入地瞭解這部分知識,可以參考 gdb 手冊的相關章節:Extending GDB (https://sourceware.org/gdb/onlinedocs/gdb/Extending-GDB.html)。

最後向大家推薦一個 github 上的. gdbinit 文件:https://github.com/gdbinit/Gdbinit,把這個弄懂,相信 gdb 腳本文件就不在話下了。

文章推薦:https://blog.csdn.net/lyshark_lyshark/article/details/125846778

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/1tnaQuZt7ak7pWAaWGDw4g