c++分析gperftools总结

习惯了golang的net/http/pprof的便利,c++的性能分析就显得繁琐了一点。

不过大致上还是一致的。

安装

包管理安装

1
2
3
4
5
centos:
yum install google-perftools google-perftools-devel

ubuntu:
apt-get install google-perftools google-perftools-devel

64位系统还需要安装unwind(google-perftools出现的时候64位还没普及,现在libunwind应该是必装了)

1
2
3
4
5
centos:
yum install libunwind

ubuntu:
apt-get install libunwind8 libunwind-dev

这样就完毕了,简单的很,但是后面在装http接口支持库的时候会缺失一个stacktrace库。

我给出了一个现成的stacktrace静态库,但是如果遇到兼容性问题只能自己通过源码安装来解决。

没遇到兼容性问题的时候可以忽略下面的源码安装步骤

源码安装

先装libunwind

1
2
3
4
5
6
7
wget http://download.savannah.gnu.org/releases/libunwind/libunwind-0.99-beta.tar.gz
tar xzvf libunwind-0.99-beta.tar.gz
cd libunwind-0.99-beta
autoreconf -i
./configure
make
make install

再装google-perftools

1
2
3
4
5
6
7
wget http://google-perftools.googlecode.com/files/google-perftools-1.6.tar.gz
tar xzvf google-perftools-1.6.tar.gz
cd google-perftools-1.6
./autogen.sh
./configure(装完有问题用./confgiure --enable-frame-pointers, google-perftools的INSTALL里这么写的)
make
make install

使用

编译选项

1
2
3
4
TARGET_LINK_LIBRARIES(${TARGET} tcmalloc)
TARGET_LINK_LIBRARIES(${TARGET} profiler)
TARGET_LINK_LIBRARIES(${TARGET} unwind)
TARGET_LINK_LIBRARIES(${TARGET} -Wl,--eh-frame-hdr) //纯静态编译的时候这个很重要,见[使用可能遇到的问题]一节

cpu profile

cpu使用检测

https://gperftools.github.io/gperftools/cpuprofile.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//SIGUSR1: start profiling
//SIGUSR2: stop profiling

static void gprof_callback(int signum) {
if (signum == SIGUSR1) {
printf("Catch the signal ProfilerStart\n");
ProfilerStart("bs.prof"); //开始profile的api
}
else if (signum == SIGUSR2) {
printf("Catch the signal ProfilerStop\n");
ProfilerStop(); //结束profile的api
}
}

static void setup_signal() {
struct sigaction profstat;
profstat.sa_handler = gprof_callback;
profstat.sa_flags = 0;
sigemptyset(&profstat.sa_mask);
sigaddset(&profstat.sa_mask, SIGUSR1);
sigaddset(&profstat.sa_mask, SIGUSR2);

if (sigaction(SIGUSR1, &profstat,NULL) < 0)
fprintf(stderr, "SIGUSR1 Fail !");

if (sigaction(SIGUSR2, &profstat,NULL) < 0)
fprintf(stderr, "SIGUSR2 Fail !");
}

int main() {
setup_signal();
...
}

对SIGUSR1和SIGUSR2注册信号来进行调优开始和结束

运行后使用pprof看结果

--lines按行统计

--func按函数统计

1
2
3
4
5
6
#文本结果
~ pprof ./out.exe ./bs.prof --lines --edgefraction=1e-10 --nodefraction=1e-10 --text
#火焰图
~ google-pprof ./out.exe ./bs.prof --lines \
--edgefraction=1e-10 --nodefraction=1e-10 --collapsed > /tmp/out.data
~ /root/FlameGraph/flamegraph.pl /tmp/out.data > /tmp/out.svg

heap profile

内存使用检测

https://gperftools.github.io/gperftools/heapprofile.html

1
2
3
4
5
<gperftools/heap-profiler.h>

HeapProfilerStart()
...checkcode...
HeapProfilerStop()

会输出很多个profile.000x.heap文件

如果发现没有输出,可能是内存使用太少了,参数HEAP_PROFILE_ALLOCATION_INTERVAL控制多少内存使用dump一次

默认是1073741824 (1 Gb)

对输出的out.exe使用如下命令来对比出结果

1
pprof --base=/tmp/profile.0004.heap out.exe /tmp/profile.0100.heap

heap checker

内存泄露检测

1
2
3
4
5
6
HeapLeakChecker heap_checker("test_foo");
{
code that exercises some foo functionality;
this code should not leak memory;
}
if (!heap_checker.NoLeaks()) assert(NULL == "heap memory leak");

