恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

gdb 实战:断点、watchpoint、core dump 与多线程调试

  • 首页
  • 资讯中心
  • /
  • gdb 实战:断点、watchpoint、core dump 与多线程调试

相关资讯

C++ 日志与单元测试:无第三方库的最小实现 2026/10/17 6:39:35
自定义分配器与内存池:std::pmr 从零到 free list 2026/10/17 6:39:35
PyCharm下Robot Framework自动化测试环境搭建与实战指南 2026/10/17 6:39:35

最新资讯

社区防疫物资申报系统实战:SpringBoot+Vue全栈设计与实现
PAT甲级1001-1159刷题复盘:思路、测试点与代码沉淀
ORB-SLAM2彩色点云补全:从稀疏着色到TSDF稠密重建
Windows 10与Arch Linux双系统安装:LVM分区实战指南
一文讲清,什么是ERP系统?
大模型调用链的数据安全从模型路由到企业级反萃取:TaoToken 统一 Key 通道下的可复制配置与验证

今日推荐

Qwen3.5-122B-A10B-singprobe幻觉检测实战:AUC 0.7806,如何实时揪出大模型一本正经的胡话?
NetSpot 实战:用信道分析与频宽优化解决 Wi-Fi 卡顿
C++ split() 字符串分割的五种实现与性能实测

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

gdb 实战:断点、watchpoint、core dump 与多线程调试

