Valgrind原理和检测S++插件内存泄漏的实例方法

来源:互联网 发布:行业大数据平台 编辑:程序博客网 时间:2024/06/08 19:27

0、背景

      前几天用valgrind解决了一个台server的内存泄漏(图片上传到存储平)的问题,下面是valgrind的原理和使用方法,感兴趣的同学可以看一下,希望对大家有帮助。J

1、介绍

Valgrind是一款用于内存调试、内存泄漏检测以及性能分析的软件开发工具。Valgrind这个名字取自北欧神话中英灵殿的入口。Valgrind的最初作者是Julian Seward,他于2006年由于在开发Valgrind上的工作获得了第二届Google-O'Reilly开源代码奖。你可以在它的环境中运行你的程序来监视内存的使用情况,比如C语言中的mallocfree或者C++中的newdelete。如果你使用了未初始化内存,在数组末端外设置内存或是忘记释放指针,Valgrind都可以检测出来。

 

1.1、Valgrind的主要功能

Valgrind工具包包含多个工具,如Memcheck,Cachegrind,Helgrind, Callgrind,Massif。Valgrind由内核(core)以及基于内核的其他调试工具组成。内核类似于一个框架(framework),它模拟了一个CPU环境,并提供服务给其他工具;而其他工具则类似于插件 (plug-in),利用内核提供的服务完成各种特定的内存调试任务。架构如下所示:

 

下面分别介绍个工具的作用:

(1) Memcheck 工具主要检查下面的程序错误:

    使用未初始化的内存 (Use of uninitialised memory)

    使用已经释放了的内存 (Reading/writing memory after it has been free’d)

    使用超过 malloc分配的内存空间(Reading/writing off the end of malloc’d blocks)

    对堆栈的非法访问 (Reading/writing inappropriate areas on the stack)

    申请的空间是否有释放 (Memory leaks – where pointers to malloc’d blocks are lost forever)

    malloc/free/new/delete申请和释放内存的匹配(Mismatched use of malloc/new/new [] vs free/delete/delete [])

    src和dst的重叠(Overlapping src and dst pointers in memcpy() and related functions)

   

 (2) Callgrind收集程序运行时的一些数据,函数调用关系等信息,还可以有选择地进行cache 模拟。在运行结束时,它会把分析数据写入一个文件。callgrind_annotate可以把这个文件的内容转化成可读的形式。

Cachegrind模拟 CPU中的一级缓存I1,D1和L2二级缓存,能够精确地指出程序中 cache的丢失和命中。如果需要,它还能够为我们提供cache丢失次数,内存引用次数,以及每行代码,每个函数,每个模块,整个程序产生的指令数。这对优化程序有很大的帮助。

 

(3) Helgrind它主要用来检查多线程程序中出现的竞争问题。Helgrind

寻找内存中被多个线程访问,而又没有一贯加锁的区域,这些区域往往是线程之间失去同步的地方,而且会导致难以发掘的错误。Helgrind实现了名为”Eraser” 的竞争检测算法,并做了进一步改进,减少了报告错误的次数。

 

(4) Massif堆栈分析器,它能测量程序在堆栈中使用了多少内存,告诉我们堆块,堆管理块和栈的大小。Massif能帮助我们减少内存的使用,在带有虚拟内存的现代系统中,它还能够加速我们程序的运行,减少程序停留在交换区中的几率。

 

这里主要使用的是Memcheck,适用于Memcheck工具的相关选项:

   1.

      -leak-check=no|summary|full 要求对leak给出详细信息? [summary]

   2.

      -leak-resolution=low|med|high how much bt merging in leak check [low]

   3.

      -show-reachable=no|yes show reachable blocks in leak check? [no]

 

1.2、安装valgrind

 

(1) 下载:http://valgrind.org/

 

(2) 解压:

tar  jvxf  valgrind-3.6.0.tar.bz2

 

(3) 安装:

./configure
make
make install

 

1.3、Memcheck检测原理

Memcheck 能够检测出内存问题,关键在于其建立了两个全局表。

 

   (1) Valid-Value 表:

对于进程的整个地址空间中的每一个字节(byte),都有与之对应的 8 个 bits;对于 CPU 的每个寄存器,也有一个与之对应的 bit 向量。这些 bits 负责记录该字节或者寄存器值是否具有有效的、已初始化的值。

 

   (2) Valid-Address 表

对于进程整个地址空间中的每一个字节(byte),还有与之对应的 1 个 bit,负责记录该地址是否能够被读写。

 

检测原理:

 

    * 当要读写内存中某个字节时,首先检查这个字节对应的 A bit。如果该A bit显示该位置是无效位置,memcheck 则报告读写错误。

    * 内核(core)类似于一个虚拟的 CPU 环境,这样当内存中的某个字节被加载到真实的 CPU 中时,该字节对应的 V bit 也被加载到虚拟的 CPU 环境中。一旦寄存器中的值,被用来产生内存地址,或者该值能够影响程序输出,则 memcheck 会检查对应的V bits,如果该值尚未初始化,则会报告使用未初始化内存错误

2、Valgrind实例调试S++插件

 

2.1 内存泄漏的现象

 

由下图可以看出机器的内存使用量每天都在上涨,初步判定存在内存泄漏的问题。

 

 

2.2 调试步骤

 

步骤1:用valgrind起SPP的worker程序来检测内存泄漏,命令如下图所示。proxy和ctrl进程无需调试的话只需用./spp_actuploadpic_proxy ../conf/spp_proxy_actuploadpic.xml将proxy启动即可。

这里需要调试的SO需使用 -g选项编译,这样就有可能获得一些信息来直接指出相关的代码行。

启动后valgrind会输出一个启动时的summary检查报告,因为没有请求,并未发现启动worker时存在内存泄漏,如下所示:

 

步骤2:上一步骤只启动一个worker是为了方便观察和调试,用watch简单观察一下worker(进程号是15758)的内存使用,注意这里VSZ虚拟内存占用是以k为单位的,如果是少量的内存泄漏是看不出来的

 

步骤3:模拟一个请求给SPP,worker的内存使用量上涨了,初步判断是有内存泄漏的。

 

步骤4:kill -USR1 15758杀掉valgrind起的worker进程,valgrind会输出汇总的报告,如下所示:

报告内容较多,也存在误报的情况,这里需要特别关注自己编写server的代码,如果公共库和API一般经过测试比较稳定了,泄漏的可能性不大。

 

Memcheck将内存泄露分为两种,一种是可能的内存泄露(Possibly lost),另外一种是确定的内存泄露(Definitely lost)。Possibly lost 是指仍然存在某个指针能够访问某块内存,但该指针指向的已经不是该内存首地址。Definitely lost 是指已经不能够访问这块内存。需要重点关注Definitely lost的信息。

 

 

以最后的一块提示信息为例,说明如下:

==15758== 是worker进程的 ID。

消息“34,389 bytes in 3 blocks are definitely lost in loss record 31 of 33”说明有33k的数据没有释放,是在268行(vg_replace_malloc.c)的new进行分配的,该分配是由server_ctl.cpp文件upload_picture()函数的538行调用的。

 

步骤5:根据提示找到相应的源代码,找到内存泄漏的地方在538行

 

其它参考资料:

http://blog.ednchina.com/junjinlee/1772918/message.aspx