gperftools是google出品的一个性能分析工具,相关介绍可见:
https://github.com/gperftools/gperftools/wiki
gperftools性能分析通过抽样方法完成,默认是1秒100个样本,即一个样本是10毫秒,因此程序运行时间要长一些。

1、安装gperftools

1.1、安装automake

sudo apt-get install automake

1.2、编译安装libunwind

从https://github.com/libunwind/libunwind/releases下载最新版本的libunwind源码包
解压到/usr/local/src目录
cd 解压源码目录
./autogen.sh
./configure
make -j6
make install

1.3、编译安装gperftools

从https://github.com/gperftools/gperftools/releases下载最新版本的gperftools源码包
解压到/usr/local/src目录
cd 解压源码目录
./autogen.sh
./configure
make -j6
make install

1.4、安装图像分析生成工具

sudo apt-get install graphviz

2、使用

2.1、运行一段时间就会正常退出的程序的性能分析

这种情况,我们可以直接在代码中插入性能分析函数。示例代码如下:

#include <gperftools/profiler.h>
#include <stdlib.h> void f()
{
int i;
for (i=; i<*; ++i)
{
char *p = (char*)malloc(**);
free(p);
}
} int main()
{
ProfilerStart("test.prof");//开启性能分析
f();
ProfilerStop();//停止性能分析
return ;
}

编译运行,注意编译时需要连接tcmalloc和profiler库。运行后会生成test.prof文件,然后用pprof就可以生成text的分析报告,具体如下:

root@ubuntu:/home/zte/test/perf# gcc not_run_alway.c -ltcmalloc -lprofiler
root@ubuntu:/home/zte/test/perf# ./a.out
PROFILE: interrupts/evictions/bytes = //
root@ubuntu:/home/zte/test/perf# pprof --text a.out test.prof
Using local file a.out.
Using local file test.prof.
Total: samples
21.4% 21.4% 21.4% SpinLock::Unlock (inline)
21.4% 42.9% 21.4% __GI_madvise
14.3% 57.1% 14.3% SpinLock::Lock (inline)
7.1% 64.3% 7.1% TCMalloc_PageMap2::get (inline)
7.1% 71.4% 28.6% do_malloc_pages
7.1% 78.6% 14.3% tcmalloc::PageHeap::Delete
7.1% 85.7% 14.3% tcmalloc::PageHeap::New
7.1% 92.9% 28.6% tcmalloc::PageHeap::ReleaseAtLeastNPages
7.1% 100.0% 7.1% tcmalloc::PageHeap::RemoveFromFreeList
0.0% 100.0% 14.3% SpinLockHolder (inline)
0.0% 100.0% 21.4% TCMalloc_SystemRelease
0.0% 100.0% 100.0% __libc_start_main
0.0% 100.0% 100.0% _start
0.0% 100.0% 28.6% do_allocate_full (inline)
0.0% 100.0% 71.4% do_free_pages
0.0% 100.0% 28.6% do_malloc (inline)
0.0% 100.0% 100.0% f
0.0% 100.0% 100.0% main
0.0% 100.0% 7.1% tcmalloc::PageHeap::Carve
0.0% 100.0% 21.4% tcmalloc::PageHeap::DecommitSpan
0.0% 100.0% 7.1% tcmalloc::PageHeap::GetDescriptor (inline)
0.0% 100.0% 28.6% tcmalloc::PageHeap::IncrementalScavenge
0.0% 100.0% 7.1% tcmalloc::PageHeap::MergeIntoFreeList
0.0% 100.0% 21.4% tcmalloc::PageHeap::ReleaseLastNormalSpan
0.0% 100.0% 28.6% tcmalloc::allocate_full_malloc_oom
0.0% 100.0% 21.4% ~SpinLockHolder (inline)

输出数据解析:
每行包含6列数据,依次为:
1 分析样本数量(不包含其他函数调用)
2 分析样本百分比(不包含其他函数调用)
3 目前为止的分析样本百分比(不包含其他函数调用)
4 分析样本数量(包含其他函数调用)
5 分析样本百分比(包含其他函数调用)
6 函数名

