其他
无法用 ASan 或需要精确内存错误/缓存/调用图/堆分析时…
当程序疑似内存泄漏、越界、释放后使用、未初始化读取,或需要不依赖 root 的缓存/调用图/堆分配分析时,用 Valgrind 各工具排查。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 35/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 38/100 ×0.15
适用场景
无法用 ASan 或需要精确内存错误/缓特定
解决思路
- Memcheck 编译:用 `-g -O1` 获得最佳结果;`-O0` 也可;避免 `-O2`+,因为可能产生误报。
- 运行 Memcheck:`valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all --track-origins=yes --error-exitcode=1 ./prog [args]`
- `--leak-check=full`:默认只给摘要,开启完整泄漏详情。
- `--show-leak-kinds=all`:默认只显示 definite,显示所有泄漏类型。
- `--track-origins=yes`:默认 no,显示未初始化值来源,较慢。
- `--error-exitcode=N`:默认 0,发现错误时以 N 退出,用于 CI 集成。
- `--log-file=file`:默认 stderr,保存报告到文件。
- `--suppressions=file`:默认 none,抑制已知误报。
- `--gen-suppressions=yes`:默认 no,为错误打印 suppression 指令。
- `--max-stackframe=N`:默认 2000000,深栈时增大。
- `--malloc-fill=0xAB`:默认 off,填充已分配内存以检测未初始化使用。
- `--free-fill=0xCD`:默认 off,填充已释放内存以检测释放后使用。
- Memcheck 输出格式示例:`==12345== Invalid read of size 4` 后跟调用栈;`Address ... is 0 bytes after a block of size 40 alloc'd` 指出分配位置。
- `Invalid read`/`Invalid write`:越界访问;检查数组边界。
- `Use of uninitialised value`:写前读取;用 `--track-origins=yes`。
- `Invalid free`/`double free`:malloc/free 不匹配;检查所有权。
- `Definitely lost`:没有指针可达该块;明确泄漏。
- `Indirectly lost`:通过另一泄漏块间接丢失;通常意味着有一个根泄漏。
- `Possibly lost`:指针可能指向块中间;自定义分配器下常为误报。
- 泄漏类型 `Definitely lost`:无指针指向块。
- 泄漏类型 `Indirectly lost`:通过另一个 lost 块丢失。
- 泄漏类型 `Possibly lost`:指针指向块内部。
- 泄漏类型 `Still reachable`:退出时仍有指针存在;不是泄漏但从未释放。
- 对库代码:用 `--show-leak-kinds=definite,indirect` 减少 still-reachable 噪声。
- 生成 suppression:`valgrind --gen-suppressions=yes ./prog 2>&1 | grep -A20 '{'`
- 编写 suppression 文件,例如 `valgrind.supp` 中条目 `{ openssl_uninit Memcheck:Cond fun:SHA256_Init ... }`
- 使用 suppression 文件:`valgrind --suppressions=valgrind.supp ./prog`
- Cachegrind 运行:`valgrind --tool=cachegrind ./prog`
- Cachegrind 输出:`cachegrind.out.PID`
- 注释源码:`cg_annotate cachegrind.out.12345 --auto=yes`
- 对比两次运行:`cg_diff cachegrind.out.before cachegrind.out.after`
- Cachegrind 关键指标:`I1mr`/`ILmr` 为 L1/LL 指令缓存未命中率。
- Cachegrind 关键指标:`D1mr`/`DLmr` 为 L1/LL 数据读未命中率。
- Cachegrind 关键指标:`D1mw`/`DLmw` 为 L1/LL 数据写未命中率。
- Callgrind 运行:`valgrind --tool=callgrind --callgrind-out-file=callgrind.out ./prog`
- 分析 Callgrind:`callgrind_annotate callgrind.out`
- 用 KCachegrind GUI 可视化:`kcachegrind callgrind.out`
- Callgrind 比 perf 慢,但无需 root 且提供精确调用次数。
- Massif 运行:`valgrind --tool=massif ./prog`
- 可视化 Massif:`ms_print massif.out.PID | less`
- Massif GUI:`massif-visualizer massif.out.PID`
- Massif 显示堆使用随时间变化;用于找峰值分配点和跟踪渐进泄漏。
- 性能注意:Valgrind Memcheck 比原生慢约 10–50 倍;用更短的代表性工作负载。
- 在 CI 中用 `--error-exitcode=1` 快速失败。
- 开发期间用 ASan(`-fsanitize=address`)做更快内存检查。
- 仅在 ASan 无法使用时保留 Valgrind,例如旧工具链、类生产环境。
- 比较 Valgrind vs ASan 见 `references/valgrind-vs-asan.md`。
- 相关替代:更快 ASan/UBSan 见 `skills/runtimes/sanitizers`;CPU 级 profiling 见 `skills/profilers/linux-perf`;可视化 Callgrind 输出见 `skills/profilers/flamegraphs`。
适用边界 · 注意事项
- 不要在 Memcheck 下使用 `-O2` 及以上优化,否则可能产生误报。
- 当开发中可用 ASan 且需要快速迭代内存检查时,不要用 Valgrind 作主要工具;它慢约 10–50 倍。
- 当需要 CPU 级 profiling 或无法承受 Valgrind 的 slowdown 时,改用 `perf` 等;Cachegrind/Callgrind 也较慢。