GDB 的 七个单步调试命令

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

调试器 是一个可以运行你的代码并检查问题的软件。​​GNU Debugger​​​(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;}

如果你在没有 调试符号debugging sysbols 的情况下进行编译(LCTT 译注:即在使用 ​​gcc​​ 编译程序时没有写 ​​-g​​ 选项),然后在 ​​bar​​ 上设置一个断点,然后尝试在这个函数内使用 ​​step​​ 来单步执行语句。GDB 会给出一个 没有行号信息no line number information

gcc exmp.c -o exmpgdb ./exmp(gdb) b barBreakpoint 1 at 0x401135(gdb) rStarting program: /home/ahajkova/exmpBreakpoint 1, 0x0000000000401135 in bar ()(gdb) stepSingle stepping until exit from function bar,which has no line number information.i = 20x0000000000401168 in main ()

2、stepi 命令

但是你仍然可以在没有行号信息的函数内部单步执行语句,但要使用 ​​stepi​​ 命令来代替 ​​step​​。​​stepi​​ 一次只执行一条指令。当使用 GDB 的 ​​stepi​​ 命令时,先做 ​​display/i $pc​​ 通常很有用,这会在每一步之后显示 程序计数器program counter 的值和相应的 机器指令machine instruction:

(gdb) b barBreakpoint 1 at 0x401135(gdb) rStarting program: /home/ahajkova/exmpBreakpoint 1, 0x0000000000401135 in bar ()(gdb) display/i $pc1: x/i $pc=> 0x401135 <bar+4>: sub $0x10,%rsp

在上述的 ​​display​​ 命令中,​​i​​ 代表机器指令,​​$pc​​ 表示程序计数器寄存器(即 PC 寄存器)。

使用 ​​info registers​​ 命令,来打印寄存器的内容,也是十分有用的。

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

3、复杂的函数调用

在带调试符号的 ​​-g​​ 选项,重新编译示例程序后,你可以使用行号在 ​​main​​ 中 ​​bar​​ 调用上设置断点,然后再单步执行 ​​bar​​ 函数的语句:

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

接下来,用 ​​step​​,来单步执行 ​​bar()​​ 函数的语句:

(gdb) stepnum () at exmp.c:44 return 2;

函数调用的参数需要在实际的函数调用之前进行处理,​​bar()​​ 函数的参数是 ​​num()​​ 函数,所以 ​​num()​​ 会在 ​​bar()​​ 被调用之前执行。但是,通过 GDB 调试,你怎么才能如愿以偿地进入 ​​bar()​​ 函数呢?你可以使用 ​​finish​​ 命令,并再次使用 ​​step​​ 命令。

(gdb) finishRun till exit from #0 num () at exmp.c:40x0000000000401161 in main () at exmp.c:1414 bar(num());Value returned is $1 = 2(gdb) stepbar (i=2) at exmp.c:99 printf("i = %d\n", i);

4、tbreak 命令

​tbreak​​ 命令会设置一个临时断点。如果你不想设置永久断点,那么这个命令是很有用的。举个例子🌰,你想进入一个复杂的函数调用,例如 ​​f(g(h()), i(j()), ...)​​,在这种情况下,你需要一个很长的 ​​step/finish/step​​ 序列,才能到达 ​​f​​ 函数。如果你设置一个临时断点,然后再使用 ​​continue​​ 命令,这样就不需要以上的序列了。为了证明这一点,你需要像以前一样将断点设置在 ​​main​​ 的 ​​bar​​ 调用上。然后在 ​​bar​​ 上设置临时断点。当到达该临时断点后,临时断点会被自动删除。

(gdb) rStarting program: /home/ahajkova/exmpBreakpoint 1, main () at exmp.c:1414 bar(num());(gdb) tbreak barTemporary breakpoint 2 at 0x40113c: file exmp.c, line 9.

在调用 ​​bar​​ 的时候遇到断点,并在 ​​bar​​ 上设置临时断点后,你只需要使用 ​​continue​​ 继续运行直到 ​​bar​​ 结束。

(gdb) continueContinuing.Temporary breakpoint 2, bar (i=2) at exmp.c:99 printf("i = %d\n", i);

5、disable 命令

