Appearance
C++ 调试、Core Dump 与运行期诊断
排查 C++ 问题时,不要只靠“加日志猜原因”。先保留符号和现场,再按崩溃、内存、并发、性能四类问题选择合适工具;不同工具擅长发现不同错误。
故障定位路径
先确认版本、构建产物、输入、线程数和运行环境。线上问题尤其要保留 build ID、可执行文件和匹配的调试符号;拿“另一个版本”的二进制分析 core 往往会得到错误栈。
Core Dump 保存了什么
Core dump 是操作系统在进程异常终止(或显式请求)时保存的进程现场文件,不是“只生成一个调用栈”。其中通常包含寄存器状态、映射内存片段、线程信息、信号原因以及部分栈/堆数据;能否看到局部变量、内联帧和准确源代码位置,取决于调试符号与优化程度。
Linux 中可通过 ulimit -c unlimited 允许 shell 启动的进程生成 core;实际落盘位置和收集方式还受 core_pattern、systemd-coredump、容器权限与磁盘策略影响。core 可能含口令、令牌和用户数据,必须按敏感生产数据管理。
常用 GDB 起点:
bash
gdb ./my_server core
(gdb) bt # 当前线程调用栈
(gdb) thread apply all bt # 全部线程调用栈
(gdb) frame 3 # 切换到某一帧
(gdb) info locals # 查看局部变量(依赖符号与优化)
(gdb) p variable # 打印表达式优化构建可能内联函数、消除变量或重排控制流,导致“变量已优化掉”或调用栈不直观。为复现调试可采用 -g -O0;需要更接近线上性能时可用 -g -O1 / -g -O2 并接受部分可观测性下降。
Sanitizer:尽早发现内存与并发问题
| 工具 | 擅长发现 | 常见编译选项 | 重要限制 |
|---|---|---|---|
| AddressSanitizer(ASan) | 越界访问、use-after-free、栈/堆错误 | -fsanitize=address | 增加内存和运行期开销,通常用于测试/灰度 |
| LeakSanitizer(LSan) | 进程退出时的泄漏 | 常与 ASan 一起启用 | 对常驻全局对象和第三方库需正确解读 |
| UndefinedBehaviorSanitizer(UBSan) | 有符号溢出、错误转换、部分未定义行为 | -fsanitize=undefined | 不能覆盖所有 UB |
| ThreadSanitizer(TSan) | 数据竞争与部分同步错误 | -fsanitize=thread | 开销较高,通常不能与 ASan 同时使用 |
示例(Clang / GCC 工具链,具体可用组合以工具链文档为准):
bash
c++ -std=c++20 -g -O1 -fno-omit-frame-pointer \
-fsanitize=address,undefined app.cpp -o app
ASAN_OPTIONS=detect_leaks=1 ./appSanitizer 报告指向的是触发点,根因可能更早:例如 ASan 报 use-after-free 时,需要同时检查对象所有权、异步回调、容器失效和线程停止顺序。
常见问题与首选手段
| 症状 | 第一选择 | 关键追问 |
|---|---|---|
| 段错误、非法指令、abort | core + GDB + ASan | 空/悬空指针?数组越界?断言或 terminate? |
| 堆损坏、偶发崩溃 | ASan、最小化复现 | 更早的越界是否破坏了分配器元数据? |
| 内存持续增长 | LSan、堆 profile、对象指标 | 真泄漏还是缓存、队列堆积、未释放连接? |
| 随机错结果/偶发死锁 | TSan、锁顺序、线程栈 | 是否有无锁读写、锁顺序反转、生命周期竞态? |
| CPU 高或尾延迟升高 | profiler、火焰图、P95/P99 | 热点是算法、锁竞争、分配、I/O 还是调度? |
日志仍重要,但要记录结构化的请求 ID、错误码、状态转换和可控采样;不要把用户密钥、原始密码或完整敏感载荷写入日志。
strace 与系统调用跟踪
strace 的原理与风险:strace 基于 ptrace 系统调用,把目标进程置于被跟踪状态——每次系统调用都要停下目标进程,由 tracer 接管后再恢复。生产环境风险:
- 系统调用密集的进程可能慢几倍到几十倍(每次 syscall 都多出停止/恢复与上下文切换);
- 目标被暂停、时序改变,竞态类问题可能消失或恶化(Heisenbug);
- ptrace 可能被安全机制拒绝(
ptrace_scope、容器 seccomp 策略); - 输出文件巨大,可能打满磁盘。
更轻量的替代方案:
| 工具 | 原理 | 适用 |
|---|---|---|
perf stat / perf record | 采样 + 硬件计数器 | 热点分析、火焰图,不改写目标行为 |
eBPF / bpftrace | 内核内安全探针(kprobe/tracepoint) | 按需插桩、可过滤,开销小可灰度 |
ftrace | 内核自带跟踪框架 | 内核函数调用跟踪 |
| 应用层埋点 | 日志、指标、延迟分布 | 最可控,作为常规手段 |
一句话:strace 全量拦截系统调用,重且改变时序;生产环境优先 perf/eBPF 等采样式或插桩式方案,strace 只在低流量或非生产环境短暂使用。
面试速答
Linux 程序崩溃如何定位:先确保二进制与调试符号可对应,收集 core 和日志;用 gdb binary core 查看崩溃信号、bt 和全部线程栈,再检查崩溃帧的参数、局部变量与相关对象生命周期。若可复现,使用 ASan/UBSan 运行复现输入;涉及并发时用 TSan 或检查同步、停止与销毁协议。最后修复根因并加入回归测试,而不是只在崩溃点加空指针判断。