--trace-children=yes是Valgrind监控子进程的必要开关,启用后为各进程生成独立日志(推荐--log-file=valgrind.%p.log),主进程需收SIGINT触发已退出子进程的泄漏报告,并应配合--show-leak-kinds=all和--show-reachable=yes全面识别子进程内存问题。

必须加 --trace-children=yes
Valgrind 默认不进入子进程,哪怕主程序 fork 或 exec 了新进程,Memcheck 也只监控父进程。不加这个参数,子进程里的内存泄漏、越界访问全被忽略。
常见错误现象是:日志里只有父进程的堆 summary,子进程分配的内存完全没出现在 LEAK SUMMARY 中;或者程序崩溃在子进程中,但 Valgrind 报告里找不到对应调用栈。
-
--trace-children=yes是开关,不是可选建议 —— 缺它等于没测子进程 - 它对
fork、execve、posix_spawn等创建的子进程都生效 - 如果子进程本身又 fork 出孙进程,
--trace-children=yes默认不会递归跟踪,需确认是否真需要(多数场景不需要)
日志输出要分开或重定向,否则混在一起难排查
启用 --trace-children=yes 后,Valgrind 会为每个子进程生成独立的输出段,但默认全打到同一个终端或文件里,时间戳和 PID 交错,容易看串。
推荐做法是强制按进程分离日志:
- 用
--log-file=valgrind.%p.log,%p会被替换成进程 PID,每个进程一个文件 - 避免只写
--log-file=a.log,否则父子日志挤在同一文件,definitely lost找不到归属 - 如果必须合并在一个流里,至少加
--time-stamp=yes和--verbose,靠时间戳和 PID 字段人工对齐
子进程退出后不自动出报告?得手动发 SIGINT
当主进程长期运行(比如守护进程、FastCGI 服务),而子进程已退出,Valgrind 不会立刻汇总子进程的泄漏结果 —— 它在等整个进程树结束,或你干预。
正确做法是向 Valgrind 的主控进程(不是子进程本身)发送中断信号:
- 先用
ps aux | grep valgrind找到 Valgrind 主进程 PID(通常是启动命令那一行) - 执行
kill -s SIGINT,它会立即触发所有已退出子进程的泄漏分析,并把完整报告刷进日志 - 别对子进程 PID 发
SIGINT,那只是终止子进程,Valgrind 还卡在等待状态
--show-leak-kinds=all 和 --show-reachable=yes 得配合用
子进程生命周期短,很多“泄漏”其实是全局变量或 atexit 注册的清理函数延迟释放,单纯看 definitely lost 会漏掉关键问题。
建议组合使用:
-
--show-leak-kinds=all确保possible、still reachable都显示出来 -
--show-reachable=yes把那些被全局指针、static 变量持有的内存块也列出来 —— 子进程中这类模式很常见(比如配置缓存、连接池单例) - 注意:
still reachable不一定安全,尤其在子进程里:如果子进程反复 fork/exec,每次积累一点still reachable,实际就是隐性泄漏
子进程内存问题最难的不是发现,而是判断“这算不算泄漏”。同一段代码,在父进程中 still reachable 是设计使然,在子进程中反复出现就可能是资源未重置。盯住 PID 和调用栈起点,比单纯数 bytes in blocks 更有效。











