GDB调试基础——断点、单步、内存查看
面试的时候被问过一个场景你的机器人程序跑着跑着突然segfault了你怎么排查我当时说了句加printf看看。面试官没说话但我能感觉到他不太满意。后来才知道GDB是C/C开发者的标配调试工具。在机器人开发里很多问题是运行时的——内存越界、空指针、段错误光靠printf根本定位不了。GDB能让你在程序运行时暂停、检查变量、查看调用栈甚至回溯崩溃前的现场。今天就把GDB最核心的用法讲一遍。不用全记住先把断点、单步、查看变量这几个学会面试的时候就有东西可聊了。编译时加-g调试的前提很多人忘了这一步。GDB要工作需要可执行文件里包含调试信息。编译的时候得加-g参数g -g -o robot_node robot_node.cpp如果用CMake在CMakeLists.txt里加上set(CMAKE_BUILD_TYPE Debug)Debug模式会自动加上-g参数。Release模式会做优化变量可能被编译器优化掉GDB里就看不到了。还有个细节优化等级别开太高。-O2和-O3会导致代码重排调试的时候执行顺序和你写的不一样看着会很困惑。调试阶段用-O0或者干脆不加优化。启动GDB和基础命令gdb ./robot_node进去之后就是一个(gdb)提示符。几个最基本的命令run 运行程序可以带参数run --ros-args ... break main 在main函数设断点 break 42 在第42行设断点 continue 继续运行到下一个断点 next 单步执行不进入函数内部 step 单步执行进入函数内部 print 变量名 查看变量的值 quit 退出GDBnext和step的区别很重要。next把函数调用当成一步执行完step会进入函数内部逐行执行。调试的时候大部分情况用next就够了只有你想看某个函数内部怎么执行的时候才用step。断点调试的核心断点就是告诉程序跑到这里停一下。break main 在main函数入口设断点 break robot_node.cpp:58 在第58行设断点 break MyClass::initSensor 在类的成员函数设断点 break robot_node.cpp:58 if i 100 条件断点只在i100时暂停条件断点特别有用。比如你有个循环跑1000次第500次出了问题。你不想单步按500次就设个条件断点让程序在前499次正常跑到第500次才暂停。info breakpoints 查看所有断点 delete 2 删除第2号断点 disable 1 禁用第1号断点不删除只是不生效 enable 1 重新启用还有个高级用法watchpoint。监控某个变量的值变化一旦变了就暂停。watch sensor_data 当sensor_data的值改变时暂停在机器人项目里如果你怀疑某个传感器数据在某个时刻突然跳变用watchpoint就能抓住那个瞬间。程序暂停后做什么程序在断点处暂停后你可以检查当前的状态。print x 打印变量x的值 print arr[0] 打印数组第一个元素 print *ptr 打印指针指向的值 print this-sensor_config 打印当前对象的成员p是print的简写老手都直接用p。p /x value 以十六进制显示 p /t value 以二进制显示 p /c value 以字符显示查看调用栈backtrace 显示完整的函数调用链简写bt frame 2 切换到第2层调用栈 info locals 查看当前栈帧的所有局部变量backtrace在排查崩溃问题时特别关键。程序segfault之后用bt就能看到崩溃前的函数调用链从最底层到最顶层一目了然。你就知道是从哪个函数调到哪个函数在哪一行出的问题。处理segfault机器人开发最常见的崩溃segfault段错误是C开发中最常见的崩溃类型。原因通常是访问了空指针、数组越界、或者使用了已释放的内存。在GDB里调试segfault的标准流程gdb ./robot_node (gdb) run # 程序崩溃 (gdb) bt # 查看调用栈找到崩溃位置 (gdb) frame N # 切换到崩溃的那个栈帧 (gdb) info locals # 查看所有局部变量 (gdb) p ptr # 检查可疑的指针有一次我写激光雷达数据处理程序跑了几分钟就segfault。用GDB一跑bt显示崩溃在一个数组访问的地方。p index一看index值是65536但数组大小只有1024。原来是点云数据量突然增大index越界了。加了个边界检查就解决了。如果程序不是每次都崩溃而是跑一段时间才崩可以用core dump。让程序崩溃时生成一个core文件然后用GDB加载分析ulimit -c unlimited # 允许生成core文件 ./robot_node # 运行等它崩溃 # 崩溃后会生成core文件 gdb ./robot_node core # 加载core文件分析 (gdb) bt # 查看崩溃时的调用栈在机器人项目中使用GDBROS节点可以直接用GDB调试gdb --args ros2 run my_package my_node如果节点是launch启动的可以在launch文件里设置参数让节点在GDB下运行。或者先用GDB启动节点再让其他节点连接它。调试多线程程序时GDB也能处理info threads 查看所有线程 thread 2 切换到第2号线程 thread apply all bt 查看所有线程的调用栈机器人项目通常有多个线程传感器读取、控制循环、通信等出问题时thread apply all bt能帮你看清每个线程当时在干什么哪个线程卡住了哪个线程崩了。面试中怎么聊GDB面试官问调试经验不要只说我会用GDB。要讲具体场景有一次机器人运行时导航模块segfault了我用GDB加载core文件bt看到崩溃在路径规划的一个数组访问处检查发现是栅格地图的索引越界。修复后加了边界检查和单元测试再也没出过这个问题。这种回答比说十句我会用break和print都有说服力。GDB脚本自动化调试如果你经常需要重复同一个调试步骤比如每次都在某个函数上设断点、打印变量、继续运行可以把这些命令写成GDB脚本# debug_script.gdb break processCloud commands 1 print cloud-size() continue end run然后用gdb -x debug_script.gdb ./my_node加载脚本。这在排查需要反复触发的问题时特别有用省去每次手动输入命令的麻烦。GDB调试的实战技巧实际项目中最常用的GDB调试流程先用gdb启动程序用break设断点用run运行程序。程序崩溃后用bt查看调用栈用info locals查看局部变量。一个实用技巧是用catch throw捕获C异常快速定位异常抛出的位置。另外gdb -core corefile可以直接分析core dump文件在生产环境排查问题时特别有用。补充一点在GDB中可以用display命令设置自动显示的变量每次程序暂停时都会打印这些变量的值调试循环特别方便。给你的建议先学会用GDB处理segfault。这是最实用的场景也是面试最常问的。能看懂backtrace能检查变量基本就够了。不要怕GDB的命令行界面。它确实不如图形化调试器直观但在服务器上、在远程机器人上你往往只有命令行可用。习惯了之后GDB的效率其实比IDE的调试器更高。还有一个建议平时写代码就养成加断言的习惯。assert(ptr ! nullptr)、assert(index size)这些断言能在问题发生的第一时间暴露出来比事后用GDB查core dump容易得多。上一篇第100篇 Git团队协作——分支策略、冲突解决和Code Review流程下一篇预告第102篇 GDB进阶——多线程调试、core dump分析和机器人崩溃排查