执行完毕以后使用HEAPCHECK=local ./out.exe或者更严格的HEAPCHECK=draconian ./out.exe来检测泄露

如果存在泄漏的话,会assert停掉,并且提示你运行命令去跑pprof看泄漏点,例如

1
2
3
4
5
6
7
8
9
10
11
12
Leak of 4 bytes in 1 objects allocated from:
@ 7f822f3bec92
@ 7f822f3c262d
@ 7f822f3b7942
@ 7f822f5bfb9a
@ 7f822f5bfca1
@ 7f822f5af13a


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

pprof ./out.exe "/tmp/out.exe.52933._main_-end.heap" --inuse_objects --lines --heapcheck --edgefraction=1e-10 --nodefraction=1e-10 --text --cum

如果是ubuntu20的话,pprof命令变成了google-pprof,这个挺坑的

要注意活动对象检测问题,local模式没问题以后最好在用draconian跑一次看看

“活动对象”检测:在程序执行的任意时刻,所有可访问的内存都被认为是活动的对象。这包括所有的全局变量以及全局指针变量指向的内存,所有从当前stack frame及CPU寄存器可达的所有内存,还包括线程本地存储、thread heaps、以及从线程本地存储、thread heaps可达的内存空间。对于除“draconian”之外的所有模式来说,活动对象都不会被视为是内存泄露。

不推荐直接使用gooogle-perftools

这和golang直接使用http接口来进行性能分析比麻烦了很多,万幸的是google也提供了http接口的支持

配合http接口体验更好

http接口安装

库名是gperftools-httpd,包管理没有,必须进行源码安装

google code因为已经被google放弃了,因此不能下载了,github有个可以用的

1
git clone https://github.com/awesomeleo/gperftools-httpd

直接使用会遇到一些问题

这里有个我fork的库,已经完成了下面步骤一。在centos 6下可以和包管理的gperftools一起使用,但并不保证你一定可用

gperftools-httpd

如果不可用需要自己重复步骤一

步骤一 配置

这是个c的库,c++要使用的话需要修改gperftools-httpd.h

1
2
3
extern "C" {
extern void ghttpd(void);
}

然后编译的时候会报错找不到libstacktrace

libstacktrace是只有gperftools在源码编译以后,在编译结果的目录里才能找到的

1
cp gperftools/.libs/libstacktrace.a gperftools-httpd

记得修改Makefile去当前目录找库

步骤二 安装

1
2
3
cd gperftools-httpd
make
make install

http接口使用

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include "gperftools-httpd.h"

using namespace std;

int main(int argc,char** argv)
{
ghttpd();
int i;
while(1) i++;
}
1
2
g++ -g -O0 -o  main gperftool-test.cpp -lghttpd -lprofiler -lpthread
./main

使用http接口查看

1
2
3
4
5
6
7
8
9
10
11
[root@localhost.localdomain ~]# pprof  ./main  http://localhost:9999/pprof/profile
Using local file ./main.
Gathering CPU profile from http://localhost:9999/pprof/profile?seconds=30 for 30 seconds to
/root/pprof/main.1534407852.localhost
Be patient...
Wrote profile to /root/pprof/main.1534407852.localhost
Welcome to pprof! For help, type 'help'.
(pprof) top 10
Total: 3006 samples
2997 99.7% 99.7% 2997 99.7% main
9 0.3% 100.0% 9 0.3% idle

使用可能遇到的问题

运行时出现:No nodes to print

程序逻辑使用CPU次数太少,无法分析

运行时出现:SIGPROF handler is already in use

使用了其它的性能分析工具如:gprof等,造成冲突

pg选项

要注意有没有带pg选项,由于和gprof是冲突的,带了pg选项会无法使用

静态编译后性能变差

大约性能会下降10-20倍

根据gperftools文档

INSTALL document to fix hung bug of libunwind

CAUTION: if you install libunwind from the url above, be aware that you may have trouble if you try to statically link your binary with perftools: that is, if you link with 'gcc -static -lgcc_eh ...'. This is because both libunwind and libgcc implement the same C++ exception handling APIs, but they implement them differently on some platforms. This is not likely to be a problem on ia64, but may be on x86-64.

Also, if you link binaries statically, make sure that you add -Wl,--eh-frame-hdr to your linker options. This is required so that libunwind can find the information generated by the compiler required for stack unwinding.

Using -static is rare, though, so unless you know this will affect you it probably won't.

纯静态链接-static需要添加-Wl,--eh-frame-hdr选项

总结

简单探究了如何使用google-perftools工具配合http接口查看cpu分析结果

后续遇到问题还会继续更新

参考资料

C++ Profiler工具之初体验

gperftools 使用经验总结