Linux 命令行入门学习资料 day_6 GDB 监视点watch一、什么是 watch—— 变量的“监控摄像头”之前学过的break是按位置暂停比如某一行代码而watch是按数据变化暂停。watch的作用告诉 GDB 持续监视某个变量或表达式只要它的值发生变化就立刻中断程序执行并显示旧值和新值。典型场景某个变量的值莫名其妙被修改不知道在哪改的想要知道一个全局变量或关键局部变量何时被赋予新值代替在大量地方手动加printf来追踪变量变化二、监视点的三种类型GDB 根据监视的触发条件将监视点分为三类类型命令触发条件写监视点(watchpoint)watch 表达式当被监视的表达式的值被写入修改时触发读监视点(read watchpoint)rwatch 表达式当被监视的表达式的值被读取时触发读写监视点(access watchpoint)awatch 表达式当被监视的表达式的值被读取或写入时触发最常用的是watch也就是写监视点。今天重点掌握它。三、如何使用 watch —— 完整流程演示准备工作写一个简单的测试程序创建文件watch_demo.c#includestdio.hintsum0;// 全局变量方便全程监视voidadd(intn){sumn;// 这里会修改 sum}intmain(){inti;for(i1;i3;i){add(i);printf(After adding %d, sum %d\n,i,sum);}return0;}编译必须加-g建议不要优化gcc-g-O0watch_demo.c-owatch_demo进入 GDB 并设置监视点gdb ./watch_demo1. 先给 main 函数设一个断点确保程序跑起来后再设 watch因为 watch 要监视的变量必须存在于当前作用域(gdb) break main Breakpoint 1 at 0x...: file watch_demo.c, line ... (gdb) run Starting program: ./watch_demo Breakpoint 1, main () at watch_demo.c:12 12 for (i 1; i 3; i) {2. 设置监视点监视全局变量sum(gdb) watch sum Hardware watchpoint 2: sum“Hardware watchpoint” 表示系统使用硬件寄存器来监视速度很快。3. 继续运行程序(gdb) continue Continuing.程序会一直运行直到sum的值发生变化。4. 触发监视点当第一次执行sum n后GDB 会突然暂停Hardware watchpoint 2: sum Old value 0 New value 1 add (n1) at watch_demo.c:7 7 }它告诉我们监视点编号 2sum的旧值是 0新值变成了 1程序停在修改sum的那一行实际上是add函数的第 7 行即修改sum语句的结束此时你可以用print n查看参数用backtrace查看调用栈。5. 继续运行观察每次变化(gdb) continue Continuing. After adding 1, sum 1 Hardware watchpoint 2: sum Old value 1 New value 3 add (n2) at watch_demo.c:7 7 }重复操作每次sum被修改都会暂停直到程序结束。四、监视局部变量如果监视的是局部变量必须在它的作用域内设置 watch并且一旦离开作用域监视点会自动删除。例子监视main中的循环变量i。(gdb) break main (gdb) run (gdb) watch i Hardware watchpoint 3: i (gdb) continue但需要注意当i在循环内频繁被修改每次都会触发可能过于频繁。如果只想在某个特定条件下暂停可以用条件监视点见后文。五、监视表达式watch不仅可以监视单个变量还可以监视任何合法的表达式。只要表达式的值发生变化就会触发。示例监视sum i虽然这不太常用但展示灵活性(gdb) watch sum i当sum或i任何一个变化导致sum i的结果改变时都会触发。注意复杂表达式可能无法使用硬件监视点此时 GDB 会自动降级为软件监视点软件监视点会让程序单步执行非常慢应尽量避免。六、条件监视点就像断点可以加条件一样watch也可以加条件只关心特定情况。格式watch 变量 condition 监视点编号 条件或者一步到位watch 变量 if 条件示例只监视sum被修改且新值大于 2 时才暂停。(gdb) watch sum (gdb) condition 2 sum 2或者(gdb) watch sum if sum 2这样当sum从 1 变到 3 时会触发新值 3 2但之前sum从 0 变到 1 时不会触发。七、管理监视点监视点和断点共用相同的编号体系所以可以用管理断点的命令来管理监视点。命令作用info watchpoints或info breakpoints查看所有监视点和断点delete 编号删除指定监视点/断点disable 编号暂时禁用监视点enable 编号启用监视点ignore 编号 count忽略监视点触发 count 次针对软件监视点示例(gdb) info breakpoints Num Type Disp Enb Address What 1 breakpoint keep y 0x... in main at watch_demo.c:12 2 hw watchpoint keep y sum (gdb) delete 2就会删除对sum的监视。八、硬件监视点 vs 软件监视点硬件监视点 (hw watchpoint)依赖处理器提供的调试寄存器数量有限一般 x86 有 4 个但对程序运行速度几乎无影响。软件监视点 (sw watchpoint)当硬件监视点不够用或表达式复杂无法用硬件实现时GDB 会使用软件监视点。其原理是每执行一条指令后都检查变量是否变化导致程序运行极慢。如果看到Software watchpoint提示要尽量简化监视表达式。查看监视点类型info breakpoints中 Type 列会显示hw watchpoint或watchpoint软件。九、实战综合示例追踪一个神秘错误假设有一个程序某个变量counter莫名被清零我们可以这样调试(gdb) watch counter Hardware watchpoint 1: counter (gdb) run ... Hardware watchpoint 1: counter Old value 100 New value 0 0x... in some_function () at buggy.c:42立刻就找到了清零的代码位置。十、✅ watch 命令速查卡命令简写作用示例watch 变量wa 变量当变量被写入时暂停watch sumrwatch 变量rw 变量当变量被读取时暂停rwatch flagawatch 变量aw 变量当变量被读或写时暂停awatch statuswatch 变量 if 条件-条件监视watch x if x 10info breakpointsi b列出所有监视点和断点i bdelete 编号d 编号删除监视点/断点d 2disable 编号-禁用监视点disable 3enable 编号-启用监视点enable 3十一、练习由易到难基础篇使用上面的watch_demo.c程序在 GDB 中对sum设置写监视点运行并记录每次触发时的新值。在同一个程序中尝试对main中的局部变量i设置watch i观察触发频率。然后删除该监视点。使用awatch sum观察程序是否在读取sum时也会暂停如在printf中。进阶篇编写一个小程序swap_test.c内容如下#includestdio.hvoidswap(int*a,int*b){inttemp*a;*a*b;*btemp;}intmain(){intx5,y10;printf(Before: x%d, y%d\n,x,y);swap(x,y);printf(After: x%d, y%d\n,x,y);return0;}在 GDB 中对x设置写监视点注意x是main中的局部变量需在进入main后设置。运行程序观察x是否在swap中被修改GDB 会停在哪里用backtrace查看调用栈。对指针变量设置监视点在swap函数内对*a设置watch *a观察它何时变化。提高篇编写一个程序包含一个全局数组int data[10]。在循环中修改data[3]的值。在 GDB 中对data[3]设置写监视点直接watch data[3]。注意观察硬件监视点可能支持但若提示软件监视点解释原因并记录运行速度。尝试条件监视监视sum只在变为偶数时暂停watch sum if sum % 2 0。用之前的watch_demo.c验证。十二、答案与关键点解析基础篇每次暂停输出新旧值依次为 0→1, 1→3, 3→6。i每次循环变化都会触发非常频繁。awatch sum会在printf读取sum时也触发如果监视成功通常硬件支持读写监视。进阶篇编译加-g -O0。watch x必须在main断点后设置。当执行swap(x,y)时*a的修改会改变xGDB 会停在*a *b;或下一行。backtrace显示swap和main。在swap函数内watch *a会在*a *b时触发旧值 5新值 10。提高篇对数组元素data[3]设置监视点通常可以用硬件监视点如果支持。若是软件监视点运行会明显变慢。条件监视watch sum if sum % 2 0。当sum从 0 变 1不触发1 变 3不触发3 变 6触发因为新值 6 是偶数。十三、学习检查清单watch和break的根本区别是什么设置watch x后什么时候程序会暂停如果监视的局部变量离开了作用域会发生什么如何查看当前设置的所有监视点如何删除一个监视点硬件监视点和软件监视点有什么区别哪种更快怎样设置“只在新值为负数时才暂停”的监视点如果这些问题都能流畅回答说明你已经把watch命令的核心用法完全吸收。现在你可以用这把“变量监控刀”去精准地剖析任何程序中的数据变化。实际编程中结合break、print和watch你将拥有非常强大的调试能力。继续加油