Linux中國

GDB 的 7 個單步調試命令

即使是複雜的函數,也有幾種方法可以單步調試,所以下次在排除代碼故障時,可以嘗試一下這些 GDB 技術。

調試 是一個可以運行你的代碼並檢查問題的軟體。GNU Debugger(GBD)是最流行的調試器之一,在這篇文章中,我研究了 GDB 的 step 命令和其他幾種常見情況的相關命令。step 是一個被廣泛使用的命令,但它有一些人們不太了解的地方,可能會使得他們十分困惑。此外,還有一些方法可以在不使用 step 命令的情況下進入一個函數,比如使用不太知名的 advance 命令。

1、無調試符號

考慮以下這個簡單的示常式序:

#include <stdio.h>

int num() {
    return 2;
}

void bar(int i) {
    printf("i = %dn", i);
}

int main() {
    bar(num());
    return 0;
}

如果你在沒有 調試符號 debugging sysbols 的情況下進行編譯(LCTT 譯註:即在使用 gcc 編譯程序時沒有寫 -g 選項),然後在 bar 上設置一個斷點,然後嘗試在這個函數內使用 step 來單步執行語句。GDB 會給出一個 沒有行號信息 no line number information 的錯誤信息。

gcc exmp.c -o exmp
gdb ./exmp
(gdb) b bar
Breakpoint 1 at 0x401135
(gdb) r
Starting program: /home/ahajkova/exmp
Breakpoint 1, 0x0000000000401135 in bar ()
(gdb) step
Single stepping until exit from function bar,
which has no line number information.
i = 2
0x0000000000401168 in main ()

2、stepi 命令

但是你仍然可以在沒有行號信息的函數內部單步執行語句,但要使用 stepi 命令來代替 stepstepi 一次只執行一條指令。當使用 GDB 的 stepi 命令時,先做 display/i $pc 通常很有用,這會在每一步之後顯示 程序計數器 program counter 的值和相應的 機器指令 machine instruction

(gdb) b bar
Breakpoint 1 at 0x401135
(gdb) r
Starting program: /home/ahajkova/exmp
Breakpoint 1, 0x0000000000401135 in bar ()
(gdb) display/i $pc
1: x/i $pc
=> 0x401135 <bar+4>: sub $0x10,%rsp

在上述的 display 命令中,i 代表機器指令,$pc 表示程序計數器寄存器(即 PC 寄存器)。

使用 info registers 命令,來列印寄存器的內容,也是十分有用的。

(gdb) info registers
rax 0x2 2
rbx 0x7fffffffdbc8 140737488346056
rcx 0x403e18 4210200
(gdb) print $rax
$1 = 2
(gdb) stepi
0x0000000000401139 in bar ()
1: x/i $pc
=> 0x401139 <bar+8>: mov %edi,-0x4(%rbp)

3、複雜的函數調用

在帶調試符號的 -g 選項,重新編譯示常式序後,你可以使用行號在 mainbar 調用上設置斷點,然後再單步執行 bar 函數的語句:

gcc -g exmp.c -o exmp
gdb ./exmp
(gdb) b exmp.c:14
Breakpoint 1 at 0x401157: file exmp.c, line 14.
(gdb) r
Starting program: /home/ahajkova/exmp
Breakpoint 1, main () at exmp.c:14
14 bar(num());

接下來,用 step,來單步執行 bar() 函數的語句:

(gdb) step
num () at exmp.c:4
4 return 2;

函數調用的參數需要在實際的函數調用之前進行處理,bar() 函數的參數是 num() 函數,所以 num() 會在 bar() 被調用之前執行。但是,通過 GDB 調試,你怎麼才能如願以償地進入 bar() 函數呢?你可以使用 finish 命令,並再次使用 step 命令。

(gdb) finish
Run till exit from #0 num () at exmp.c:4
0x0000000000401161 in main () at exmp.c:14
14 bar(num());
Value returned is $1 = 2
(gdb) step
bar (i=2) at exmp.c:9
9 printf("i = %dn", i);

4、tbreak 命令

tbreak 命令會設置一個臨時斷點。如果你不想設置永久斷點,那麼這個命令是很有用的。舉個例子?,你想進入一個複雜的函數調用,例如 f(g(h()), i(j()), ...),在這種情況下,你需要一個很長的 step/finish/step 序列,才能到達 f 函數。如果你設置一個臨時斷點,然後再使用 continue 命令,這樣就不需要以上的序列了。為了證明這一點,你需要像以前一樣將斷點設置在 mainbar 調用上。然後在 bar 上設置臨時斷點。當到達該臨時斷點後,臨時斷點會被自動刪除。

