内存泄露检测工具之gperftools

背景


    C++世界的同学或多或少都应该遇到过内存泄露的问题,一般情况下,如果能够明确相应的业务场景,排查起来是相对容易的,不管是简单粗暴的撸代码还是借助相关的一些内存检测的工具来检测,
基本都能比较快速定位问题来源。但是现实中遇到的比较多的这类问题都是不明确具体业务场景,总的来说就是做了一系列的测试,发现内存有比较明显的上涨,测试结束之后也不见内存有下降。
    通常来说,排查这类问题比较常见的内存检测工具有valgrind和perf。我们一般用的比较多的是valgrind,其使用起来非常简单,没有使用过的同学可以参考我的valgrind文章说明。
valgrind的缺点也是特别明显,其会严重影响被检测进程的性能,在大数据量或者频繁申请内存的场景下,会使节点运行性能出现几十倍的下降,实际遇到比较多的是启动一个UFT节点可能要花几十分钟到几个小时,
如果出错或者要重新测试,要全部从头开始,这使得问题排查的效率特别低。perf工具的使用门槛相对高一些,并且其对linux内核版本有较高的要求,一般环境可能不支持。
在此背景下,希望有一种能够兼顾运行性能与检测准确性的工具能替代valgrind。目前已知的工具有两个,一个是本文要介绍的gperftools
【官网地址: https://github.com/gperftools/gperftools 】, 另一个是在编译期就要引入的sanitize,关于sanitize的使用,目前还未经过实际验证。

gperftools安装

直接在github上下载最新的源码包编译安装即可。安装步骤跟标准的linux源码安装步骤一致:

./configure
make
make install

安装过程中,可能遇到报错:configure: WARNING: No frame pointers **and** no libunwind. Using experimental backtrace capturing via libgcc. Expect crashy cpu profiler.
报错是因为未安装 libunwind-devel,直接yum源安装即可。

内存泄露检测

    gperftools支持内存泄露检测需要依赖tcmalloc,引入tcmalloc的方式有两种:一种是直接编译期引入依赖;另外一种是运行期通过设置相应的环境变量来引入。我们希望对现有程序的影响越小越好,所以本文倾向于使用第二种方式。
完整内存检测的模式有4种:

minimal:在程序初始化期间(即 main 函数运行前)不进行内存泄露检查
normal:正常模式,通过跟踪某块内存是否可以被 live object 来访问,来判断是否出现内存泄露
strict:类似于 normal 模式,但是对全局对象的内存泄露有一些额外的检查
draconian:在该模式下,只有所有申请的内存都被释放,才认为没有出现内存泄露
需要注意的是,gperftools在被检测进程停止时才会正常做内存分配的搜集比对,所以需要确保被检测进程能够正常退出。

简单测试代码

#include 
#include 
#include 

int main()
{
    for(int i=0; i<10; i++)
    {
        char *lpLeak = (char*)malloc(1);
    }
    return 0;
}

检测命令参考

这里做如下假设:
被检测可执行程序是: test
gperftools的安装目录是:/XX
运用gperftools的检测命令如下:

LD_PRELOAD="/XX/lib/libtcmalloc.so" PPROF_PATH=/XX/bin/pprof HEAPCHECK=normal ./test

内存泄露检测工具之gperftools_第1张图片

从上图中,只能看到检测到有泄露,但是并没有指示出具体的代码行,我们可以根据结果提示中的命令行解析heap文件来获取更加详细的信息。
这里有几个注意点:
命令中的 --gv 选项表示图形化展示调用链,一般我们的环境中并未安装相关界面组件,是无法展示的
pprof 命令中指定的可执行程序要使用完整路径

运行结果解析


这里列出两种解析方式,一种是文本方式展示,一种是生成pdf文件。

文本展示
pprof server "/tmp/server.1._main_-end.heap" --inuse_objects --lines --heapcheck  --edgefraction=1e-10 --nodefraction=1e-10 --text --stack > 1.txt
最终可以看到泄露的代码行

pdf导出

pprof server "/tmp/server.1._main_-end.heap" --inuse_objects --lines --heapcheck  --edgefraction=1e-10 --nodefraction=1e-10 --pdf --stack > 1.pdf
pdf文件中以图形化的方式展示了调用链路,效果还是很nice的,重点关注灰色底的有泄露的部分,再根据其找到对应的调用链路即可。
使用pdf导出可能会遇到报错:
sh: dot: 未找到命令
安装 graphviz 即可:
yum install graphviz

生成文件对比

执行内存检测的时候,可以通过设置一些参数来控制检测文件生成,比如每间隔固定时间生成一个dump文件。一个简单的使用场景是可以比较简单的过滤掉被检测进程初始化的内存占用检测。
具体的参数和含义可以参考官方文档:
Gperftools Heap Profiler
HEAP_PROFILE_TIME_INTERVAL 间隔多长时间产生一个dump文件
HEAP_PROFILE_ALLOCATION_INTERVAL 内存消耗超过多少产生一个dump文件
可以使用如下的简单命令对比第一个文件和第三个文件:
# 生成txt
pprof --lines --text --base /tmp/heapprof.0001.heap ./gperf_malloc /tmp/heapprof.0003.heap > diff.txt
# 生成pdf
pprof --lines --pdf --base /tmp/heapprof.0001.heap ./gperf_malloc /tmp/heapprof.0003.heap > diff.pdf

参考文档

1、gperftools官网:
https://github.com/gperftools/gperftools

你可能感兴趣的:(运维,linux,服务器)