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中指定不要报告的项目,不管这个缓存是前面的哪种情况。