---
author: Alexandra
category: 软件开发
comments_data: []
count:
  commentnum: 0
  favtimes: 0
  likes: 0
  sharetimes: 0
  viewnum: 2754
date: '2022-12-19 09:38:32'
editorchoice: false
excerpt: 即使是复杂的函数，也有几种方法可以单步调试，所以下次在排除代码故障时，可以尝试一下这些 GDB 技术。
fromurl: https://opensource.com/article/22/12/gdb-step-command
id: 15362
islctt: true
largepic: /images/p/202212/19/093831nrjrmozx1mixmgii.jpg
permalink: /article-15362-1.html
pic: /images/p/202212/19/093831nrjrmozx1mixmgii.jpg.thumb.jpg
related: []
reviewer: wxy
selector: lkxed
summary: 即使是复杂的函数，也有几种方法可以单步调试，所以下次在排除代码故障时，可以尝试一下这些 GDB 技术。
tags:
- GDB
- 调试
thumb: false
title: GDB 的 7 个单步调试命令
titlepic: true
translator: chai001125
updated: '2022-12-19 09:38:32'
---

![](/images/p/202212/19/093831nrjrmozx1mixmgii.jpg)



> 
> 即使是复杂的函数，也有几种方法可以单步调试，所以下次在排除代码故障时，可以尝试一下这些 GDB 技术。
> 
> 
> 


**调试器** 是一个可以运行你的代码并检查问题的软件。[GNU Debugger](https://opensource.com/article/21/3/debug-code-gdb)（GBD）是最流行的调试器之一，在这篇文章中，我研究了 GDB 的 `step` 命令和其他几种常见情况的相关命令。`step` 是一个被广泛使用的命令，但它有一些人们不太了解的地方，可能会使得他们十分困惑。此外，还有一些方法可以**在不使用 `step` 命令的情况下进入一个函数**，比如使用不太知名的 `advance` 命令。


### 1、无调试符号


考虑以下这个简单的示例程序：



```
#include <stdio.h>


int num() {
    return 2;
}

void bar(int i) {
    printf("i = %d\n", i);
}


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

```

如果你在没有 <ruby> 调试符号 <rt>  debugging sysbols </rt></ruby> 的情况下进行编译（LCTT 译注：即在使用 `gcc` 编译程序时没有写 `-g` 选项），然后在 `bar` 上设置一个断点，然后尝试在这个函数内使用 `step` 来单步执行语句。GDB 会给出一个 <ruby> 没有行号信息 <rt>  no line number information </rt></ruby> 的错误信息。



```
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` 命令来代替 `step`。`stepi` 一次只执行一条指令。当使用 GDB 的 `stepi` 命令时，先做 `display/i $pc` 通常很有用，这会在每一步之后**显示** <ruby> 程序计数器 <rt>  program counter </rt></ruby> 的值和**相应的** <ruby> 机器指令 <rt>  machine instruction </rt></ruby>：



```
(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` 选项，重新编译示例程序后，你可以使用行号在 `main` 中 `bar` 调用上设置断点，然后再单步执行 `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 = %d\n", i);

```

### 4、tbreak 命令


`tbreak` 命令会设置一个**临时断点**。如果你不想设置永久断点，那么这个命令是很有用的。举个例子?，你想进入一个复杂的函数调用，例如 `f(g(h()), i(j()), ...)`，在这种情况下，你需要一个很长的 `step/finish/step` 序列，才能到达 `f` 函数。如果你设置一个临时断点，然后再使用 `continue` 命令，这样就不需要以上的序列了。为了证明这一点，你需要像以前一样将断点设置在 `main` 的 `bar` 调用上。然后在 `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 = %d\n", 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 = %d\n", 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 = %d\n", 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 = %d\n", 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](https://opensource.com/users/ahajkova) 选题：[lkxed](https://github.com/lkxed) 译者：[chai001125](https://github.com/chai001125) 校对：[wxy](https://github.com/wxy)


本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出