了解如何使用 gdb 的一些鲜为人知的功能来检查和修复代码。-- Tim Waugh(作者)

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

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

条件断点

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

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

(gdb) break sometimes_crashesBreakpoint 1 at 0x40110e: file prog.c, line 5.(gdb) run[...]Breakpoint 1, sometimes_crashes (f=0x7fffffffd1bc) at prog.c:55 fprintf(stderr,(gdb) continueBreakpoint 1, sometimes_crashes (f=0x7fffffffd1bc) at prog.c:55 fprintf(stderr,(gdb) continue

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

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

break [LOCATION] if CONDITION(gdb) break sometimes_crashes if !fBreakpoint 1 at 0x401132: file prog.c, line 5.(gdb) run[...]Breakpoint 1, sometimes_crashes (f=0x0) at prog.c:55 fprintf(stderr,(gdb)

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

断点命令

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

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

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

(gdb) break sometimes_crashesBreakpoint 1 at 0x401132: file prog.c, line 5.(gdb) commands 1Type commands for breakpoint(s) 1, one per line.End with a line saying just "end".>silent>if !f >frame >printf "Skipping call" >return 0 >continue >end>printf "Continuing">continue>end(gdb) runStarting program: /home/twaugh/Documents/GDB/progwarning: Loadable section ".note.gnu.property" outside of ELF segmentsContinuingContinuingContinuing#0 sometimes_crashes (f=0x0) at prog.c:55 fprintf(stderr,Skipping call[Inferior 1 (process 9373) exited normally](gdb)

转储二进制内存

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

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

(gdb) x/33xb mydata0x404040  : 0x02 0x01 0x00 0x02 0x00 0x00 0x00 0x010x404048  : 0x01 0x47 0x00 0x12 0x61 0x74 0x74 0x720x404050 : 0x69 0x62 0x75 0x74 0x65 0x73 0x2d 0x630x404058 : 0x68 0x61 0x72 0x73 0x65 0x75 0x00 0x050x404060 : 0x00

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

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

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

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

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

行内反汇编

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

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

(gdb) disassemble/s mainDump of assembler code for function main:prog.c:11 { 0x0000000000401158 : push %rbp 0x0000000000401159 : mov %rsp,%rbp 0x000000000040115c : sub $0x10,%rsp12 int n = 0; 0x0000000000401160 : movl $0x0,-0x4(%rbp)13 sometimes_crashes(&n); 0x0000000000401167 : lea -0x4(%rbp),%rax 0x000000000040116b : mov %rax,%rdi 0x000000000040116e : callq 0x401126 [...snipped...]

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

反向调试

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

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

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

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

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

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

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

(gdb) b mainBreakpoint 1 at 0x401160: file prog.c, line 12.(gdb) rStarting program: /home/twaugh/Documents/GDB/prog[...]Breakpoint 1, main () at prog.c:1212 int n = 0;(gdb) target record-full(gdb) cContinuing.Program received signal SIGSEGV, Segmentation fault.0x0000000000401154 in sometimes_crashes (f=0x0) at prog.c:77 return *f;(gdb) reverse-finishRun back to call of #0 0x0000000000401154 in sometimes_crashes (f=0x0) at prog.c:70x0000000000401190 in main () at prog.c:1616 sometimes_crashes(0);

这些只是 GNU 调试器可以做的一些有用的事情。还有更多有待发现。你最喜欢 gdb 的哪个隐藏的、鲜为人知或令人吃惊的功能?请在评论中分享。


gdb查看空指针 linux_5 个鲜为人知 GNU 调试器(GDB)技巧相关推荐

  1. LINUX下的GNU调试工具,GNU调试器 GDB 8.3 发布及安装更新,支持RISC-V与IPv6连接等

    Joel Brobecker刚刚宣布了大型GDB 8.3功能版本. GNU调试器的这一更新带来了许多改进和新功能,可以帮助开发人员. GNU Debugger 8.3版本为RISC-V GNU / L ...

  2. 5 个鲜为人知 GNU 调试器(GDB)技巧 | Linux 中国

    Table of Contents 条件断点 断点命令 转储二进制内存 行内反汇编 反向调试 GNU 调试器(gdb)是一种宝贵的工具,可用于在开发程序时检查正在运行的进程并解决问题. 你可以在特定位 ...

  3. Day 3 Linux(目录篇 、文件操作篇 、压缩解压相关命令, 编辑器vi 、编译器gcc 、调试器gdb、 makefile工程管理器初学)

    一.目录篇: 1.文件处理命令:cp 英文原意:copy 语法:cp -rp[源文件或目录][目的目录] 源文件可以同时是多个,即同时复制到多个文件 -r 复制目录 -p保留文件属性,比如文件创建时间 ...

  4. 【Linux】Linux调试器--gdb详解

    Linux环境基础开发工具使用(二) 一.Linux调试器-gdb使用 1.背景 2.使用 二.Linux项目自动化构建工具-make/Makefile 1.背景 2.依赖关系和依赖方法 3.原理 4 ...

  5. 转载:Qt Creator的CDB调试器--使用技巧与心得,重点是Symbols Path设置

    Qt Creator的CDB调试器--使用技巧与解决调试很慢的心得,重点是Symbols Path设置_$firecat全宏的代码足迹$-CSDN博客_cdb.exewindows系统下主要的调试器: ...

  6. gdb查看空指针 linux_资深程序员总结:分析Linux进程的6个方法,全都告诉你!

    文章来源:https://mp.weixin.qq.com/s/3u5XH8NGj3bF3Gn81N40gA 作者:LemonCoder 操作系统「进程」是学计算机都要接触的基本概念,抛开那些纯理论的 ...

  7. Linux下C语言的调试器 Gdb

    调试是所有程序员都会面临的问题. 如何提高程序员的调试效率, 更好更快地定位程序中的问题从而加快程序开发的进度, 是大家共同面对的问题. 就如读者熟知的Windwos下的一些调试工具, 如VC自带的设 ...

  8. GDB调试器——GDB调试器简介

    以下内容源于C语言中文网的学习与整理,如有侵权,请告知删除. 一.程序调试器的含义 程序中的错误主要分为 2 类,分别为语法错误和逻辑错误. 程序中出现的语法错误可以借助编译器解决:但逻辑错误则只能靠 ...

  9. Linux调试器-gdb使用

    目录 1. 背景 2. 开始使用 3. 理解 创建需要调试的代码 debug&&release 4 详细调试 list/l 行号 list/l 函数名 r或run break(b) i ...

最新文章

  1. c# list 自定义排序
  2. 谁说PCB布线不能走直角。。。
  3. keil5ARM编译器改字体大小和颜色的方法
  4. PCL学习笔记02:在ROS下建立编译链接PCL模块
  5. labuladong的算法小抄pdf_东哥手写正则通配符算法,结构清晰,包教包会!
  6. Kubernetes 小白学习笔记(13)--k8s集群路线-init流程
  7. 在Spring项目下集成CXF
  8. 网络安全渗透测试自学
  9. 计算机boot进入u盘启动,BIOS中设置U盘启动的几种方法
  10. 1031 Hello World for U
  11. 如何做好团队测试建设
  12. 系统迁移的重点步骤及注意事项
  13. Iperf测试问题处理指南
  14. 服务器域名绑定公网IP地址
  15. Python自动化测试学习3
  16. 游戏感:虚拟感觉的游戏设计师指南——第二章 游戏感与人类感知能力
  17. 开源硬件论坛,燃烧你的创造力 | COSCon'18 特辑
  18. 关于@EnableConfigurationProperties 注解
  19. 行业生态重塑中,新氧如何逆风翻盘
  20. React高频面试题总结 (附答案及原理代码)

热门文章

  1. C#使用了未赋值的局部变量
  2. C#通过DllImport引入dll中的C++非托管类
  3. 客服会话 小程序 如何发起_小程序、公众号、App三者如何融合布局?这里有一份避坑指南...
  4. 大众点评网2016校招试题选录
  5. 液位系统c语言程序,超声波自动测量物体液位系统的设计
  6. 最大后验估计_PR Ⅱ:贝叶斯估计/推断及其与MAP的区别
  7. Blender建模与游戏换装(转载文)
  8. 【10.20校内测试】【小模拟】【无向图建树判奇偶环】【树上差分】
  9. 博客园修改页面显示样式css
  10. 笔记整理——linux程序设计