Howto: 在FreeBSD下使用google perftools对C/C++程序做性能分析


这篇文章不是google perftools的Quick Start,阅读本文之前请先阅读官方文档 https://gperftools.github.io/gperftools/cpuprofile.html。这篇是讲在FreeBSD下怎么能让CPU Profiler正确的跑起来,因为做性能分析的时候需要得到当前栈的函数信息,这个不容易拿到。下面仅针对64位的FreeBSD讲。
当我们进行函数调用的时候,每进入一个函数,首先就会向栈上push一个指针,记录的是当前的eip,这样在子函数执行完的时候,才能知道该从哪条指令继续执行下去。于是我们通过回溯栈上的内容,就可以得到一层层的函数地址。
进行栈回溯有很多种方式:
  1. libunwind。需要注意的是,google perftools的1.10版本,需要配合libunwind 0.99-beta使用。libunwind 的版本很重要,太老或者太新,都不推荐。采用这种方式,那么需要注意的是尽量不要用静态链接。在静态链接的时候要加上-Wl,--eh-frame-hdr。据报道,libunwind 0.99-beta在Linux下可能会引起你的程序crash,如果确实遇到了这个问题,那么就换1.0.1。http://groups.google.com/group/google-perftools/msg/2686d9f24ac4365f
  2. 使用google perftools自带的。使用这种方式的要求是:在编译所有库,以及自己的程序的时候都加上-fno-omit-frame-pointer。
  3. devel/libexecinfo。这个是通过gcc的内置函数__builtin_frame_address来得到栈信息。但是不知道为什么,这种方式下有时候进行backtrace的时候会崩溃。详情请见:https://www.freebsdchina.org/forum/viewtopic.php?t=53029 。使用这种方式的要求是,所有的库在链接的时候都要加上-Wl,--export-dynamic。
综上所述,google最为推荐的方式是:使用libunwind,并采用动态链接。(ports中的google perftools采用的是第二种方式)
但是光得到函数地址不够。你光知道0x00000008012b2b0c这个函数被调用的次数最多,那么它到底是哪个函数呢?!如果这个函数是主程序 ELF中的函数(而不是动态库里的),那么相对还比较简单。如果是libc里面的……
要想把所有的函数地址能正确的解析到函数名,必须满足以下条件:
  1. 在/proc上挂载procfs。注意是FreeBSD的procfs,不能是linux的linprocfs。如果这个不正确,那么生成的prof数据文件中,会缺少Text List of Mapped Objects这一段。
  2. 所有的库必须有符号信息。默认安装的libc.so等等,都是被strip过的。所以要在/etc/make.conf中加上一行,"STRIP=”。然后重新make world。CentOS/Red Hat企业版的用户不必有此烦恼,因为用FreeBSD的人都是纯爷们,从来不考虑崩溃之后怎么办,gdb看不到函数名怎么办。
  3. 给pprof这个脚本打patch。详情可见ports下的patch文件。
  4. if ($l =~ /^($h)-($h)\s+..x.\s+($h)\s+\S+:\S+\s+\d+\s+(\S+\.(so|dll|dylib|bundle)((\.\d+)+\w*(\.\d+){0,3})?)$/i) {
  5. if ($l =~ /^($h)-($h)\s+..x.\s+($h)\s+\S+:\S+\s+\d+\s+(\S+\.(so|dll|dylib|bundle)((\.\d+)+\w*(\.\d+){0,3})?)\s+[A-Z]+\s+[\-0-9]+$/i) {
  6. 如果用的是gcc 4.6,采用-ggdb3生成调试信息,那么系统自带的nm/objdump等工具是用不了的。所以在运行pprof的时候要加上"--tools=/usr/local/bin/”(注意不要少最后那个斜杠) ,使用从ports中安装的nm/objdump等。
这当中最痛苦的莫过于make world ……

此博客中的热门博文

少写代码,多读别人写的代码

在windows下使用llvm+clang

tensorflow distributed runtime初窥