--num-callers=50(Nginx/Boost项目建议100)、必须编译带-g且禁用内联(-O0或-fno-inline)、优先用--xml=yes输出、多线程需--trace-children=yes,三者缺一不可。

valgrind --num-callers 要设够大
默认只显示 12 层调用栈,很多 C++ 模板展开、RAII 包装或 Nginx 模块嵌套后,关键帧早被截断了。不加这个参数,你看到的 main 下面可能就两行,根本找不到 ngx_palloc 或 std::vector::push_back 是在哪一层分配的。
实操建议:
-
--num-callers=50是较稳妥的起点,Nginx 模块或 Boost-heavy 项目建议直接设到100 - 配合
--track-origins=yes,能连未初始化内存的源头(比如哪个malloc返回值没赋给指针)也一起带栈回溯 - 如果栈太深导致输出爆炸,可先用
--log-file=valgrind.log重定向,再 grep 关键函数名
必须编译时带 -g 且禁用内联优化
没有调试符号,valgrind 只能打印地址和汇编偏移,比如 0x40064B,你得手动 addr2line 查,还经常对不上行号。而 -O2 或更高优化会把小函数内联,调用栈直接“塌陷”成一两层。
正确做法:
- 编译命令必须含
-g,CMake 里加set(CMAKE_BUILD_TYPE Debug) - 临时排查泄漏时,显式加
-O0;若需保留部分优化,至少禁用内联:-fno-inline -fno-inline-small-functions - Nginx 模块要重新 configure:加上
--with-debug --without-http_gzip_module等非必要模块,减少干扰符号
XML 输出比文本更可靠
终端滚动太快、颜色编码乱码、中文路径被截断——这些都会让文本报告里的调用栈“看着有,实际缺”。Valgrind 原生 XML 输出结构清晰,每条泄漏记录带完整 <frame> 序列,连模板实例化名(如 std::unique_ptr<Foo, Deleter>::release)都原样保留。
关键命令组合:
valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all --num-callers=100 --xml=yes --xml-file=leak.xml ./your_program- 生成的
leak.xml可直接丢给脚本或 AI 解析,不用人工数缩进、拼函数名 - 注意:XML 模式下
--log-file无效,所有输出只写入--xml-file
多线程程序要加 --trace-children=yes
很多泄漏藏在子进程或 fork 出的 worker 里,比如 Nginx 的 reload 流程、日志轮转子进程、或测试框架启动的临时服务。默认 valgrind 只跟踪主进程,子进程的 malloc 完全不监控,泄漏就“消失”了。
启用后需注意:
-
--trace-children=yes会让每个子进程也跑在 Valgrind 下,总耗时显著增加 - 避免误跟踪系统工具(如
/bin/sh),可用--trace-children-skip=/bin/*,/usr/bin/* - 结合
--log-file时,Valgrind 会自动为每个进程生成带 PID 的文件,如valgrind.log.12345
new 或 create_pool。


















