说到 Valgrind,C/C++ 程序员恐怕没有谁没听说过它,要是如此真是 out了。Valgrind 是 一个测试框架,它包含各种工具:

  1. Memcheck, SGcheck 是内存错误检测工具。
  2. Cachegrind, Callgrind 缓存分析工具。
  3. Helgrind, DRD 是线程错误检测工具。在编写多线程程序时很有用。
  4. Massif, DHAT 是堆分析工具。
  5. BBV is an experimental SimPoint basic block vector generator. It is useful to people doing computer architecture research and development.
  6. 还有其他的工具Variants and Patches

Valgrind 是非侵入式的,所以使用它可以完成很多的检测任务而不需要对程序源码进行任何 修改,除非需要一些高级的功能。Valgrind 运行在一个模拟的 CPU 上,它是 Valgrind 的核心。 然后各种检测在它之上完成相应的工作。这也是为什么 Memcheck 工具不需要像 Memcheck, D.U.M.A那样需要引用相应的头文件,也可以很漂亮的完成工作。
也正因为如此,在 Valgrind 上运行的程序会慢很多。


安装

很多 Linux 发行版的源都有已经编译好的二进制包,直接安装即可。openSUSE下的安装:

$ zypper install valgrind

也可以源码安装:

$ ./configure; make; sudo make install

Memcheck 能检测到的错误

  1. 非法读写内存 当读写无效的内存地址时(如:已释放内存块或者未分配的地址等), 它会报错并给出发生错误的地方和调用堆栈。可以指定 –read-var-info=yes 获得更加详细 的信息,但程序会运行的更慢。

  2. 使用未初始化的变量 未初始化的值主要来自未初始化的本地变量和未初始化的堆内存块。只有这个值会影响程序的 行为时 Valgrind 才会报错。例如将它作为调用参数或者作为分支条件。可以指定 –track-origins=yes 获得详细的信息。

  3. 使用为初始化的变量或者无效的地址作为系统调用的参数 Valgrind 会对系统调用的所有参数做检查,包括:
    1. 检查变量是否已经初始化
    2. 如果系统调用需要读某个内存块,则检查该内存块是否已经初始化。
    3. 如果系统调用需要写某个用户指定的内存块,则检查该内存块是否是有效和已初始化。
  4. 非法释放内存块 指的是 double-free 或者 传递给 free/delte 的指针没有指向内存块的起始位置。

  5. 分配和释放内存块的函数不匹配 如使用 free 分配的内存块使用 delete 去释放会导致报错。

  6. 目标内存块和源内存块重叠 对于像 memcpy, strcpy, strncpy, strcat, strncat 这样的内存块拷贝函数,需要指定 两个参数 src 和 dst,如果这两个参数分别指定的内存块存在重叠的区域则会报错。

  7. 内存泄露 当程序退出时 Memcheck 会报告内存的释放情况。程序退出时还存在的内存块分为四种类型:
    1. 肯定丢失的:即没有任何指针指向那些内存块。
    2. 间接丢失的:还有指针指向那些内存块,但这些指针在“肯定丢失”的内存块中。
    3. 可能丢失的:还有指针指向那些内存块,但这些指针没有指向那些内存块的起始位置。
    4. 仍然可访问的块:还有指针指向那些内存块。

Valgrind 的使用

为了使 Memcheck 能够精确的报告错误的位置,编译程序时必须指定 -g 以便生成的 二进制文件中包含有调试信息。如果可以容忍程序运行得慢一点,可以指定 -O0,这样 就可以准确的报告错误所在的行数。大部分时候 -O1 也是可以正常的定位。但最好不要 指定 -O2 或者更高的优化级别,这样导致无法检测使用未初始化的变量。因为这些变量 可能已经被优化而不存在了。
而且,不能同时与其他的 allcator 同时使用,否则会导致 Valgrind 无法正常正常工作。

Valgrind 的调用格式如下:

valgrind --tool=memcheck [valgrind-options] your-prog [your-prog-options]

–tool 参数指定使用的检测工具,即上面提到的。默认为 memcheck。

Valgrind 生成的报告信息如下:

==10772== Memcheck, a memory error detector
==10772== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==10772== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==10772== Command: ls -l
==10772==
...
==10772==
==10772== HEAP SUMMARY:
==10772==     in use at exit: 20,346 bytes in 36 blocks
==10772==   total heap usage: 641 allocs, 605 frees, 113,106 bytes allocated
=10772==
==10772== LEAK SUMMARY:
==10772==    definitely lost: 0 bytes in 0 blocks
==10772==    indirectly lost: 0 bytes in 0 blocks
==10772==      possibly lost: 0 bytes in 0 blocks
==10772==    still reachable: 20,346 bytes in 36 blocks
==10772==         suppressed: 0 bytes in 0 blocks
==10772== Rerun with --leak-check=full to see details of leaked memory
==10772==
==10772== For counts of detected and suppressed errors, rerun with: -v
==10772== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

10772 指的是进程 id。报告中包含 堆的使用情况和内存泄漏信息。如果出现内存泄漏,则可以 再次使用 –leak-check=full 获得更详细的信息。


Valgrind 相关的选项


Memcheck 选项


设置常用的选项

对于那些常用的选项,总是在命令行中指定不是个好主意。Valgrind 提供三个地方可以 设置这些常用的选项。

  1. ~/.valgrindrc
  2. $VALGRIND_OPTS
  3. ./.valgrindrc Valgrind 会按照从上往下的顺序读取,命令行中指定的相同选项会覆盖它们。每个选项应该 以工具名开头+冒号+选项=值的格式设置。如:
    --memcheck:leak-check=yes
    

参考链接

  1. Valgrind User Manual
  2. Using Valgrind to Find Memory Leaks and Invalid Memory Use