5个鲜为人知GNU调试器(GDB)技巧

GNU 调试器(gdb)是一种宝贵的工具,可用于在开发程序时检查正在运行的进程并解决问题。 2019-12-06 14:30:41 GNU调试器GDB修复代码 图解Kubernetes应用部署 在一切皆容器的时代,Kubernetes无疑是一个时代宠儿。越来越多的企业和个人开始使用Kubernetes来虚拟化自己的业务,管理自己的容器。那么如何在Kubernetes部署应用,以及对运行中的集群出现故障如何排查就成了大家日益关注的问题了。 2019-12-06 14:24:58 Kubernetes容器YAML 未编译的Python代码比Go慢100倍,编译后呢? 我是编译型编程语言的忠实粉丝,一直都是。虽然解释型编程语言可以让开发者更快地编写和测试代码,但我仍然认为编译器是值得长期投入的。 2019-12-06 13:59:37 代码开发Python 20 个新的且值得关注的 Vue 开源项目 Vue 相对不于 React 的一个优点是它易于理解和学习,且在国内占大多数。咱们可以在 Vue 的帮助下创建任何 Web 应用程序。 因此,时时了解一些新出现又好用的Vue 开源项目也是挺重要,一方面可以帮助咱们更加高效的开发,另一方面,咱们也可以模范学习其精华部分。 2019-12-06 13:58:42 GitHubCSS开源 如何让Git适应敏捷开发流程? 一旦涉及版本控制系统,Git实际上代表敏捷开发的水平。Git作为一款强大的开源系统,有较强的灵活性,可以按需匹配任何开发团队的工作流程。 2019-12-06 11:14:57 Git开发系统 网站上线前的终极测试清单 译文 在启用网站新的功能,或是新网站上线之前,我们需要根据一份终极测试清单,通过针对用户界面、以及浏览器兼容性等方面的测试,来避免事后可能发生的错误。 2019-12-06 11:03:34 网站上线终极测试 都9012年了,为何我还坚持用C语言开发游戏 我最近所有个人项目游戏都是用“纯” C语言开发。解释为什么要这样做可能很有趣。预警:一大堆枯燥的编程语言观点将要出现。

GNU 调试器(gdb)是一种宝贵的工具,可用于在开发程序时检查正在运行的进程并解决问题。

[[284924]]

了解如何使用 gdb 的一些鲜为人知的功能来检查和修复代码

GNU 调试器gdb)是一种宝贵的工具,可用于在开发程序时检查正在运行的进程并解决问题。

你可以在特定位置(按函数名称、行号等)设置断点、启用和禁用这些断点、显示和更改变量值,并执行所有调试器希望执行的所有标准操作。但是它还有许多其它你可能没有尝试过的功能。这里有五个你可以尝试一下。

条件断点

设置断点是学习使用 GNU 调试器的第一步。程序在达到断点时停止,你可以运行 gdb 的命令对其进行检查或更改变量,然后再允许该程序继续运行。