样本数量相当于消耗的CPU时间。
整个函数消耗的CPU时间相当于包括函数内部其他函数调用所消耗的CPU时间

运行命令生成函数调用树形式的pdf分析报告:
pprof --pdf a.out test.prof >test.pdf

树上的每个节点代表一个函数,节点数据格式:
1、函数名 或者 类名+方法名
2、不包含内部函数调用的样本数 (百分比)
3、of 包含内部函数调用的样本数 (百分比) #如果没有内部调用函数则这一项数据不显示

2.2 一直运行的程序的性能分析

一直运行的程序由于不能正常退出,所以不能采用上面的方法。我们可以用信号量来开启/关闭性能分析,具体代码如下:

#include <gperftools/profiler.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h> void gprofStartAndStop(int signum) {
static int isStarted = ;
if (signum != SIGUSR1) return; //通过isStarted标记未来控制第一次收到信号量开启性能分析,第二次收到关闭性能分析。
if (!isStarted){
isStarted = ;
ProfilerStart("test.prof");
printf("ProfilerStart success\n");
}else{
ProfilerStop();
printf("ProfilerStop success\n");
}
} void f()
{
int i;
for (i=; i<*; ++i)
{
char *p = (char*)malloc(*);
free(p);
}
} int main()
{
signal(SIGUSR1, gprofStartAndStop); while(){
printf("call f\n");
f();
sleep();//为了防止死循环,导致信号处理函数得不到调度
}
return ;
}

编译运行如下:

root@ubuntu:/home/zte/test/perf# gcc run_always.c -ltcmalloc -lprofiler
root@ubuntu:/home/zte/test/perf# ./a.out

通过kill命令发送信号给进程来开启/关闭性能分析:
用top命令查看进程的PID
kill -s SIGUSR1 PID //第一次运行命令启动性能分析
kill -s SIGUSR1 PID //再次运行命令关闭性能分析,产生test.prof

后续分析报告生成同2.1

用gperftools对C/C++程序进行profile

以及ProfilerStart的使用

什么是perftools

在Linux的C/C++编程的世界里,性能调优一直是个让人头疼的事。最出名的gprof虽然大家都知道, 其用法比较单一(只支持程序从启动到结束的profile),而且对程序的运行时间会有比较大的影响, 所以其profile不一定准确。

valgrind功能十分强大,但profile也一般针对整个程序的运行,很难只对程序运行中的某段时间进行profile。 而且也多少会影响程序的运行,且使用的难度也较大,所以我目前还没尝试。

除去上面的两个常见的工具,之前在公司的项目见过使用Google的gperftools 进行profile的,当时就被他简单的使用方法吸引。而最近维护的服务器也有性能问题,需要做性能调优。 在尝试了多种原始的profile方式之后,我选择了gperftools

如何profile

在gperftools的文档中,就简单的说了下面的方式来进行profile:

gcc [...] -o myprogram -lprofiler
CPUPROFILE=/tmp/profile ./myprogram

是的,在编译和安装了gperftools之后,只需要上面的步骤就可以进行profile了,非常简单。 而profile的结果就保存在/tmp/profile。查看结果只需要用gperftools自带的一个pprof脚本来看就可以:

$ pprof --text ./myprogram /tmp/profile
2.1% 17.2% 8.7% std::_Rb_tree::find

pprof的输出也很直观,不过也还不够好,从这个输出中还不好看出调用关系,包括caller和callee。 而pprof也可以输出图示,还可以输出callgrind兼容的格式,这样就可以用kcachegrind来看profile结果了。

$ pprof --callgrind ./myprogram /tmp/profile > callgrind.res

然后利用kcachegrind打开这个callgrind.res文件就可以看到类似下面的画面(图片来自kcachegrind官网):

