必须重点关注==pid==开头的错误报告行、heap summary段和leak summary段;其余如syscall或param相关提示优先级低,应通过--log-file导出后用grep筛选invalid read、definitely lost、use of uninitialised value等关键错误,并启用--track-origins=yes追踪未初始化值源头。

Valgrind输出里哪些行必须看
Valgrind默认把所有内存操作、系统调用、工具初始化信息全打出来,但真正要修的 bug 只集中在几类行。盯住这三块就够了:==PID==开头的错误报告行、HEAP SUMMARY段、LEAK SUMMARY段。其他全是干扰项,比如==12345== Command: ./myapp或==12345== Syscall param write(buf) points to uninitialised byte(s)这种带param或Syscall字样的,基本是内核调用层的间接提示,优先级远低于直接的Invalid read或definitely lost。
用--log-file和grep组合过滤关键错误类型
别在终端里滚屏找,直接导出再筛:
valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all --track-origins=yes ./myapp 2> valgrind.log
然后按需 grep:
-
grep "Invalid read" valgrind.log—— 找越界读(常见于数组下标错、结构体字段偏移算错) -
grep "definitely lost" valgrind.log—— 找确认泄漏(指针彻底丢失,没任何变量指向那块内存) -
grep "Use of uninitialised value" valgrind.log—— 找未初始化变量使用(常出现在malloc后没memset或赋值) -
grep -A 3 -B 1 "at.*\.c:" valgrind.log—— 把报错行连带前后3行和上1行一起抓,方便定位上下文
--suppressions生成抑制规则避免重复干扰
有些第三方库(比如 glibc 的 __libc_start_main、某些日志框架的静态缓冲区)会固定触发 Valgrind 报告,但你没法改。这时候不要忽略,而是用--gen-suppressions=all生成抑制文件:
valgrind --tool=memcheck --gen-suppressions=all ./myapp 2> suppressions.supp
之后再运行时带上它:
valgrind --tool=memcheck --suppressions=suppressions.supp --leak-check=full ./myapp
这样每次跑出来的报告就只留你代码里的真问题。注意:抑制文件要定期更新,尤其当你升级了依赖库版本之后,旧的 suppressions 可能失效或误压新问题。
为什么--track-origins=yes不能省
遇到Use of uninitialised value却找不到源头?大概率是因为没开--track-origins=yes。这个选项会让 Valgrind 追踪未初始化值从哪来——比如是malloc返回的裸内存,还是某个函数参数传进来就没初始化。不开它,你只能看到“用了未初始化值”,开了它,你会看到类似这样的链:
==12345== Use of uninitialised value of size 8 ==12345== at 0x1092A7: process_data (main.c:42) ==12345== by 0x1091F2: main (main.c:28) ==12345== Uninitialised value was created by a heap allocation ==12345== at 0x4C2D8D2: malloc (vg_replace_malloc.c:309) ==12345== by 0x1091C0: init_buffer (buffer.c:15)
没这行Uninitialised value was created by...,你就得靠猜;有了它,直接跳到buffer.c:15查。
真正卡住人的不是 Valgrind 输出多,而是它把“可疑”和“确定”混在一起报。盯住definitely lost、Invalid read/write、use-after-free这三类,其他先压进 suppressions,等主路径稳了再回过头看。











