1. 下载安装
下载地址:Valgrind: Current Releases
安装:例如版本 valgrind-3.18.1
tar xvf valgrind-3.18.1.tar.bz2
cd valgrind-3.18.1/
./configure
make
sudo make install
2. 使用示例
valgrind --log-file=valgrind.log --tool=memcheck --leak-check=full --show-leak-kinds=all ./your_app arg1 arg2
参数说明:
- --log-file 报告文件名。如果没有指定,输出到stderr。
- --tool=memcheck 指定Valgrind使用的工具。Valgrind是一个工具集,包括Memcheck、Cachegrind、Callgrind等多个工具。memcheck是缺省项。
- --leak-check 指定如何报告内存泄漏(memcheck能检查多种内存使用错误,内存泄漏是其中常见的一种),可选值有:
- no 不报告
- summary 显示简要信息,有多少个内存泄漏。summary是缺省值。
- yes 和 full 显示每个泄漏的内存在哪里分配。
- show-leak-kinds 指定显示内存泄漏的类型的组合。类型包括definite, indirect, possible,reachable。也可以指定all或none。缺省值是definite,possible。
3. 运行完成log分析
比如一个log的内容:
...
==6405== LEAK SUMMARY:
==6405== definitely lost: 45,983,648 bytes in 35,755 blocks
==6405== indirectly lost: 438,500,226 bytes in 364,136 blocks
==6405== possibly lost: 769,133,739 bytes in 1,548,741 blocks
==6405== still reachable: 588,418,997 bytes in 2,354,626 blocks
==6405== of which reachable via heuristic:
==6405== length64 : 5,600 bytes in 80 blocks
==6405== newarray : 1,680 bytes in 30 blocks
==6405== suppressed: 0 bytes in 0 blocks
==6405==
==6405== Use --track-origins=yes to see where uninitialised values come from
==6405== For lists of detected and suppressed errors, rerun with: -s
==6405== ERROR SUMMARY: 11549 errors from 387 contexts (suppressed: 0 from 0)
- memcheck跟踪所有malloc()/new()分配的堆块,所以它能在程序退出时知道哪些块还没有释放。
- 这里把程序能访问到的指针集合叫做root-set。memcheck根据从root-set能不能到达某个块,判断这个块是不是泄露了(reachable还是lost)。
- 到达块有两种方式:一是start-pointer,它指向块的开始地址;二是interior-pointer,它指向块的中间某个位置。
- 有start-pointer引用的缓存被认为是reachable。
- 有interior-pointer引用的缓存则不一定了。
- 它可能是分配之后的指针故意向后移动的结果。这时程序使用缓存开始的几个字节保存特别信息,而真正的数据在后面某个位置。比如指向C++的std::string内部数组的指针、指向new[]分配的C++对象数组的指针、多重继承的情况等。这时缓存是reachable的。
- 但它也可能只是偶然指向缓存。这时缓存就是lost的。
- 所以interior-pointer引用的缓存是possible lost。
根据从root-set到缓存是否需要跳转,memcheck 将分配的缓存的状态分为几种:
- directly reachable: root-set中的指针指向缓存,不需跳转
- indirectly reachable: root-set中的指针不直接指向缓存,需要跳转
- directly lost:没有任何指针指向缓存
- indirectly lost:有指针指向缓存,但从root-set中也无法到达这个指针
下图列出了缓存的各种情况。其中RRR是root-set中的节点,AAA和BBB是缓存。
Pointer chain AAA Leak Case BBB Leak Case
------------- ------------- -------------
(1) RRR ------------> BBB DR
(2) RRR ---> AAA ---> BBB DR IR
(3) RRR BBB DL
(4) RRR AAA ---> BBB DL IL
(5) RRR ------?-----> BBB (y)DR, (n)DL
(6) RRR ---> AAA -?-> BBB DR (y)IR, (n)DL
(7) RRR -?-> AAA ---> BBB (y)DR, (n)DL (y)IR, (n)IL
(8) RRR -?-> AAA -?-> BBB (y)DR, (n)DL (y,y)IR, (n,y)IL, (_,n)DL
(9) RRR AAA -?-> BBB DL (y)IL, (n)DL
Pointer chain legend:
- RRR: a root set node or DR block
- AAA, BBB: heap blocks
- --->: a start-pointer
- -?->: an interior-pointer
Leak Case legend:
- DR: Directly reachable
- IR: Indirectly reachable
- DL: Directly lost
- IL: Indirectly lost
- (y)XY: it's XY if the interior-pointer is a real pointer
- (n)XY: it's XY if the interior-pointer is not a real pointer
- (_)XY: it's XY in either case
下面是memcheck中LEAK SUMMARY部分中的项目:
- Still reachable: 上图中(1)和(2)的情况,从root-set中通过start-pointer直接到达或跳转到达缓存。这一项是确定不是lost的,用户无需关系。
- Definitely lost:(3)的情况。没有任何指针指向缓存。这一项确定是lost的,用户需解决。
- Indirectly lost:(4)和(9)中BBB的情况。有start-pointer或interior-pointer指向缓存,但从root-set不能到达这些指针。这一项可以推迟解决,因为Indirectly lost一定与definitely lost对应,definitely lost解决了,indirectly lost也就变成reachable或者possible lost了。
- Possibly lost: 有interior-pointer指向缓存,从root-set能到达这些指针。因为memcheck不能判断interior-pointer是否lost,所以需要用户排除。
所以实际上只要关心definitely lost和possible lost就可以了。这也是memcheck的缺省值:
--show-leak-kinds=definite,possible
- suppressed: 用户在suppressed file中指定不要报告的项目,不管这个缓存是前面的哪种情况。