这样调优起来就非常直观了。而且这种方式的最大优点是非侵入式,也就是不需要改动一行代码就能够进行profile了。

动态profile

上面说到的方式是通过环境变量来触发profile,而跨度也是整个程序的生命周期。 那如果是想要在程序运行的某段时间进行profile呢?如果我想在程序不结束的情况下就拿到profile的结果呢?

这种情况下就需要用到动态profile的方式了。要实现这种方式,就需要改动程序的代码了,不过也比较简单:

#include <gperftools/profiler.h>

int main()
{
ProfilerStart("/tmp/profile");
some_func_to_profile();
ProfilerStop(); return ;
}

没错,你只需要在你想要profile的函数的开头和结尾加上ProfilerStartProfilerStop调用就可以了。 在ProfilerStop结束之后,profile的结果就会保存在/tmp/profile里面了。 利用这种方式就可以在指定的时间点对程序进行profile了。

最后需要说的一点是,gperftools的profile过程采用的是采样的方式,而且对profile中的程序性能影响极小, 这对于在线或者离线profile都是一个极其重要的特点。

对服务器进行profile

对于后端程序员,每天都要和后台服务器打交道。而服务器的特点是长时间运行而不停止, 在这种情况下要对程序进行profile就比较麻烦。

在这我提供一种方式,使得profile服务器可以很方便,也可以按需profile。

首先要注意的一点是,gperftools提供了两种链接方式——动态库和静态库。 其中动态库链接的方式可以用环境变量和改动代码两种方式进行profile,而静态库只能使用改代码的方式。 乍看起来好像是动态库库的方式比较方便,不过在陈硕的《Linux多线程服务端编程》 中就说过,对于服务器来说,静态编译的方式对于于动态链接有优势,并且部署上也比较方便。 而我自己也是使用的静态链接的方式来使用gperftools的,所以以下假定都是用静态编译。

对于服务器来说,一般的模式是事件循环,而我们也需要在某段时间之内进行profile。 一个很直观的思路是在接受到某种请求的时候开始profile,而接受到另一种请求之后就结束。 那我们就可以用类似下面的代码来实现:


#include <gperftools/profiler.h>

void on_request(Request* req)
{
static bool is_profile_started = false;
if (req->type == START_PROFILE && !is_profile_started)
{
ProfilerStart("/tmp/profile");
is_profile_started = true;
}
else if (req->type == STOP_PROFILE && is_profile_started)
{
ProfilerStop();
is_profile_started = false;
}
else
{
// normal request processing here
}
}

利用来面的代码,我们可以在想要profile的时间段内分别向服务器发送特殊的请求, 这样就可以在不停止服务器的情况下,对服务器进行profile。

当然这种方式会产生安全问题,在有外网请求的服务器上是不能这么用的。 而且gperftools的文档上也说明了,在线上的服务器最好是不要开启profile,而对测试服务器用就好了。

总结

gperftools对于Linux下的服务器profile进行了很大的简化。能够在不改代码或者改极少代码并且 不增加太多的依赖的情况下,对服务器进行在线profile。有了gperftools,Linux程序员的生活可以又轻松一些了!

http://airekans.github.io/cpp/2014/07/04/gperftools-profile