类似地,你也可以在 ​​bar​​ 上设置一个正常的断点,然后执行 ​​continue​​,然后在不再需要第二个断点时,使用 ​​disable​​ 命令禁用这个断点,这样也能达到与 ​​tbreak​​ 相同的效果。

(gdb) b exmp.c:14Breakpoint 1 at 0x401157: file exmp.c, line 14.(gdb) rStarting program: /home/ahajkova/exmpBreakpoint 1, main () at exmp.c:1414 bar(num());(gdb) b barBreakpoint 2 at 0x40113c: file exmp.c, line 9.(gdb) cContinuing.Breakpoint 2, bar (i=2) at exmp.c:99 printf("i = %d\n", i);(gdb) disable 2

正如你所看到的,​​info breakpoints​​ 命令在 ​​Enb​​ 列下显示为 ​​n​​,这意味着这个断点已被禁用。但你也能在再次需要这个断点时,再启用它。

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

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

另一个进入函数内部的方法是 ​​advance​​ 命令。你可以简单地用 ​​advance bar​​,来代替 ​​tbreak bar ; continue​​。这一命令会将程序继续运行到指定的位置。

​advance​​ 命令的一个很棒的地方在于:如果程序并没有到达你试图进入的位置,那么 GDB 将在当前函数运行完成后停止。因此,程序的执行会受到限制:

Breakpoint 1 at 0x401157: file exmp.c, line 14.(gdb) rStarting program: /home/ahajkova/exmpBreakpoint 1, main () at exmp.c:1414 bar(num());(gdb) advance barbar (i=2) at exmp.c:99 printf("i = %d\n", i);

7、skip 命令

进入 ​​bar​​ 函数的另一种方式是使用 ​​skip num​​ 命令:

(gdb) b exmp.c:14Breakpoint 1 at 0x401157: file exmp.c, line 14.(gdb) skip numFunction num will be skipped when stepping.(gdb) rStarting program: /home/ahajkova/exmpBreakpoint 1, main () at exmp.c:1414 bar(num());(gdb) stepbar (i=2) at exmp.c:99 printf("i = %d\n", i);

请使用 ​​info skip​​ 命令,来了解 GDB 跳过了哪些函数。​​num()​​ 函数被标记为 ​​y​​,表示跳过了 ​​num()​​ 函数:

(gdb) info skipNum Enb Glob File RE Function1 y n <none> n num

如果不再需要 ​​skip​​,可以禁用(并稍后重新启用)或完全删除它。你可以添加另一个 ​​skip​​,并禁用第一个 ​​skip​​,然后全部删除。要禁用某个 ​​skip​​,必须指定其编号(例如,​​skip disable 1​​),如果没有指定,则会禁用所有的 ​​skip​​。启用或删除 ​​skip​​ 的工作原理相同:

(gdb) skip bar(gdb) skip disable 1(gdb) info skipNum Enb Glob File RE Function1 n n <none> n num2 y n <none> n bar(gdb) skip delete(gdb) info skipNot skipping any files or functions.

GDB 的 step 命令

使用 GDB 的 ​​step​​ 命令是调试程序的一个有用工具。即使是复杂的函数,也有几种方法可以单步调试这些函数,所以下次你在排除代码问题的时候,可以尝试一下这些 GDB 技术。

责任编辑:庞桂玉 来源: Linux中国
相关推荐

2010-11-10 15:47:59

2009-07-14 11:15:35

MyEclipse 单

2021-11-17 15:28:06

LinuxLinux命令

2023-11-06 18:06:00

Docker容器

2021-11-22 12:13:54

Linuxwget 命令

2019-07-11 15:26:50

数据中心技术数据网络

2023-12-21 18:01:58

Docker容器部署

2023-11-03 18:01:59

Docker开源平台

2010-03-04 15:00:12

Linux毁坏命令

2016-11-18 08:57:46

2009-02-03 08:23:35

Linux命令安全

2020-12-15 11:05:21

JavascriptChrome浏览器

2023-07-12 23:41:04

Linuxfind

2022-09-21 11:47:15

CIO虚假敏捷

2015-06-11 13:34:54

编程编程阶段

2022-06-15 10:24:13

Pytho装饰器代码

2024-11-06 14:26:40

2022-11-18 14:33:39

2011-03-02 09:34:58

AppFuse

2018-05-17 13:59:28

IT顾问
点赞
收藏

51CTO技术栈公众号