发布时间:2026/10/17 6:39:35
gdb 实战:断点、watchpoint、core dump 与多线程调试 程序崩溃segfault、逻辑错、死锁是每个 C 开发者的日常。靠printf打日志排查又慢又容易改错代码专业的做法是上调试器。gdbGNU Debugger是 Linux 上最主流的调试器这篇把它的核心命令和实战套路系统讲一遍。关于 gdb 本身先诚实交代这篇环境里没有 gdbfind探测/opt、/usr均无只有同名的gdbus不是它所以下面的gdb命令都是用法示例标注出来是为了不假装跑过。但gdb 要解决的那些问题「崩溃在哪一行」「哪个变量越界」「谁在反复调用」这篇全部用可实测的工具补上了ASan/UBSan 的崩溃栈、gprof 的调用图见第 6.5 节。作为调试对象的 C 程序本身也都真实编译运行在线 gcc 13.2 验证。1. 引子程序崩了第一件事是什么假设你的程序跑着跑着Segmentation fault (core dumped)。你要问的第一个问题不是「哪里写错了」而是「崩溃那一刻的调用栈是什么样」。gdb 的价值就是把「崩溃现场」摊开给你看哪个函数、哪一行、变量当时是什么值。先用一个能跑的目标程序当调试对象#includecstdio#includevector// 供 gdb 断点调试的目标程序累加一个数组intsum(conststd::vectorintv){inttotal0;for(intx:v){totalx;// 可以在这里打断点观察 total 的变化}returntotal;}intmain(){std::vectorintdata{1,2,3,4,5};intresultsum(data);std::printf(sum %d\n,result);return0;}sum 15官方文档std::vector —— 上面程序的容器类型调试时print data能看到它的内容2. 核心命令断点、单步、看变量gdb 最核心的几个命令背下来就够应付 80% 的场景命令作用场景break 函数名/break 文件:行号设断点停在想看的代码处run开始运行启动程序可带命令行参数next/step单步执行next不进入函数step进入continue继续到下一个断点跳过不关心的部分print 表达式打印变量/表达式看变量当前值backtrace缩写bt打印调用栈看「谁调用了谁」watch 变量观察点watchpoint变量被修改时自动停下用法示例标注gdb 命令环境无 gdb未实测g-g-O0demo.cpp-odemo# -g 生成调试信息-O0 避免优化掉变量gdb ./demo# (gdb) break sum # 在 sum 函数入口打断点# (gdb) run # 运行停在 sum# (gdb) next # 单步不进入函数# (gdb) print total # 看 total 的值# (gdb) backtrace # 看调用栈sum - main# (gdb) continue # 跑到结束# (gdb) quit调试必须-g加调试信息、-O0关优化否则断点可能对不上行号变量也可能被优化掉显示optimized out。3. 条件断点与 frame 切换断点打在循环里会停很多次条件断点能只在满足条件时停# (gdb) break demo.cpp:7 if total 10 # total 超过 10 才停# (gdb) condition 1 total 15 # 给 1 号断点加条件崩溃时backtrace会打印多层调用栈用frame在栈帧间切换、查看每一层的局部变量# (gdb) backtrace# #0 sum(...) at demo.cpp:7# #1 main() at demo.cpp:13# (gdb) frame 1 # 切到 main 那一帧# (gdb) print data # 看 main 里的 data# (gdb) frame 0 # 切回 sum一个典型的「变量被观察」场景作为watch的演示对象#includecstdio// 供 watchpoint 观察的目标循环里变量逐步变化intmain(){intbudget100;intstep10;for(inti0;i5;i){budget-step;// 设 watch budget观察每次变化std::printf(第 %d 轮后 budget %d\n,i1,budget);}std::printf(最终 budget %d\n,budget);return0;}第 1 轮后 budget 90 第 2 轮后 budget 80 第 3 轮后 budget 70 第 4 轮后 budget 60 第 5 轮后 budget 50 最终 budget 50官方文档未定义行为UB —— 很多难查的崩溃根源是 UBgdb 是定位它的第一现场工具对上面的程序watch budget后每次budget - step执行完gdb 都会自动停下来并打印旧值和新值比手写printf省事得多。4. core dump崩溃现场的「快照」程序崩溃时操作系统可以把那一刻的内存镜像存成一个 core 文件事后用 gdb 打开它不需要重新运行就能看崩溃现场。这是排查「线上偶发崩溃」的关键手段。ulimit-cunlimited# 允许生成 core 文件默认可能被限制为 0g-g-O0crash.cpp-ocrash ./crash1# 触发崩溃生成 core文件名可能是 core 或 core.进程号gdb ./crash core# 打开 core 文件直接看崩溃现场# (gdb) backtrace # 崩溃那一刻的调用栈# (gdb) print 变量 # 崩溃时变量的值core dump 的完整回放流程core dump 的产生与回放流程 程序运行 ──崩溃(SIGSEGV)──► 内核把内存镜像写进 core 文件 │ ▼ gdb ./程序 core 文件 │ ▼ backtrace 看崩溃那一刻的调用栈 │ ▼ frame 逐帧切换 print 看各层变量下面是会触发崩溃的演示程序。它的崩溃路径需要传命令行参数沙箱无参数运行时走正常路径#includecstdio// 反例不要这么写解引用空指针触发 SIGSEGV// 真实代码应遵循 Core Guidelines 用智能指针 / 引用避免裸指针voidcrash_by_null_deref(){int*pnullptr;std::printf(%d\n,*p);// 反例解引用空指针这里会崩溃}intmain(intargc,char**){if(argc1){crash_by_null_deref();// 命令行传任意参数 → 触发崩溃产生 core}else{std::printf(正常路径没有崩溃\n);}return0;}正常路径没有崩溃官方文档std::signal —— SIGSEGV 这类信号是崩溃/被中断的底层机制ulimit -c要设成unlimited才能生成 core 文件0表示不生成。core 文件名和存放位置可用/proc/sys/kernel/core_pattern查看。拿到 core 文件后gdb ./crash core一行就能回到崩溃现场不用重现 bug。这对「只在线上偶发、本地复现不出来」的崩溃尤其重要。5. 多线程调试thread apply all bt多线程程序卡死、死锁时光看主线程没用要看所有线程各自的调用栈。先给一个多线程目标程序#includecstdio#includethread#includeatomic// 供 gdb 多线程调试的目标程序两个线程各自累加voidworker(std::atomicintcounter){for(inti0;i1000;i){counter.fetch_add(1,std::memory_order_relaxed);}}intmain(){std::atomicintcounter{0};std::threadt1(worker,std::ref(counter));std::threadt2(worker,std::ref(counter));t1.join();t2.join();std::printf(最终计数 %d期望 2000\n,counter.load());return0;}最终计数 2000期望 2000官方文档std::thread 与 std::atomic —— 多线程调试的两个核心对象多线程调试的关键命令g-g-O0-pthreadthread.cpp-othread# 多线程要加 -pthreadgdb ./thread# (gdb) run# (gdb) info threads # 列出所有线程# (gdb) thread 2 # 切到 2 号线程# (gdb) backtrace # 看这个线程的栈# (gdb) thread apply all bt # 一次性看所有线程的调用栈排查死锁利器死锁时用thread apply all bt能看到线程 A 停在lock()等 B 释放锁线程 B 也停在lock()等 A。两个栈一对就看出死锁环了。6. 完整示例一段「看着对但结果错」的代码把断点、watchpoint、frame 的排查思路串起来。下面这段代码的结果不符合直觉正是调试器的用武之地#includecstdio#includevector// 目标累加 1..10但结果却不对 —— 供 gdb 排查的完整示例intmain(){std::vectorintdata;for(inti1;i10;i){data.push_back(i);}inttotal0;for(std::size_t i0;idata.size();i){totaldata[i];}std::printf(累加 1..10 的结果 %d\n,total);return0;}累加 1..10 的结果 55这段代码本身是对的结果 55。真正排查时你会在total data[i]那行打断点、watch total观察累加过程、用print i和print data[i]看循环走到了哪一步。这就是 gdb 排查「结果不对」的标准姿势先定位到错误的那一次循环再往前推是哪个输入导致的。6.5 受限环境下的替代用 ASan/UBSan 拿到「崩溃栈」gdb 最有价值的一招是崩溃后的backtrace告诉你崩在哪个函数、哪一行。这个能力在没有 gdb 的受限环境里可以用AddressSanitizer / UndefinedBehaviorSanitizer顶上编译期插桩崩溃时直接打印精确的调用栈和出错行号。下面是一个越界读vector只有 3 个元素却读第 4 个ASan 的崩溃报告真实输出关键行2ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000001c ... READ of size 4 at 0x60200000001c thread T0 #0 0x4014cd in main /app/example.cpp:6 #1 0x72f42e62a1c9 (/lib/x86_64-linux-gnu/libc.so.60x2a1c9) #2 0x72f42e62a28a in __libc_start_main ... #3 0x401154 in _start ... ... SUMMARY: AddressSanitizer: heap-buffer-overflow /app/example.cpp:6 in main看#0 ... in main /app/example.cpp:6和SUMMARY: ... /app/example.cpp:6。它直接告诉你越界发生在第 6 行作用等价于 gdb 的backtrace崩溃现场自己带上了调用栈不用重现。编译方式g-O1-fsanitizeaddress-gdemo.cpp-odemo# ASan 调试信息./demo# 越界时直接打印上面的崩溃栈UBSan-fsanitizeundefined同理能定位「有符号整数溢出」「无效类型转换」这类 UB 的确切行号。所以「沙箱没 gdb」不代表「没法做崩溃定位」sanitizer 编译跑一遍崩溃栈比 gdb 还省事不用手动breakrun。补一句性能侧的「谁在反复调用」用 gprof真实调用图见 perf 篇第 2.5 节系统调用侧的「程序卡在哪」用strace跟踪。这几样凑一起就是受限环境里不靠 gdb 的一套调试工具箱。7. 延伸阅读GDB 官方手册sourceware.org所有命令的权威说明break/watch/thread apply都在里面C Core Guidelines — 调试调试与错误排查的工程基调Compiler Explorergodbolt在线看编译结果与汇编调试「编译器生成什么」时配合用本知识库内的相关篇目《perf 与火焰图从热点到定位接 Perfetto》 —— 程序慢在哪这篇讲 Linux perf 采样型 profiler 的原理周期采样 栈回溯、pe《clang-tidy 与 cppcheck静态分析落地》 —— 编译器 -Wall 能抓到的是语法和浅层语义问题《二进制分析nm、objdump、readelf 看懂符号表与段》 —— 链接报 multiple definition、undefined reference8. 一句话总结gdb 的核心就五件事断点break、单步next/step、看值print、看栈backtrace、观察变化watch崩溃先看 core dump 的backtrace多线程卡死用thread apply all bt。记住「编译加-g -O0」这个前提。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号