章节摘要:性能案件查"慢",调试案件查"错"。本章处理四类最难缠的缺陷:需要断点与回溯的常规崩溃(GDB)、一观测就变的并发竞争(TSan 与锁分析)、内存越界与泄漏(AddressSanitizer/Valgrind)、以及生产环境猝死后的验尸(core dump 分析)。这一章的工具扰动大,但对付的是玻尔式稳定缺陷与偶发海森 Bug 的分界线两侧。
调试和性能分析用的是同一套思维:假设、证据、定罪。只是证据来源从计数器变成了程序状态。
调试器的原理与日常操作,从 attach 到 backtrace 的标准动作。
数据竞争为什么难抓,死锁的四要素与检测手段。
两代内存错误检测器的机制对比与实战。
从 ulimit 配置到符号化还原,把猝死进程的死因挖出来。
5.1 GDB 是通用审讯室 │ ├──→ 5.2 并发案(要在审讯室里配专用仪器 TSan) ├──→ 5.3 内存案(另一类专用仪器 ASan/Valgrind) └──→ 5.4 验尸(现场没了,只能靠尸体 core dump)
调试深水区处理的是"错"而不是"慢",四节各管一类罪证:GDB 管现场勘察(断点、回溯、core 解剖),并发一节管时序类案件(死锁凝固现场一次取证、data race 靠 TSan 在测试负载里筛),内存一节管非法访问(ASan 便宜可常驻、Valgrind 免重编译兜底),core 验尸一节管生产事后的完整证据链。分工速记:
gdb -p $PID -batch -ex 'thread apply all bt 10' # 现场勘察 clang++ -fsanitize=thread -O1 -g ... # 并发罪证筛查 clang++ -fsanitize=address -O1 -g ... # 内存罪证筛查 gcore -o /var/case/core $PID # 生产快照
三件事对本章所有工具成立:第一,观测即暂停,attach、断点、检测器都会改变时序,生产上先快照后分析,交互式调试留给测试环境;第二,检测器结论要区分"检出的"与"未检出"——TSan 只报真阳性但覆盖率有限,没报不等于没病;第三,每起案件的产物是可复验的证据(core 文件、rr 录像、带符号的二进制),连同 build-id 归档,没有归档的验尸等于没做过。
调试深水区最容易犯的不是技术错,而是策略错:在一个无法稳定复现的 Bug 上连续消耗数天,而不肯花半天构造最小复现。本章四节共享的自救原则是:先花力气把"不可复现"变成"可复现"(压力放大、时序扰动、rr 录制、构造性故障注入),复现在手之后再选检测工具;二分法永远快于猜测——逐个关掉子系统、逐层剥离输入,比盯着代码想象并发场景可靠得多;每一步实验前写下预期结果,实验后对照,防止"看到想看的"这种人类本能污染证据。做到这三条,深水区案件的可控度会接近普通案件。