GDB 学习笔记
2023-08-15 20:36:00 # Tools

1. 常见调试策略

1.1 确认

  1. 你相信某个变量有某个确定的值
  2. 你相信在给定的 if-then-else 语句中, else 语句是被执行的部分
  3. 你相信当你调用某个函数的时候, 该函数会正确接收其参数
    找 bug 的过程就是确认上述所有事情

1.2 二分

根据代码行数二分, 在 mid 的位置确认上述事情, 进而缩小范围

1.3 甚至无法通过编译

删除代码直到不产生编译错误, 再逐步缩小删除的代码范围, 进而确认错误的具体位置

2. 使用调试工具和一个好的文本编辑器

2.1 不要使用 print 大法作为主要调试手段

你可能在代码中间添加 print 语句从而检查变量的值
这样很不方便, 你需要重新编译所有文件, 改完 bug 还要逐一删除

调试工具不需要重新编译, 而且还可以自动追踪所有变量
而且它还可以告诉你段错误等致命错误发生的位置

2.2 工具

  1. 调试工具
    1. gdb 最常用的调试工具, 并不一定是最好
    2. ddd 是基于 $GUI$ 界面的 gdb
      不过建议先使用 $TUI$ 界面的 gdb
  2. 编辑器: 最好支持 undo, redo , 多窗口等功能
  3. IDE: 有时, 调试器会集成在 IDE

3. 如何使用 gdb

基本策略: 设置断点, 然后往下逐步执行代码, 并随时检测变量的值

3.1 常用命令

首先确认编译的时候使用 -g 命令

1
gcc -g file.c
  1. 开始和结束调试:
    • 开始调试 gdb <可执行文件>
    • 退出调试 q
  2. r(run), 正常运行程序后面要加的参数也要加进来 r < a.in > a.out
  3. l(list)
    • 单个文件后面加行号表示列出某行, 回车键继续向下滚动
    • 多个文件 l a.b.c:55
    • 可以指定函数名从而列出函数
  4. b (breakpoint), c(continue)
    • b
      • 第一参数是位置, 表示在哪行或者哪个函数位置打断点, 同 l
      • 第二参数是条件, 条件断点: b main if x > 0
    • c 表示断点过后继续执行
    • disable <断点编号> 表示取消断点
  5. disp(display), p(print)
    • disp <变量名> 表示每次运行都要显示这个变量, 用 undisp <编号> 取消
    • p <变量名> 只打印一次, p /x <变量名> 表示使用十六进制输出
    • printf "X = %d, Y = %d\n", X, Y, 相当于 Cprintf 去掉括号
  6. x/nfu <地址>
    • n 表示多少内存单元
    • f 表示输出格式(同 C 语言)
    • u 表示多少个字节组成一个内存单元, 默认为 4
  7. n(next), s(step)
    • n 向下执行一步, 但不会进入调用的函数内部
    • s 向下执行一步, 如果调用了函数就会跳转到函数第一行
    • si, ni 执行汇编代码
  8. bt (backtrace): 查看堆栈信息, 可能会在未知问题上生效
  9. set 改变程序中变量的值: set variable x = 12
  10. call func() 调用程序中的函数, 通常是调试目的的函数
  11. define <宏名称>, 在接下来的行中描述宏命令, 以新一行的 end 结尾

更改代码之后, 重新编译, 不需要退出 gdb, 它会自动检测到二进制文件更改