gperftools对程序进行分析的更多相关文章

  1. [原创]推荐一款强大的.NET程序内存分析工具.NET Memory Profiler

    [原创]推荐一款强大的.NET程序内存分析工具.NET Memory Profiler 1 官方网站:http://memprofiler.com/2 下载地址:http://memprofiler. ...

  2. VS2010/MFC编程入门之四(MFC应用程序框架分析)

    VS2010/MFC编程入门之四(MFC应用程序框架分析)-软件开发-鸡啄米 http://www.jizhuomi.com/software/145.html   上一讲鸡啄米讲的是VS2010应用 ...

  3. 服务器程序源代码分析之三:gunicorn

    服务器程序源代码分析之三:gunicorn 时间:2014-05-09 11:33:54 类别:网站架构 访问: 641 次 gunicorn是一个python web 服务部署工具,类似flup,完 ...

  4. 开源GUI-Microwindows之程序入口分析

    **************************************************************************************************** ...

  5. 通过汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的

    秦鼎涛  <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验一 通过汇编一个简单的C程序,分析汇编代码 ...

  6. 通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的

    实验一:通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的 学号:20135114 姓名:王朝宪 注: 原创作品转载请注明出处   <Linux内核分析>MOOC课程http: ...

  7. 01-Coredump核心转存&&Linux程序地址分析【转】

    转自:http://www.itwendao.com/article/detail/404132.html 目录(?)[-] 一Core Dump核心转存 二Linux程序地址分析 一Core Dum ...

  8. Linux安装程序Anaconda分析(续)

    本来想写篇关于Anaconda的文章,但看到这里写的这么详细,转,原文在这里:Linux安装程序Anaconda分析(续) (1) disptach.py: 下面我们看一下Dispatcher类的主要 ...

  9. Python爬虫爬取全书网小说,程序源码+程序详细分析

    Python爬虫爬取全书网小说教程 第一步:打开谷歌浏览器,搜索全书网,然后再点击你想下载的小说,进入图一页面后点击F12选择Network,如果没有内容按F5刷新一下 点击Network之后出现如下 ...

随机推荐

  1. 201772020113 李清华《面向对象程序设计(java)》第17周学习总结

    1.实验目的与要求 (1) 掌握线程同步的概念及实现技术: (2) 线程综合编程练习 2.实验内容和步骤 实验1:测试程序并进行代码注释. 测试程序1: l  在Elipse环境下调试教材651页程序 ...

  2. 【转】【JAVA资料免费下载】158个JAVA免豆精品资料汇总——下载目录

    附件完整版下载地址: http://down.51cto.com/data/431561 附件部分预览~ java中国移动收费系统[源代码] http://down.51cto.com/data/70 ...

  3. FIN_WAIT_2状态解释

    关于网络设备的FIN_WAIT_2状态解释出处:http://hi.baidu.com/netdemon1981/blog/item/584bfbb2aeb1d4acd9335ad9.html 在HT ...

  4. Java源码分析:关于 HashMap 1.8 的重大更新(转载)

    http://blog.csdn.net/carson_ho/article/details/79373134 前言 HashMap 在 Java 和 Android 开发中非常常见 而HashMap ...

  5. 【Debug】逻辑分析仪数据错乱,看波形为信号耦合导致数据错乱,实际上为逻辑分析仪地线没接上!

    如图都有数据的时间段,数据错乱,实际为逻辑分析仪地线未接,接上就不会了.

  6. Linux 添加中文字体库,解决Java 生成中文水印不显示问题

    本机 Windows 环境测试以下代码生成中文水印完全没问题,但是发布到Linux下不显示,一开始以为是报错了没打印出来,搜索发现直接提示中文乱码的或者不显示的,才明白原来是字体库原因,于是开始解决这 ...

  7. UWP简单测试

    随便写下,试试.Net Core与UWP开发,后台WCF XAML <Page x:Class="App3.MainPage" xmlns="http://sche ...

  8. c语言作业01-分支、顺序结构

    1.本章思考总结 1.1思维导图 1.2本章学习体会及代码量学习体会 1.2.1学习体会 这一个星期算是我学习c语言的起点,因为暑假没有提前自学c语言,所以一上课时会觉得比较吃力也难以跟上其他大部分同 ...

  9. 基于WebGL架构的3D可视化平台—实现小车行走路线演示

    小车行走路线演示New VS Old 刚接触ThingJS的时候,写的一个小车开进小区的演示,今天又看了教程中有movePath这个方法就重新写了一遍,其中也遇到了一些问题,尤其突出的问题就是小车过弯 ...

  10. 解决mysql连接linux上mysql服务器的问题

    在远程连接mysql的时候,连接不上,出现如下报错:Lost connection to MySQL server at 'waiting for initial communication pack ...