Memcheck和Callgrind是Valgrind的两个独立插件:Memcheck检测内存非法访问并即时报错,输出错误行号与地址;Callgrind统计指令执行与函数调用,输出热点函数与调用图,需专用工具解析。

Memcheck 和 Callgrind 用的是同一个 valgrind 命令,但行为完全不同
很多人以为 valgrind 就是“查内存泄漏的工具”,其实它是个插件平台,--tool=memcheck 和 --tool=callgrind 启动的是两个完全独立的分析引擎,底层监控逻辑、开销来源、输出目标都不同。Memcheck 关注每个字节的读写合法性,Callgrind 关注函数调用路径和指令执行次数。
内存检测(Memcheck)输出的是“错误位置”,性能分析(Callgrind)输出的是“热点函数”
Memcheck 的典型输出像这样:
==12345== Invalid write of size 4 ==12345== at 0x4006B7: main (example.c:12) ==12345== Address 0x5204048 is 0 bytes after a block of size 8 alloc'd
而 Callgrind 输出的是类似这样的调用图和计数:
events: Ir fl=example.c fn=main 12 1000000 fn=process_data 23 850000
关键区别在于:
- Memcheck 不关心函数耗时,只标记非法内存操作发生的具体行号和地址
- Callgrind 不报告任何“错误”,它默认程序逻辑正确,只统计哪段代码执行了最多指令
- Memcheck 可以在程序崩溃前就捕获 use-after-free;Callgrind 即使程序跑通了,也能告诉你
std::vector::push_back占了 60% 的指令数
性能开销差异极大:Memcheck 慢但可控,Callgrind 更慢且不可预测
两者都拖慢程序,但原因不同:
-
--tool=memcheck主要开销来自对每次内存访问插入 validity/accessibility 检查,放大系数通常在 20–30×,但行为稳定 -
--tool=callgrind需要为每条 CPU 指令记录调用上下文,遇到深度递归或密集循环时,日志体积爆炸式增长,实际运行可能比原程序慢 50–100×,甚至因磁盘满而中止 - Callgrind 默认不输出实时结果,必须等程序退出才生成
callgrind.out.*文件;Memcheck 错误会即时打印到 stderr
别混用 --leak-check 和 --dump-instr 这类参数
这些选项只对特定工具生效,强行跨工具使用会被忽略或报错:
-
--leak-check=full只对--tool=memcheck有效,加在callgrind命令里毫无作用 -
--dump-instr=yes是 Callgrind 专属,Memcheck 不认识它,会提示 “unknown option” -
--track-origins=yes看似通用,其实只被 Memcheck 实现,其他工具解析但不响应
真正容易被忽略的点是:Callgrind 生成的 profile 数据必须用 callgrind_annotate 或 kcachegrind 解析,直接看文本文件几乎无法定位瓶颈——那堆数字本身不带函数名映射,除非你编译时用了 -g 且没 strip 符号。



