(gdb) r
Starting program: /home/ahajkova/exmp
Breakpoint 1, main () at exmp.c:14
14 bar(num());
(gdb) tbreak bar
Temporary breakpoint 2 at 0x40113c: file exmp.c, line 9.

在調用 bar 的時候遇到斷點,並在 bar 上設置臨時斷點後,你只需要使用 continue 繼續運行直到 bar 結束。

(gdb) continue
Continuing.
Temporary breakpoint 2, bar (i=2) at exmp.c:9
9 printf("i = %dn", i);

5、disable 命令

類似地,你也可以在 bar 上設置一個正常的斷點,然後執行 continue,然後在不再需要第二個斷點時,使用 disable 命令禁用這個斷點,這樣也能達到與 tbreak 相同的效果。

(gdb) b exmp.c:14
Breakpoint 1 at 0x401157: file exmp.c, line 14.
(gdb) r
Starting program: /home/ahajkova/exmp
Breakpoint 1, main () at exmp.c:14
14 bar(num());
(gdb) b bar
Breakpoint 2 at 0x40113c: file exmp.c, line 9.
(gdb) c
Continuing.
Breakpoint 2, bar (i=2) at exmp.c:9
9 printf("i = %dn", i);
(gdb) disable 2

正如你所看到的,info breakpoints 命令在 Enb 列下顯示為 n,這意味著這個斷點已被禁用。但你也能在再次需要這個斷點時,再啟用它。

(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000401157 in main at exmp.c:14
breakpoint already hit 1 time
2 breakpoint keep n 0x000000000040113c in bar at exmp.c:9
breakpoint already hit 1 time
(gdb) enable 2
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000040116a in main at exmp.c:19
breakpoint already hit 1 time
2 breakpoint keep y 0x0000000000401158 in bar at exmp.c:14
breakpoint already hit 1 time

6、advance 命令運行程序到指定的位置

另一個進入函數內部的方法是 advance 命令。你可以簡單地用 advance bar,來代替 tbreak bar ; continue。這一命令會將程序繼續運行到指定的位置。

advance 命令的一個很棒的地方在於:如果程序並沒有到達你試圖進入的位置,那麼 GDB 將在當前函數運行完成後停止。因此,程序的執行會受到限制:

Breakpoint 1 at 0x401157: file exmp.c, line 14.
(gdb) r
Starting program: /home/ahajkova/exmp
Breakpoint 1, main () at exmp.c:14
14 bar(num());
(gdb) advance bar
bar (i=2) at exmp.c:9
9 printf("i = %dn", i);

7、skip 命令

進入 bar 函數的另一種方式是使用 skip num 命令:

(gdb) b exmp.c:14
Breakpoint 1 at 0x401157: file exmp.c, line 14.
(gdb) skip num
Function num will be skipped when stepping.
(gdb) r
Starting program: /home/ahajkova/exmp
Breakpoint 1, main () at exmp.c:14
14 bar(num());
(gdb) step
bar (i=2) at exmp.c:9
9 printf("i = %dn", i);

請使用 info skip 命令,來了解 GDB 跳過了哪些函數。num() 函數被標記為 y,表示跳過了 num() 函數:

(gdb) info skip
Num Enb Glob File RE Function
1 y n <none> n num

如果不再需要 skip,可以禁用(並稍後重新啟用)或完全刪除它。你可以添加另一個 skip,並禁用第一個 skip,然後全部刪除。要禁用某個 skip,必須指定其編號(例如,skip disable 1),如果沒有指定,則會禁用所有的 skip。啟用或刪除 skip 的工作原理相同:

(gdb) skip bar
(gdb) skip disable 1
(gdb) info skip
Num Enb Glob File RE Function
1 n n <none> n num
2 y n <none> n bar
(gdb) skip delete
(gdb) info skip
Not skipping any files or functions.

GDB 的 step 命令

使用 GDB 的 step 命令是調試程序的一個有用工具。即使是複雜的函數,也有幾種方法可以單步調試這些函數,所以下次你在排除代碼問題的時候,可以嘗試一下這些 GDB 技術。

via: https://opensource.com/article/22/12/gdb-step-command

作者:Alexandra 選題:lkxed 譯者:chai001125 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的電子郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國