Google Heap Profiler

功能及原理

先来大概介绍下Google Heap Profiler,大致有三类功能:

可以分析出在程序的堆内有些什么东西

定位出内存泄露

可以让我们知道哪些地方分配了比较多的内存

大概的原理就是使用tcmalloc 来代替malloc calloc new等等,这样Google Heap Profiler就能知道内存的分配情况,从而分析出内存问题。

 

安装:

一.  安装与简介 

(1) 下载源码包。路径https://code.google.com/p/google-perftools/

(2) ./configure

(3) make (如果报错,则./configure --enable-frame-pointers)

(4) make  install

头文件和库文件分别在/usr/local/inlcude/google/和/usr/local/lib/下。需要设置环境变量export LD_LIBRARAY_PATH=/usr/local/lib。跟valgrind的profiler工具的不同之处是,Google perftools使用在源程序中插入profiler代码的方式,而不是valgrind的虚拟机方式,所以Google perftools以库文件的形式提供了一系列函数接口。为了使用图形化结果还需要安装gv,可使用apt-get安装:sudo apt-get install gv。 

    Google Perftools包括三个工具(注:包括tcmalloc应该是4个),三个工具均支持多线程程序,以下分别介绍。 

 

 

用法

首先需要把tcmalloc链接到我们需要分析的程序中, 当然我们也可以动态load 这个lib,但是为了简单起见,还是推荐大家链接这个lib到自己的程序中。

链接之后,我们接下来的任务就是得到内存分析的dump文件,我们有两种方法:

    1.    静态dump方法:

        直接定义一个环境变量HEAPPROFILE来指定dumpprofile文件的位置,如:/tmp/test.log,它将会在/tmp/目录下生成很多类似/tmp/test.log.0003.heap文件名的文件

        env HEAPPROFILE="/tmp/test.log" /test/testprog

     2.    动态dump方法:

我们可以调用Google Heap Profiler的API来控制什么时候dump出内存的profiler文件,这样更加灵活,为此,我们必须包含heap-profiler.h这个头文件。

HeapProfilerStart() 用来开始内存分析

HeapProfilerStop().  用来终止内存分析

这样就只会在开始和结束之间产生dump profiler文件。

如:

#if 1

#include "acconfig.h"

#ifdefHAVE_GPERFTOOLS_HEAP_PROFILER_H

        #include<gperftools/heap-profiler.h>

#else

        #include <google/heap-profiler.h>

#endif

#ifdefHAVE_GPERFTOOLS_MALLOC_EXTENSION_H

        #include<gperftools/malloc_extension.h>

#else

        #include<google/malloc_extension.h>

#endif

#include"common/environment.h"

#endif

void  heap_profiler_start()

{

 char profile_name[PATH_MAX];

 get_profile_name(profile_name, sizeof(profile_name));

 HeapProfilerStart(profile_name);

}

void  heap_profiler_stop()

{

 HeapProfilerStop();

}

void  heap_profiler_dump(const char *reason)

{

 HeapProfilerDump(reason);

}

bool  heap_profiler_running()

{

 return IsHeapProfilerRunning();

}

void  heap_release_free_memory()

{

 MallocExtension::instance()->ReleaseFreeMemory();

}

void  heap_profiler_stats(char *buf, int length)

{

 MallocExtension::instance()->GetStats(buf, length);

}

 

选项

  •  HEAP_PROFILE_ALLOCATION_INTERVAL

程序内存每增长这一数值之后就dump 一次内存,默认是1G (1073741824)

  •  HEAP_PROFILE_INUSE_INTERVAL

程序如果一次性分配内存超过这个数值dump 默认是100K

查看内存dump文件

这么dump文件生成之后,我们接下来就可以查看内存的分布情况,如:

pprof --pdf /test/testProg/tmp/test.log.0001.heap

就是以pdf的形式来显示这个dump文件,当然我们也可以使用其他的格式来显示。

  

这就是所有可支持的格式。

注:如果pprof 运行出错,请检查时候已经正确安装,如果出现sh: dot: command not found 这个错误,就是需要安装yum install graphviz -y  

我们也可以专门focus在一些包含某些关键字的路径上,也可以忽略相关的路径

--focus

--ignore

pprof --pdf --focus=CData /test/testProg/tmp/test.log.0001.heap

使用形式 

需要安装:graphviz安装:

graphviz有多种安装方式,源码及发行包。

当前最新版源码下载:

