1 Valgrind简介
Valgrind基本原理是让待检查的程序运行在它模拟的cpu上,所以能得到非常详尽的信息,但是会影响到程序的性能,速度有可能下降20倍以上,不过作为开发环境下检查和统计内存使用情况还是不错的。
由于valgrind与cpu和操作系统紧密相关,因此只能够运行在x86/Linux, AMD64/Linux and PPC32/Linux环境当中。valgrind自带了好几个工具,功能很强大,不仅可以对内存进行检查,还能对堆、栈等内存使用情况进行统计和分析。
下载地址: http://valgrind.org/downloads/current.html
它主要包含有以下几个工具:
memcheck 内存检查
cachegrind 能对cache的使用情况进行统计
callgrind 据说能对函数的时间花销进行统计
massif 测量程序使用了多少堆空间
helgrind 对于多线程编程时,能检测到data race
编译被检测程序的时候,最好用-g选项加上调试信息,这样Memcheck给出的错误信息将包含精确的行号。
1.1 memcheck
memcheck是它所有工具中最强大最经常被使用到的工具
它带的memcheck工具至少能检查出如下几种情况:
1. 使用没有初始化的内存
2. 读或者写已经被释放掉的内存
3. malloc分配的内存的越界访问
4. 读写stack中不恰当的区域
5. 内存泄露,检测没有释放的通过malloc分配的内存块
6. malloc/new/new[] 与 free/delete/delete[] 不匹配的情况(咱们是单纯的C编程,这一点用不上)
7. memcpy时源地址与目的地址有重叠的情形
8. 不正确的内存释放,如两次释放heap内的内存。
注意:memcheck并不能检查到栈内数组上下越界的情况,array[-1]也不会报错的。
2 Valgrind的安装
Valgrind最简单的的编译和安装方法
1. 先运行bzip2 –d valgrind-3.4.1.tar.bz2 或 bunzip2 valgrind-3.4.1.tar.bz2 命令解压bz2包,得到tar包,然后再运行 tar zxf valgrind-3.4.1.tar 命令对tar包进行解压
2. 进入源码目录,运行 ./configure 命令 进行配置
3. 运行 make 进行编译
4. make check 检查编译是否成功(本步骤是可选步骤)
5. make install 安装,默认目录是 /usr/local/bin(需要root权限)
更详细的安装信息参考INSTALL文件
3 Valgrind的使用
3.1 被测试程序的编译
编译程序的时候最好用-g选项加上调试信息,这样Memcheck给出的错误信息将包含精确的行号。如果你使用的是C++语言,最将考虑将-fno-inline加入进来。这将能够清晰地看到函数调用链,在调试大的C++程序时,这样有助于减少迷惑。
3.2 Valgrind core
Valgrind的命令行格式: valgrind [valgrind-options] your-prog [your-prog-options]
注意: 在这里必须运行真正的机器码,而不是shell脚本。在valgrind中直接运行这些脚本将对/bin/shell或其它你正在使用的解释器进行检测。虽然可以使用 --trace-children=yes 选项对子进程进行检测,但是仍然会有其它令人迷惑的信息。
默认情况下,valgrind工具只产生必要的信息,如果要产生详细的信息,可以使用 -v 选项。
3.2.1 基本的运行参数
选择要运行的工具
--tool=<name> [default=memcheck]
运行名为name的Valgrind工具,如memcheck, cachegrind等。valgrind 默认使用Memcheck工具,注意:工具的名字都是小写.
打印帮助信息: -h
打印详细的检测信息: -v
是否对子进程进行跟踪: --trace-children=<yes|no> [default: no]
跟踪打开的fd信息, 包括socket: --track-fds=<yes|no> [default: no]
--log-file=<filename> 将所以的信息保存到指定的文件当中, 控制台中没有输出。
--num-callers=<number> [default: 12]
默认情况下,valgrind最多显示12层函数调用的名字来帮助使用者识别程序的位置,最多能够设置到50层。
--db-attach=<yes|no> [default: no]
当设置该功能时,如果发生错误,将会打印是否attach到gdb,提示如下:
---- Attach to debugger ? --- [Return/N/n/Y/y/C/c] ----
输入回车、N或n,将不会针对这个erro启动gdb,
输入Y或y,将启动gdb。当结束调试后,从gdb退出,程序将继续运行。
输入C或c,将不会启动gdb,以后对这个错误也将不会再打印上面的提示。
当debug调试结束后,退出debug,程序将继续运行。在GDB中通过”continue”命令继续执行程序是不会成功的。
--max-stackframe=<number> [default: 2000000]
Stack frame的最大尺寸。当栈指针移动超过这个数,valgrind将会认为程序已经切换到另一个栈当中。当你的程序中有大的在栈中分配的数组(临时数组),那么有可能需要使用这个选项。
3.3 Memcheck相关的命令行参数
--leak-check=<no|summary|yes|full> [default: summary]
当客户程序结束后,对内存泄露进行检查。内存泄露是指一个分配的块没有被释放,但是没有指针再指向它。如果设置成summary,它只告诉内存如何泄露了。如果设置成full或者yes,它将报告每个泄露的详细信息。
--undef-value-errors=<yes|no> [default: yes]
是否报告使用未定义值的错误,即访问那些没有初始化的变量的错误。
--track-origins=<yes|no> [default: no]
是否跟踪未初始化的值来自哪里。默认是关闭的,这就意味着它虽然能告诉你使用一种危险的方式访问未初始化的变量,但是不能告诉你未初始化的变量来自哪里。当设置成yes,memcheck将会努力将未初始化的变量的位置告诉你。可能来自以下四个位置:heap bloack, 栈中分配的,客户端请求的(?),或者其他方式(如调用brk等)。
--show-reachable=<yes|no> [default: no]
当disabled时,内存泄露检测只显示那些没有指针指向的块或只能找到一个指向其中间位置的指针的块。内存泄露的主要发生在这些块上。
当enabled时,泄露检测也会报告那些能找到一个指针指向的块。你的程序至少在理论上在退出前能释放这些块。与没有指针指向的块或那些只有一个内部指针指向的块相比: 它们(可能指没有指针指向的块或那些只有一个内部指针指向的块)更有可能会内存泄漏,因为你事实上没有一个指向块的起始位置的指针,即使你想释放,也没有进行释放的指针。
--partial-loads-ok=<yes|no> [default: no]
控制memcheck如何处理从可寻址或者不可寻址的地址处载入word-sized, word-aligned的数据。即在32位系统中,在非四字节对齐的地址处载入一个4字节对齐的数据(如整数),通常在这种情况下,系统将产生bus error。
当设置成yes,这样的载入不会产生一个地址错误,而是从不合法的地址处载入bytes,并将它们标记为未初始化;那些从合法地址处载入的数据以正常的方式处理。
当设置成no,从artially invalid的地址载入数据被认为与从完全无效的地址载入数据一样。产生一个无效地址错误,相应的字节被标记为未初始化。
注意: 代码的这种行为有违与ISO C/C++标准。如果有可能,应该对这样的代码进行修改。这个标记只能作为最后的方法使用。
--malloc-fill=<hexnumber>
将通过malloc,new等但是calloc除外的API分配的block用指定的字节进行填充。当对一些比较晦涩的问题进行定位时,也许会有用。分配的区域Memcheck仍然认为是未初始化的,这个flag只影响block的内容。
--free-fill=<hexnumber>
将通过free,delete释放的block用指定的字节进行填充。当对一些比较晦涩的问题进行定位时,也许会有用。这些释放的内容Memcheck仍然认为是不能够访问的,这个flag只影响block的内容。
如果适当的设置了--leak-check,对于每一个剩余block,Memcheck扫描整个进程的地址空间,搜寻指向这些块的指针。每个块位于如下三种类型之一:
• Still reachable:
能够找到一个指向block起始位置的指针。至少在理论上讲,在程序退出前,能够释放它。Memcheck将不会报告这样的blocks,除非设置了-show-reachable=yes。
• Possibly lost, or "dubious":
只能找到指向block中间位置的指针。这个指针有可能原来指向起始位置,并进行了偏移操作,或者它已完全无关。Memcheck将这样的块认为是"dubious",因为不清楚是否仍然有指针指向它。
• Definitely lost, or "leaked":
最坏的结果是没有指针指向这些block。这些block被认为是泄露了。
间接泄露是指被另一个泄露的block指向的block。
Memcheck搜索指针的精确内存区域: Memcheck记录下的所有能访问且已经初始化的所有按机器字自然对齐的内存。
3.3.1 比较完整的跟踪内存泄露的命令
从上可以看出比较完整的跟踪内存泄露的命令行是: valgrind --tool=memcheck --leak-check=full --show-reachable=yes --track-origins=yes prog_name prog_param
注意: prog_name尽量不要采用shell脚本
3.4 显示参数的说明
==19543== Source and destination overlap in strcpy(0xBEFFD1F0, 0xBEFFD1F0)
==19543== at 0x401DD8B: strcpy (mc_replace_strmem.c:268)
==19543== 是进程号, 第一行是错误描述信息, 接下来几行是错误发生的位置。