valgrind的使用

一. 简述valgrind是什么,为何能进行内存泄露
valgrind是一个程序调试及性能分析的工具集,涵盖memcheck, cachegrind,helgrind,callgrind,启动valgrind时通过–tool来指定具体要调用的工具。不论使用哪个工具,通过valgrind来启动程序时都会取得对程序的控制权,从关联库中读取调试信息。然后在valgrind核心提供的虚拟CPU上运行程序,根据选择的工具处理代码,该工具会向代码中植入检测代码,并把这些代码作为最终代码返回给valgrind核心,最后valgrind核心运行这些代码。

我们最常用valgrind进行内存泄露检查,通过–tool = memcheck --leak-check=yes指定。需要注意的是,memcheck会加入代码检查每一片内存的访问和进行值运算,导致整体代码大小至少增加12倍,运行速度比平时慢25-50倍,所以使用valgrind时,保证机器环境有足够多的内存,如果进程本身启动内存有十几G,那用valgrind启动程序时,一般启动特别慢,可能1h才能启动程序。或者,valgrind根本就拉不起来程序,此时,需要修改程序的相关配置参数,想办法将程序启动的进程内存减小。

二.编译程序时的注意

  1. 编译时,打开调试模式(gcc编译器的-g选项)。

  2. 编译时,关闭编译优化选项。一些编译优化选项(比如-O2或者更高的优化选项),可能会使得memcheck提交错误的未初始化报告,因此,为了使得valgrind的报告更精确,在编译的时候最好不要使用优化选项。

  3. 如果程序有对tcmalloc编译依赖的话,需要将tcmalloc从编译依赖中去掉,否则valgrind扫描可能会遗漏一些报警信息。

  4. 当检查的是C++程序的时候,还应该考虑另一个选项 -fno-inline。它使得函数调用链很清晰,这样可以减少你在浏览大型C++程序时的混乱。比如在使用这个选项的时候,用memcheck检查openoffice就很容易。当然,你可能不会做这项工作,但是使用这一选项使得valgrind生成更精确的错误报告和减少混乱。

三. valgrind下载 & 安装
—> 下载:wget http://www.valgrind.org/downloads/valgrind-3.8.1.tar.bz2

目前最新的版本是:Release 3.12.0 20 October 2016

—> 解压:bzip2 -d valgrind-3.8.1.tar.bz2; tar -xvf valgrind-3.8.1.tar

—> 安装: cd valgrind-3.8.1; ./configure --prefix=/home/work/tools/valgrind-3.8.1.install;make;make install

安装好后在valgrind-3.8.1.install目录里有: bin, lib, include, share 4个子目录

—> 确认是否安装正确:valgrind --version

—> 编辑/etc/profile,将valgrind工具加入PATH,再source /etc/profile,这样每次使用时都是新安装的valgrind

四、valgrind memcheck选项
参见手册http://www.valgrind.org/docs/manual/mc-manual.html

valgrind的选项非常的多,可以参考翻译手册中的说明进行选用,下面列出几个比较常用的:

Option :–leak-check=no|summary|full|yes  [default summary]

Purpose : 当这个选项打开时,会当客户程序结束时查找内存泄露。如果设置为summary,Valgrind会报告有多少内存泄露发生了。如果设置为full或yes,Valgrind给出每一个独立的泄露的详细信息。

Option :–leak-resolution=high|med|low  [default low]

Purpose :在做内存泄漏检查时,确定memcheck将怎么样考虑不同的栈是相同的情况。当设置为low时,只需要前两层栈匹配就认为是相同的情况;当设置为med,必须要四层栈匹配,当设置为high时,所有层次的栈都必须匹配。

注意–leak-resolution= 设置并不影响memcheck查找内存泄漏的能力。它只是改变了结果如何输出。

Option: --show-reachable=yes|no  [default no]

Purpose : 当这个选项关闭时,内存泄露检测器只显示没有指针指向的内存块,或者只能找到指向块中间的指针。当这个选项打开时,内存泄露检测器还报告有指针指向的内存块。这些块是最有可能出现内存泄露的地方。你的程序可能,至少在原则上,应该在退出前释放这些内存块。这些有指针指向的内存块和没有指针指向的内存块,或者只有内部指针指向的块,都可能产生内存泄露,因为实际上没有一个指向块起始的指针可以拿来释放,即使你想去释放它。

Option: -v

Purpose : 显示详细信息。在各个方面显示你的程序的额外信息,例如:共享对象加载,使用的重置,执行引擎和工具的进程,异常行为的警告信息。重复这个标记可以增加详细的级别。

Option : -fno-inline

Purpose : 当检查的是C++程序的时候,可考虑这个选项-fno-inline。它使得函数调用链很清晰

Option : --max-stackframe=[default: 2000000]

Purpose : 栈的最大值,默认2000000。如果栈指针的偏移超过这个数量,valgrind则会认为程序是切换到了另外一个栈执行。

五. 内存泄露扫描过程
—> 程序启动(如果程序本身内存十几个G,启动非常慢,可能1h多才启动):

nohup valgrind–tool=memcheck --leak-check=full --show-reachable=yes

–max-stackframe=8000000 --log-file=./valgrind.log [程序启动命令] &

—> 判断程序是否启动成功:

搜索程序端口是否被监听来判断程序是否被valgrind拉起

netstat -anlp|grep 9600|grep LISTEN   ##如果进程名是valgrind,则表示程序已经在valgrind环境中启动成功。

—> 给程序放一些请求,请求尽可能覆盖到程序的各个分支

—> 程序优雅退出,不要让服务在valgrind环境里core掉了。尽量服务有捕获kill信号能力,一般kill -9 pid会导致服务core掉。