例如,你可能知道一个经常调用的函数有时会崩溃,但仅当它获得某个参数值时才会崩溃。你可以在该函数的开始处设置一个断点并运行程序。每次碰到该断点时都会显示函数参数,并且如果未提供触发崩溃的参数值,则可以继续操作,直到再次调用该函数为止。当这个惹了麻烦的参数触发崩溃时,你可以单步执行代码以查看问题所在。

  1. (gdb) break sometimes_crashes
  2. Breakpoint 1 at 0x40110e: file prog.c, line 5.
  3. (gdb) run
  4. [...]
  5. Breakpoint 1, sometimes_crashes (f=0x7fffffffd1bc) at prog.c:5
  6. 5 fprintf(stderr,
  7. (gdb) continue
  8. Breakpoint 1, sometimes_crashes (f=0x7fffffffd1bc) at prog.c:5
  9. 5 fprintf(stderr,
  10. (gdb) continue

为了使此方法更具可重复性,你可以在你感兴趣的特定调用之前计算该函数被调用的次数,并在该断点处设置一个计数器(例如,continue 30 以使其在接下来的 29 次到达该断点时忽略它)。

但是断点真正强大的地方在于它们在运行时评估表达式的能力,这使你可以自动化这种测试。

  1. break [LOCATION] if CONDITION
  2. (gdb) break sometimes_crashes if !f
  3. Breakpoint 1 at 0x401132: file prog.c, line 5.
  4. (gdb) run
  5. [...]
  6. Breakpoint 1, sometimes_crashes (f=0x0) at prog.c:5
  7. 5 fprintf(stderr,
  8. (gdb)

条件断点使你不必让 gdb 每次调用该函数时都去问你要做什么,而是让条件断点仅在特定表达式的值为 true 时才使 gdb 停止在该位置。如果执行到达条件断点的位置,但表达式的计算结果为 false,调试器会自动使程序继续运行,而无需询问用户该怎么做。

断点命令

GNU 调试器中断点的一个甚至更复杂的功能是能够编写对到达断点的响应的脚本。断点命令使你可以编写一系列 GNU 调试器命令,以在到达该断点时运行。

我们可以使用它来规避在 sometimes_crashes 函数中我们已知的错误,并在它提供空指针时使其无害地从该函数返回。

我们可以使用 silent 作为第一行,以更好地控制输出。否则,每次命中断点时,即使在运行断点命令之前,也会显示堆栈帧。

  1. (gdb) break sometimes_crashes
  2. Breakpoint 1 at 0x401132: file prog.c, line 5.
  3. (gdb) commands 1
  4. Type commands for breakpoint(s) 1, one per line.
  5. End with a line saying just "end".
  6. >silent
  7. >if !f
  8. >frame
  9. >printf "Skipping call\n"
  10. >return 0
  11. >continue
  12. >end
  13. >printf "Continuing\n"
  14. >continue
  15. >end
  16. (gdb) run
  17. Starting program: /home/twaugh/Documents/GDB/prog
  18. warning: Loadable section ".note.gnu.property" outside of ELF segments
  19. Continuing
  20. Continuing
  21. Continuing
  22. #0 sometimes_crashes (f=0x0) at prog.c:5
  23. 5 fprintf(stderr,
  24. Skipping call
  25. [Inferior 1 (process 9373) exited normally]
  26. (gdb)

转储二进制内存

GNU 调试器内置支持使用 x 命令以各种格式检查内存,包括八进制、十六进制等。但是我喜欢并排看到两种格式:左侧为十六进制字节,右侧为相同字节表示的 ASCII 字符。

当我想逐字节查看文件的内容时,经常使用 hexdump -Chexdump 来自 util-linux 软件包)。这是 gdbx 命令显示的十六进制字节:

  1. (gdb) x/33xb mydata
  2. 0x404040 <mydata> : 0x02 0x01 0x00 0x02 0x00 0x00 0x00 0x01
  3. 0x404048 <mydata+8> : 0x01 0x47 0x00 0x12 0x61 0x74 0x74 0x72
  4. 0x404050 <mydata+16>: 0x69 0x62 0x75 0x74 0x65 0x73 0x2d 0x63
  5. 0x404058 <mydata+24>: 0x68 0x61 0x72 0x73 0x65 0x75 0x00 0x05
  6. 0x404060 <mydata+32>: 0x00

如果你想让 gdbhexdump 一样显示内存怎么办?这是可以的,实际上,你可以将这种方法用于你喜欢的任何格式。

通过使用 dump 命令以将字节存储在文件中,结合 shell 命令以在文件上运行 hexdump 以及define 命令,我们可以创建自己的新的 hexdump 命令来使用 hexdump 显示内存内容。

  1. (gdb) define hexdump
  2. Type commands for definition of "hexdump".
  3. End with a line saying just "end".
  4. >dump binary memory /tmp/dump.bin $arg0 $arg0+$arg1
  5. >shell hexdump -C /tmp/dump.bin
  6. >end

这些命令甚至可以放在 ~/.gdbinit 文件中,以永久定义 hexdump 命令。以下是它运行的例子:

  1. (gdb) hexdump mydata sizeof(mydata)
  2. 00000000 02 01 00 02 00 00 00 01 01 47 00 12 61 74 74 72 |.........G..attr|
  3. 00000010 69 62 75 74 65 73 2d 63 68 61 72 73 65 75 00 05 |ibutes-charseu..|
  4. 00000020 00 |.|
  5. 00000021

行内反汇编

有时你想更多地了解导致崩溃的原因,而源代码还不够。你想查看在 CPU 指令级别发生了什么。

disassemble 命令可让你查看实现函数的 CPU 指令。但是有时输出可能很难跟踪。通常,我想查看与该函数源代码的特定部分相对应的指令。为此,请使用 /s 修饰符在反汇编中包括源代码行。

  1. (gdb) disassemble/s main
  2. Dump of assembler code for function main:
  3. prog.c:
  4. 11 {
  5. 0x0000000000401158 <+0>: push %rbp
  6. 0x0000000000401159 <+1>: mov %rsp,%rbp
  7. 0x000000000040115c <+4>: sub $0x10,%rsp
  8. 12 int n = 0;
  9. 0x0000000000401160 <+8>: movl $0x0,-0x4(%rbp)
  10. 13 sometimes_crashes(&n);
  11. 0x0000000000401167 <+15>: lea -0x4(%rbp),%rax
  12. 0x000000000040116b <+19>: mov %rax,%rdi
  13. 0x000000000040116e <+22>: callq 0x401126 <sometimes_crashes>
  14. [...snipped...]

这里,用 info 寄存器查看所有 CPU 寄存器的当前值,以及用如 stepi 这样命令一次执行一条指令,可以使你对程序有了更详细的了解。

反向调试

有时,你希望自己可以逆转时间。想象一下,你已经达到了变量的监视点。监视点像是一个断点,但不是在程序中的某个位置设置,而是在表达式上设置(使用 watch 命令)。每当表达式的值更改时,执行就会停止,并且调试器将获得控制权。

想象一下你已经达到了这个监视点,并且由该变量使用的内存已更改了值。事实证明,这可能是由更早发生的事情引起的。例如,内存已释放,现在正在重新使用。但是它是何时何地被释放的呢?

GNU 调试器甚至可以解决此问题,因为你可以反向运行程序!

它通过在每个步骤中仔细记录程序的状态来实现此目的,以便可以恢复以前记录的状态,从而产生时间倒流的错觉。

要启用此状态记录,请使用 target record-full 命令。然后,你可以使用一些听起来不太可行的命令,例如:

  • reverse-step,倒退到上一个源代码行
  • *reverse-next,它倒退到上一个源代码行,向后跳过函数调用
  • reverse-finish,倒退到当前函数即将被调用的时刻
  • reverse-continue,它返回到程序中的先前状态,该状态将(现在)触发断点(或其他导致断点停止的状态)

这是运行中的反向调试的示例:

  1. (gdb) b main
  2. Breakpoint 1 at 0x401160: file prog.c, line 12.
  3. (gdb) r
  4. Starting program: /home/twaugh/Documents/GDB/prog
  5. [...]
  6. Breakpoint 1, main () at prog.c:12
  7. 12 int n = 0;
  8. (gdb) target record-full
  9. (gdb) c
  10. Continuing.
  11. Program received signal SIGSEGV, Segmentation fault.
  12. 0x0000000000401154 in sometimes_crashes (f=0x0) at prog.c:7
  13. 7 return *f;
  14. (gdb) reverse-finish
  15. Run back to call of #0 0x0000000000401154 in sometimes_crashes (f=0x0)
  16. at prog.c:7
  17. 0x0000000000401190 in main () at prog.c:16
  18. 16 sometimes_crashes(0);

这些只是 GNU 调试器可以做的一些有用的事情。还有更多有待发现。

©本文为清一色官方代发,观点仅代表作者本人,与清一色无关。清一色对文中陈述、观点判断保持中立,不对所包含内容的准确性、可靠性或完整性提供任何明示或暗示的保证。本文不作为投资理财建议,请读者仅作参考,并请自行承担全部责任。文中部分文字/图片/视频/音频等来源于网络,如侵犯到著作权人的权利,请与我们联系(微信/QQ:1074760229)。转载请注明出处:清一色财经

(0)
打赏 微信扫码打赏 微信扫码打赏 支付宝扫码打赏 支付宝扫码打赏
清一色的头像清一色管理团队
上一篇 2023年5月5日 09:02
下一篇 2023年5月5日 09:02

相关推荐

发表评论

登录后才能评论

联系我们

在线咨询:1643011589-QQbutton

手机:13798586780

QQ/微信:1074760229

QQ群:551893940

工作时间:工作日9:00-18:00,节假日休息

关注微信