Redian新闻
>
GDB 的 7 个单步调试命令 | Linux 中国

GDB 的 7 个单步调试命令 | Linux 中国

科技
 
导读:即使是复杂的函数,也有几种方法可以单步调试,所以下次在排除代码故障时,可以尝试一下这些 GDB 技术。
本文字数:6071,阅读时长大约:7分钟

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

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

1、无调试符号

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

  1. #include <stdio.h>
  2. int num() {
  3. return 2;
  4. }
  5. void bar(int i) {
  6. printf("i = %d\n", i);
  7. }
  8. int main() {
  9. bar(num());
  10. return 0;
  11. }

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

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

2、stepi 命令

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

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

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

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

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

3、复杂的函数调用

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

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

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

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

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

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

4、tbreak 命令

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

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

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

  1. (gdb) continue
  2. Continuing.
  3. Temporary breakpoint 2, bar (i=2) at exmp.c:9
  4. 9 printf("i = %d\n", i);

5、disable 命令

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

  1. (gdb) b exmp.c:14
  2. Breakpoint 1 at 0x401157: file exmp.c, line 14.
  3. (gdb) r
  4. Starting program: /home/ahajkova/exmp
  5. Breakpoint 1, main () at exmp.c:14
  6. 14 bar(num());
  7. (gdb) b bar
  8. Breakpoint 2 at 0x40113c: file exmp.c, line 9.
  9. (gdb) c
  10. Continuing.
  11. Breakpoint 2, bar (i=2) at exmp.c:9
  12. 9 printf("i = %d\n", i);
  13. (gdb) disable 2

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

  1. (gdb) info breakpoints
  2. Num Type Disp Enb Address What
  3. 1 breakpoint keep y 0x0000000000401157 in main at exmp.c:14
  4. breakpoint already hit 1 time
  5. 2 breakpoint keep n 0x000000000040113c in bar at exmp.c:9
  6. breakpoint already hit 1 time
  7. (gdb) enable 2
  8. (gdb) info breakpoints
  9. Num Type Disp Enb Address What
  10. 1 breakpoint keep y 0x000000000040116a in main at exmp.c:19
  11. breakpoint already hit 1 time
  12. 2 breakpoint keep y 0x0000000000401158 in bar at exmp.c:14
  13. breakpoint already hit 1 time

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

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

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

  1. Breakpoint 1 at 0x401157: file exmp.c, line 14.
  2. (gdb) r
  3. Starting program: /home/ahajkova/exmp
  4. Breakpoint 1, main () at exmp.c:14
  5. 14 bar(num());
  6. (gdb) advance bar
  7. bar (i=2) at exmp.c:9
  8. 9 printf("i = %d\n", i);

7、skip 命令

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

  1. (gdb) b exmp.c:14
  2. Breakpoint 1 at 0x401157: file exmp.c, line 14.
  3. (gdb) skip num
  4. Function num will be skipped when stepping.
  5. (gdb) r
  6. Starting program: /home/ahajkova/exmp
  7. Breakpoint 1, main () at exmp.c:14
  8. 14 bar(num());
  9. (gdb) step
  10. bar (i=2) at exmp.c:9
  11. 9 printf("i = %d\n", i);

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

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

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

  1. (gdb) skip bar
  2. (gdb) skip disable 1
  3. (gdb) info skip
  4. Num Enb Glob File RE Function
  5. 1 n n <none> n num
  6. 2 y n <none> n bar
  7. (gdb) skip delete
  8. (gdb) info skip
  9. 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中国 荣誉推出

LCTT 译者 :chai001125
🌟🌟🌟
翻译: 23.0 篇
|
贡献: 71 天
2022-10-06
2022-12-15
https://linux.cn/lctt/chai001125
欢迎遵照 CC-BY-SA 协议规定转载,
如需转载,请在文章下留言 “转载:公众号名称”,
我们将为您添加白名单,授权“转载文章时可以修改”。

微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
Rhino Linux:滚动发布但也很稳定的 Ubuntu | Linux 中国13 个从头开始构建的独立 Linux 发行版 | Linux 中国如何通过 chroot 恢复 Arch Linux 安装 | Linux 中国最佳 Linux 远程桌面客户端 | Linux 中国在美国263.肺癌?精神病、电台律师如何在 Ubuntu 和其他 Linux 中检查 CPU 和硬盘温度 | Linux 中国如何在 Ubuntu 和其他相关 Linux 中安装 Python 3.10 | Linux 中国如何在 Linux 中确定运行的是那种初始化系统 | Linux 中国重访科州翠湖开源朗读者:使用 Linux 的优势和劣势 | Linux 中国如何在 Arch Linux 中启用 Snap 支持 | Linux 中国如何在 Arch Linux 中安装 elementary OS 的 Pantheon 桌面 | Linux 中国Linux Mint 的更新管理器现在支持 Flatpak | Linux 中国双剑合璧---GLP-1R/GIPR 双激动剂Linux 中的 su 和 sudo 命令有什么区别? | Linux 中国如何在 Arch Linux 中安装 Cinnamon 桌面 | Linux 中国通过 SSH 在远程 Linux 系统上执行命令 | Linux 中国Rosalía 登意大利版《VOGUE》封面!在你的 Linux 终端中玩经典的贪吃蛇游戏 | Linux 中国乳糖不耐受和牛奶过敏千万别搞错美国入境档案--弗洛伊德,荣格及萨德·费伦齐1909年纽约入境12 个对新手最重要的 Linux 命令 | Linux 中国打造万圣节 Linux 桌面 | Linux 中国以调试 Rust 的方式来学习 Rust | Linux 中国如何在 Ubuntu Linux 上更新谷歌 Chrome | Linux 中国使用这个多功能的 Linux 命令转换音频文件 | Linux 中国解决 Linux 中的 “Bash: Command Not Found” 报错 | Linux 中国如何在 Silverblue 上变基到 Fedora Linux 37 | Linux 中国如何提高 Ubuntu 和其他 Linux 系统中的扬声器音量 | Linux 中国如何在 Ubuntu 等 Linux 中安装 Python 3.11 | Linux 中国使用 PSCP 将文件和文件夹从 Windows 传输到 Linux | Linux 中国在 Linux 中如何从命令行查找默认网关的 IP 地址 | Linux 中国Fedora Linux 37 发布 | Linux 中国天赋“易昺(bǐng)”,创造历史!5 个 htop 替代:增强你的 Linux 系统监控体验 | Linux 中国
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。