GDB 进阶指南(watch、commands等命令使用)

GDB 进阶指南

本文面向有基本调试经验的开发者,深入介绍 GDB 的一些高阶用法,尤其适用于单元测试 (UT) 场景中的变量追踪、观察点自动化命令、内存访问监控等。


1. 使用 watch 观察变量变化

watch 会在某变量值发生变化时中断程序。

示例代码

// demo_watch.c
#include <stdio.h>

int global_counter = 0;

int main() {
    for (int i = 0; i < 5; ++i) {
        global_counter += i;
    }
    return 0;
}

GDB 操作

(gdb) gcc -g demo_watch.c -o demo_watch
(gdb) gdb ./demo_watch
(gdb) break main
(gdb) run
(gdb) watch global_counter
(gdb) continue

每次 global_counter 变化,GDB 都会中断程序。


2. 给 watch 添加自动执行命令:commands

使用 commands 可以在变量变化时自动打印信息、继续执行等。

示例

(gdb) watch global_counter
(gdb) commands
> silent
> printf "global_counter changed: %d\n", global_counter
> continue
> end

这段命令让程序在变量变化时自动打印并继续执行,不手动介入。


3. 打印变量变化时的代码位置

可以结合 frameinfo line 输出调用栈和源码位置信息。

(gdb) commands
> silent
> printf "global_counter changed to: %d\n", global_counter
> frame
> info line
> continue
> end

输出示例:

global_counter changed to: 1
#0  main () at demo_watch.c:8
Line 8 of "demo_watch.c" starts at 0x... and ends at 0x...

4. 修改已有观察点的 commands

查看观察点编号

(gdb) info breakpoints

修改指定观察点的 commands

(gdb) commands 2
> silent
> printf "Updated print: %d\n", global_counter
> continue
> end

删除 commands

(gdb) commands 2
> end

5. 监控内存地址访问:awatch / rwatch

示例代码

// demo_addr.c
int val = 42;

int main() {
    int *ptr = &val;
    *ptr = 100;
    return 0;
}

GDB 监控地址

(gdb) break main
(gdb) run
(gdb) awatch *(int*)(&val)
(gdb) commands
> silent
> printf "val was accessed!\n"
> frame
> info line
> continue
> end

val 被读写时,会自动打印当前代码位置。


6. 观察结构体成员 / 复杂表达式

GDB 支持观察结构体字段:

(gdb) watch my_struct.field

支持条件观察:

(gdb) watch my_var if my_var > 100

支持设置 GDB 变量:

(gdb) set $p = &my_struct.field
(gdb) p *$p

7. GDB 内联函数无法直接调用

GDB 不能直接调用内联函数,如:

inline int get_count(struct list *l) { return l->count; }
(gdb) p get_count(&my_list)   # ❌ 报错:未定义符号

解决方案:

  • 将函数改为非 inline
  • 或在代码中主动加测试用入口函数(wrapper)

8. GDB 变量不能取地址

(gdb) set $x = 123
(gdb) p &$x   # ❌ 错误:没有物理地址

只有程序中变量才能取地址:

(gdb) p &global_counter   # ✅ 合法

如需有地址的临时变量,可用:

(gdb) set $buf = malloc(4)
(gdb) set *((int*)$buf) = 99
(gdb) p *(int*)$buf

9. 总结与建议

功能 命令 是否支持自动执行
watch 变量变化 watch(写) ✅ 使用 commands
读写监控地址 rwatch(读)/awatch(读写) ✅ 使用 commands
打印调用位置 frame + info line
清除命令 commands N 后立即 end

建议

  • 在 UT 场景中加断点太频繁容易卡断测试;建议用 watch + commands 非侵入式追踪变量
  • 若变量变化频繁,可用 if 条件限制观察触发频率
  • 避免在 commands 中加入复杂逻辑,保持调试响应快速

posted @ 2025-05-10 17:00  岸南  阅读(709)  评论(0)    收藏  举报