—> 退出等待个几分钟,让valgrind收集退出信息输出到valgrind.log。最后分析valgrind.log

六. valgrind.log 报告分析
见手册http://www.valgrind.org/docs/manual/mc-manual.html ,关注关注definitely lost和possible lost处可能的内存泄露。

1)首先搜关键字『LEAK SUMMARY』,看『definitely lost』对应是否有值,如果不为0,肯定有内存泄露。

2)如果有内存泄露,往上搜报告中『definitely lost in loss』的报警处,对照程序代码一一排查。

3)常见内存泄露警告

—> a. Illegal read / Illegal write errors

已释放后的内存进行读/写或数组的越界访问等,valgrind报告程序在尝试读写非法内存,造成的后果是程序易发生segmentation fault(吐core)风险。

Invalid write of size X,访问超出了范围的内存,试图从该内存读取数据

—> b. Use of uninitialised values

检测使用未初始化变量,可以检测在条件判断语句中使用未初始化变量,因此应该养成在声明变量时就进行初始化的习惯

Conditional jump or move depends on uninitialised value(s)

—> c. When a heap block is freed with an inappropriate deallocation function

使用不正确的方法释放内存,比如new/delete , malloc/free使用混淆了,new分配的地方用free释放了

Mismatched free() / delete / delete []

—> d. Illegal frees

重复内存释放,比如2次使用free释放同一块内存

Invalid free() / delete / delete[]

—> e. Overlapping source and destination blocks

针对C语言常见的memcpy, strcpy, strncpy, strcat, strncat拷贝类函数,出现源串和目标串地址重叠

Source and destination overlap in memcpy(0xbffff294, 0xbffff280, 21)

—> f. Fishy argument values

所有内存分配类函数在分配内存时,都会传入一个内存分配大小的数,如果你传入的数大于机器所能分配的最大数时,如64位机器上,你传入的size大于了2**63

Argument ‘size’ of function malloc has a fishy (possibly negative) value: -3

—> g. Leak detecion

HEAP SUMMARY:

如果指定–show-reachable=yes,在程序退出时memcheck会收集reachable and indirectly lost blocks

LEAK SUMMARY:

memcheck会记录由malloc/new等函数创建的所有的heap blocks,因此在程序退出时,memcheck能够知道哪些block没有free。如下:

definitely lost: 4 bytes in 1 blocks

indirectly lost: 0 bytes in 0 blocks

possibly lost: 0 bytes in 0 blocks

still reachable: 95 bytes in 6 blocks

definitely lost:如果一个block在程序在退出后,memcheck找不到指向它的pointer,一般是由于在代码中对该block没有free造成的,要重点关注。

possibly lost:在程序退出时,memcheck发现仍有 interior pointer(如果一个pointer指向一个block的中间某个位置)指向一个block,那么该block被认为是possibly lost。

still reachable:如果memcheck发现仍有start pointer指向一个block,那么该block就是still reachable。

4)valgrind不能查出哪些错误?

valgrind不对静态数组(分配在栈上)进行边界检查。如果在程序中声明了一个数组:

int main()

{

char x[10];

x[11] = ´a´;

}

valgrind则不会警告你,出于测试目的,你可以把数组改为动态在堆上分配的数组,这样就可能进行边界检查了。这个方法好像有点得不偿失的感觉。

七. 安装或使用中踩得各种坑
1)valgrind: failed to start tool ‘memcheck’ for platform ‘amd64-linux’

解决办法:

该错误是valgrind启动时,无法找到相关工具文件,有可能是安装好后相关lib的path未设置,也有可能是一开始安装就有问题。解决办法如下:

如果已经安装成功,试一下导出VALGRIND_LIB路径,用法如下(假设valgrind已经被安装到/home/work/tools/valgrind目录):##亲测有效##

export VALGRIND_LIB=/home/work/tools/valgrind3.8.1/lib/valgrind

2)valgrind --version报错

valgrind: mmap((nil), 134512640) failed during startup.

valgrind: is there a hard virtual memory limit set?

解决办法:

见下,版本太老,里边有bug,重新安装一下较新版本应该能解决。https://twiki.cern.ch/twiki/bin/view/LHCb/CodeAnalysisTools

3)vg_alloc_ThreadState: no free slots available

Increase VG_N_THREADS, rebuild and try again.

valgrind: the ‘impossible’ happened:

VG_N_THREADS is too low

解决办法:

a) 修改VG_N_THREADS和NTHREADS的值,安装用的是3.8.1版本,默认VG_N_THREADS是500,现在改为5000,同时NTHREADS由498改成4998。

include/pub_tool_threadstate.h: #define VG_N_THREADS 5000

./memcheck/tests/err_disable4.c: #define NTHREADS 4998  // VG_N_THREADS - 2

b) 按照1进行重新安装。

  1. Valgrind: FATAL: VG_N_SEGMENTS is too low.

解决办法:

VG_N_SEGMENTS默认是5000。修改coregrind/m_aspacemgr/aspacemgr-linux.c: VG_N_SEGMENTS从5000改到50000,VG_N_NAME从1000改到5000。

按照1进行重新安装。

5)valgrind.log报告中,在definitely lost的代码处全是?????全写花了,看不出具体泄露的是在哪一行代码

解决办法:

加上-g,打开调试信息重新编译程序;

valgrind扫描时加上–show-reachable=yes。

参考:

  1. http://www.xlgps.com/article/216977.html

  2. 如何使用Valgrind memcheck工具进行C/C++的内存泄漏检测

3. 用Valgrind查找内存泄漏和无效内存访问

4. valgrind介绍

作者:Shirley_奋进中的虾米
链接:https://www.jianshu.com/p/9c732c91dc79
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搁浅的渔

创作不易,多多支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值