http://120.221.32.79:6510/www.graphviz.org/pub/graphviz/stable/SOURCES/graphviz-2.38.0.tar.gz

  pprof的使用形式都是: pprof --option [ --focus=<regexp> ] [ --ignore=<regexp> ] [--line or addresses or functions] 可执行文件路径  对应的profile路径。方括号中的项目是可选项目。<regexp>表示正则表达式

形如:pprof –gif /root/axw/my_biotest/biotestclient.1940324.profile.0055.heap > graph.gif

★options的选项如下:

--text             Generate text report

  --callgrind         Generate callgrindformat to stdout

  --gv               Generate Postscript and display

  --evince           Generate PDF and display

  --web              Generate SVG and display

  --list=<regexp>     Generate source listing ofmatching routines

  --disasm=<regexp>   Generate disassembly of matching routines

  --symbols           Printdemangled symbol names found at given addresses

  --dot              Generate DOT file to stdout

  --ps               Generate Postcript to stdout

  --pdf              Generate PDF to stdout

  --svg              Generate SVG to stdout

  --gif              Generate GIF to stdout

  --raw              Generate symbolized pprof data (useful with remote fetch)

★一些宏

HEAP_PROFILE_ALLOCATION_INTERVAL

程序内存每增长这一数值之后就dump 一次内存,默认是1G (1073741824)

         HEAP_PROFILE_INUSE_INTERVAL

程序如果一次性分配内存超过这个数值dump 默认是100K,

字符统计输出

命令:pprof --text ./RBtree ./RBtree.prof 

关于文本风格输出结果

序号

说明

1

分析样本数量(不包含其他函数调用)

2

分析样本百分比(不包含其他函数调用)

3

目前为止的分析样本百分比(不包含其他函数调用)

4

分析样本数量(包含其他函数调用)

5

分析样本百分比(包含其他函数调用)

6

函数名

字符统计结果: 

         501  62.2%  62.2%      714  88.6% RBTree::insert

          84  10.4%  72.6%       84  10.4% RBTree::defaultCmp

          80   9.9%  82.5%      154  19.1% RBTree::nodeCmp

          61   7.6%  90.1%       73   9.1% RBTree::insertFixup

          47   5.8%  95.9%       47   5.8% malloc_trim

           9   1.1%  97.0%      746  92.6% main

           6    0.7%  97.8%        6   0.7% RBTree::rightRotate

           6   0.7%  98.5%        6   0.7% RBTree::leftRotate

           5   0.6%  99.1%        5   0.6% malloc

           3   0.4%  99.5%        3   0.4% operator new

           3   0.4%  99.9%        3   0.4% random_r

           1   0.1% 100.0%        1   0.1% rand

           0   0.0% 100.0%      755  93.7% __libc_start_main 

每行对应一个函数的统计。第一,二列是该函数的本地采样(不包括被该函数调用的函数中的采样次数)次数和比例,第三列是该函数本地采样次数占当前所有已统计函数的采样次数之和的比例。第四,五列是该函数的累计采样次数(包括其调用的函数中的采样次数)和比例。 

 

Text输出结果分析

14  2.1% 17.2%       58   8.7%std::_Rb_tree::find

含义如下:

14:find函数花费了14个profiling samples

2.1%:find函数花费的profiling samples占总的profilingsamples的比例

17.2%:到find函数为止,已经运行的函数占总的profiling samples的比例

58:find函数加上find函数里的被调用者总共花费的profilingsamples

8.7%:find函数加上find函数里的被调用者总共花费的profilingsamples占总的profiling samples的比例

std::_Rb_tree::find:表示profile的函数

ps: 100samples a second,所以得出的结果除以100,得秒单位

 

图片输出

命令

Pprof --pdf /root/biotest /var/log/client.1940324.profile.0047> graph.gif  生成的gif统计图如下。

结果如图:

 

 

    图中每个节点对应一个函数,节点中的文字分别为类名,函数明,本地采样次数比例和累计采样次数比例(如果跟本地相同则省略)。每条边表示一个函数调用关系:caller调用callee,边上的数字表示callee中因为caller调用而被采样的次数。 

    pprof如果不带任何选项调用(只有可执行文件路径和profile文件路径)则进入互动模式,在互动模式下可使用gv,gif,text等命令来替代前面介绍的带选项的pprof调用。

比较dump文件

命令:pprof --pdf --base /tmp/test.log.0001.heap /test/testProg/tmp/test.log.0101.heap

 

为了知道在某一段时间内的内存分布情况,或者需要了解某段时间内有没有内存泄露,我们就需要用到diff我们的dump文件

例如:pprof --pdf --base /tmp/test.log.0001.heap /test/testProg/tmp/test.log.0101.heap

比较了第一个dump文件与第101个文件的差异,而且结果以pdf的形式显示

Heap Checker 

    堆内存泄漏检测工具。使用简单,先在链接被检查程序的时候用-ltcmalloc选项连接Goolge Perftools的堆内存管理库tcmalloc(tcmalloc会替代C的堆内存管理库),然后每次用命令行“env HEAPCHECK=normal 可执行程序路径”来进行检查,其中检查形式normal可以替换成其他值,检查的结果会以屏幕报告的形式给出。以下给出一个实例: 

# cat test_heap_checker.cpp
#include <cstdio>
#include <cstdlib>
int* fun(int n)
{
    int *p1=new int[n];
    int *p2=new int[n];
    return p2;
}
int main()
{
    int n;
    scanf("%d",&n);
    int *p=fun(n);
    delete [] p;
    return 0;
}
# g++ -O0 -g test_heap_checker.cpp -ltcmalloc -o test_heap_checker 

# env HEAPCHECK=normal /home/hongcheng/mycode/google-perftools-tests/test_heap_checker
WARNING: Perftools heap leak checker is active -- Performance may suffer
100
Have memory regions w/o callers: might report false leaks
Leak check _main_ detected leaks of 400 bytes in 1 objects
The 1 largest leaks:
Leak of 400 bytes in 1 objects allocated from:
If the preceding stack traces are not enough to find the leaks, try running THIS shell command:
pprof /home/hongcheng/mycode/google-perftools-tests/test_heap_checker "/tmp/test_heap_checker.13379._main_-end.heap" --inuse_objects --lines --heapcheck  --edgefraction=1e-10 --nodefraction=1e-10 --gv
If you are still puzzled about why the leaks are there, try rerunning this program with HEAP_CHECK_TEST_POINTER_ALIGNMENT=1 and/or with HEAP_CHECK_MAX_POINTER_OFFSET=-1
Exiting with error code (instead of crashing) because of whole-program memory leaks 

    上面的报告显示有400个字节的内存泄漏,并提示使用pprof进一步跟踪泄漏来源的方法。 

    包括normal在内总共有4种泄漏检查方式:minimal,忽略进入main函数之前的初始化过程;normal,报告所有的无法再引用的内存对象;strick,在normal的基础上增加一些额外的检查;draconian,在程序退出的时候存在未释放的内存的情况下报错。 

    除了前面使用env命令行的全局内存泄漏检查方式外,还可以作对代码段的更加细粒度的泄漏检查。这里需要先在源代码中包含头文件google/heap-checker.h。下面是一个检查代码段的实例: 

       HeapLeakChecker heap_checker("test_foo");
    {
        code that exercises some foo functionality;
        this code should preserve memory allocation state;
    }
    if (!heap_checker.SameHeap()) assert(NULL == "heap memory leak"); 

  在进入代码段之前建立当前堆内存使用情况的snapshot,然后在结束代码段的时候通过与记录的snapshot对比检查是否有泄漏。方法NoLeaks()也可以用在这里。下面是一个实例: 

    #include <cstdio>
    #include <cstdlib>
    #include <cassert>
    #include <google/heap-checker.h>
    int* fun(int n)
    {
        int *p2;
        HeapLeakChecker heap_checker("fun");
        {
            new int[n];
            p2=new int[n];
            //delete [] p1;
        }
        assert(!heap_checker.NoLeaks());
        return p2;    
    }
    int main(int argc,char* argv[])
    {
        int n;
        scanf("%d",&n);
        int *p=fun(n);
        delete [] p;
        return 0;
    } 

    注意被检查程序的main函数形式必须为带2个参数的形式,否则会在编译时报告重复定义。运行env命令行将会报告assert失败。

    另外,还可以跳过某些代码段的检查,方式如下: 

        {
        HeapLeakChecker::Disabler disabler;
        <leaky code>
    } 

  <leaky code>处的代码将被heap-checker忽略。

[root@inspur178 my_biotest]# HEAPCHECK=normal ./biotest -w 1/biotest1/ -s 34 -b 4096

WARNING: Perftools heap leakchecker is active -- Performance may suffer

------processID -> 2391436

thread index : 1 -> /1.dat

begin write...

------processID -> 2391436

input command:  end write .

thread [1] complete !

c:(null) p:(null)

input command:  quit

c:quit p:(null)

wait join all the thread...

all thread over!!!

Have memory regions w/o callers:might report false leaks

Leak check _main_ detected leaksof 144 bytes in 1 objects

The 1 largest leaks:

Using local file ./biotest.

Leak of 144 bytes in 1 objectsallocated from:

    @7fa290af3d88 icfs_os_setxattr

    @7fa290aef8c9 IcfsContext

    @7fa290aebec5 common_preinit

    @7fa2909207bb icfs_create

    @4037bc main

    @7fa28fa23d1d __libc_start_main

    @4016e9 _start

If the preceding stack traces arenot enough to find the leaks, try running THIS shell command:

pprof ./biotest"/tmp/biotest.2391436._main_-end.heap" --inuse_objects --lines--heapcheck  --edgefraction=1e-10--nodefraction=1e-10 --gv

If you are still puzzled aboutwhy the leaks are there, try rerunning this program withHEAP_CHECK_TEST_POINTER_ALIGNMENT=1 and/or withHEAP_CHECK_MAX_POINTER_OFFSET=-1

If the leak report occurs in asmall fraction of runs, try running with TCMALLOC_MAX_FREE_QUEUE_SIZE of fewhundred MB or with TCMALLOC_RECLAIM_MEMORY=false, it might help find leaks morere

Exiting with error code (insteadof crashing) because of whole-program memory leaks

 
 

Google Heap Profiler使用方法

最近在查找内存泄露的问题,使用了一些工具来查找问题,定位问题,但是工具都有各自的优劣,在我们实际的使用过程当中,只有各种工具结合使用才能发挥最大的效用。 由于项目需要,最近也开始使用Google H...
  • jhzhou
  • jhzhou
  • 2012年02月10日 10:12
  • 9922

用google-perftool分析程序的内存/CPU使用

最近,用到了google-perftool分析程序的内存和CPU的使用情况,总结一下使用的一些方法和体会,分享给有需要的朋友。首先,说说google-perftool,它是由google开发的用来分析...
  • zm_21
  • zm_21
  • 2014年06月30日 17:41
  • 1960
 

【揭秘】程序员升职加薪的捷径来了!

在岗5年,总想着闲下来的时候应该如何安排自己的程序人生呢?无意中看到这个!眼睛亮了..

用google-perftool分析程序的内存/CPU使用

最近,用到了google-perftool分析程序的内存和CPU的使用情况,总结一下使用的一些方法和体会,分享给有需要的朋友。首先,说说google-perftool,它是由google开发的用来分析...

ceph存储 Google perftools工具内存检测以及性能分析

一. 安装与简介      从主页http://code.google.com/p/google-perftools/downloads/list下载源码包,解压后使用命令序列./configu...
  • skdkjxy
  • skdkjxy
  • 2015年08月04日 09:17
  • 2236

使用ceoh-deploy工具快速部署ceph--先决条件准备

最近按照ceph的官方文档来部署ceph,发现遇到了不少的问题,其中很多问题都是安装部署的时候遇到的坑,在这里提出来和大家一起分享探讨,如有不足之处欢迎评论。笔者这里使用的操作系统:CentOS7.2...
 

使用ceoh-deploy工具快速部署ceph--部署

最近按照ceph的官方文档来部署ceph,发现遇到了不少的问题,其中很多问题都是安装部署的时候遇到的坑,在这里提出来和大家一起分享探讨,如有不足之处欢迎评论。存储集群部署在controllernode...

OpenStack 整合使用Ceph实现(Copy-On-Write)克隆 (笔记)

本文使用的系统环境: CentOS6.5 四台机器 规划如下: HostName IP        安装服务 c01 192.168.40.101 mon mds osd c02 19...
  • mrz001
  • mrz001
  • 2014年08月28日 14:46
  • 937

如何在ceph中新增google unit test总结

如何在ceph中新增unit test总结

ceph perf counter 源码分析及使用

ceph perf counter 源码分析及使用示例1 enum { 2 test_perfcounter_first = 200, 3 test_perfcount...

google开源的C++性能分析工具 - gperftools

gperftools是Google提供的一套工具,其中的一个功能是CPU profiler,用于分析程序性能,找到程序的性能瓶颈。 安装 gperftools:http://code.google...
原文地址:https://www.cnblogs.com/bokeyuan228/p/8176500.html