查看程序运行热点(perf)

主要介绍一个定位程序热点的工具:perf。

  性能调优工具如 perf,Oprofile 等的基本原理都是对被监测对象进行采样,最简单的情形是根据 tick 中断进行采样,即在 tick 中断内触发采样点,在采样点里判断程序当时的上下文。假如一个程序 90% 的时间都花费在函数 foo() 上,那么 90% 的采样点都应该落在函数 foo() 的上下文中。运气不可捉摸,但我想只要采样频率足够高,采样时间足够长,那么以上推论就比较可靠。因此,通过 tick 触发采样,我们便可以了解程序中哪些地方最耗时间,从而重点分析。

Perf中的子工具
1.         annotate 根据数据文件,注解被采样到的函数,显示指令级别的热点。

2.         archive  根据数据文件中记录的build‐id,将所有被采样到的 ELF文件打成压缩包。利用此压缩包,可以在任何机器上分析数据文件中记录的采样数据。

3.         bench Perf中内置的benchmark,目前包括两套针对调度器和内存管理子系统的benchmark。

4.         buildid‐cache 管理 perf的 buildid 缓存。每个 ELF 文件都有一个独一无二的 buildid。Buildid 被 perf用来关联性能数据与ELF文件。

5.         buildid‐list 列出数据文件中记录的所有buildid。

6.         diff  对比两个数据文件的差异。能够给出每个符号(函数)在热点分析上的具体差异。

7.         evlist  列出数据文件中的所有性能事件。

8.         inject  该工具读取perfrecord工具记录的事件流,并将其定向到标准输出。在被分析代码中的任何一点,都可以向事件流中注入其它事件。

9.          kmem  针对内存子系统的分析工具。

10.     kvm  此工具可以用来追踪、测试运行于KVM 虚拟机上的GuestOS。

11.     list   列出当前系统支持的所有性能事件。包括硬件性能事件、软件性能事件以及检查点。

12.     lock  分析内核中的加锁信息。包括锁的争用情况,等待延迟等。

13.     record  收集采样信息,并将其记录在数据文件中。随后可通过其它工具对数据文件进行分析。

14.     report  读取 perfrecord创建的数据文件,并给出热点分析结果。

15.     sched   针对调度器子系统的分析工具。

16.     script   执行 perl或 python 写的功能扩展脚本、生成脚本框架、读取数据文件中的数据信息等。

17.     stat   剖析某个特定进程的性能概况,包括CPI、Cache 丢失率等。

18.     test  Perf对当前软硬件平台的测试工具。可以用此工具测试当前的软硬件平台(主要是处理器型号和内部版本)是否能支持perf的所有功能。

19.     timechart  生成一幅描述处理器与各进程状态变化的矢量图。

20.     top 类似于 Linux 的 top 命令,对系统性能进行实时分析。

21.     trace strace inspired tool.

22.     probe 用于定义动态检查点

运行命令“perf record -p 32554”后,会产生一个perf.data的文件,使用“perf report”查看结果:




perf record 的-g -e等选项很实用,可以使用perf record -h来查看学习

参考:

http://blog.163.com/121abc_603/blog/static/64394318201382922940555/

http://blog.csdn.net/trochiluses/article/details/10261339

http://blog.chinaunix.net/uid-10540984-id-3854969.html

https://www.ibm.com/developerworks/cn/linux/l-cn-perf1/index.html

这个博客对oprofile的使用写得也很简单明了:

http://www.cnblogs.com/bangerlee/archive/2012/08/30/2659435.html

常用命令

使用oprofile进行cpu使用情况检测,需要经过初始化、启动检测、导出检测数据、查看检测结果等步骤,以下为常用的oprofile命令。

初始化

opcontrol --no-vmlinux : 指示oprofile启动检测后,不记录内核模块、内核代码相关统计数据
opcontrol --init : 加载oprofile模块、oprofile驱动程序
检测控制

opcontrol --start : 指示oprofile启动检测
opcontrol --dump : 指示将oprofile检测到的数据写入文件
opcontrol --reset : 清空之前检测的数据记录
opcontrol -h : 关闭oprofile进程
查看检测结果

opreport : 以镜像(image)的角度显示检测结果,进程、动态库、内核模块属于镜像范畴
opreport -l : 以函数的角度显示检测结果
opreport -l test : 以函数的角度,针对test进程显示检测结果
opannotate -s test : 以代码的角度,针对test进程显示检测结果
opannotate -s /lib64/libc-2.4.so : 以代码的角度,针对libc-2.4.so库显示检测结果


opreport输出解析

正如以上命令解析所言,不加参数的opreport命令从镜像的角度显示cpu的使用情况:


linux # opreport
CPU: Core 2, speed 2128.07 MHz (estimated)
Counted CPU_CLK_UNHALTED events (Clock cycles when not halted) with a unit mask of 0x00 (Unhalted core cycles) count 100000
CPU_CLK_UNHALT.........|
  samples |           %|
------------------------
   31645719     87.6453      no-vmlinux 
    4361113     10.3592      libend.so
       7683      0.1367      libpython2.4.so.1.0
       7046      0.1253      op_test
        ⋯⋯

以上列表按以下形式输出:

              samples |                            %|
-----------------------------------------------------
     镜像内发生的采样次数     采样次数所占总采样次数的百分比      镜像名称


因我们在初始化时执行了"opcontrol --no-vmlinux"命令,指示oprofile不对模块和内核进行检测,因而在探测结果中,模块和内核一同显示成no-vmlinux镜像。输出中,libend.so和libpython2.4.so.1.0均为动态库,op_test为进程。以上采样数据表明,检测时间内,cpu主要执行内核和模块代码,用于执行libend.so库函数的比重亦较大,达到10%左右。



进一步地,我们可以查看到进程、动态库中的每个函数在检测时间内占用cpu的情况:


linux # opreport -l
samples           %        image name        app name         symbol name
31645719     87.4472        no-vmlinux      no-vmlinux         /no-vmlinux
4361113     10.3605         libend.so       libend.so             endless
    7046      0.1253           op_test         op_test                main
    ⋯⋯

以上输出显示消耗cpu的函数为libend.so库中的endless函数,以及op_test程序中的main函数。



进行oprofile初始化时,若我们执行opcontrol --vmlinux=vmlinux-`uname -r`,指定oprofile对内核和内核模块进行探测,在执行opreport查看检测结果时,内核和内核模块就不再显示为no-vmlinux,而是内核和各个内核模块作为单独的镜像,显示相应cpu占用情况。



使用opannotate从代码层看cpu占用情况

以上介绍了使用oprofile的opreport命令,分别从进程和函数层面查看cpu使用情况的方法。看到这里,有的同学可能有这样的疑问:使用opreport,我找到了消耗cpu的进程A,找到了进程A中最消耗cpu的函数B,进一步地,是否有办法找到函数B中最消耗cpu的那一行代码呢?



oprofile中的opannotate命令可以帮助我们完成这个任务,结合具备调试信息的程序、带有debuginfo的动态库,opannotate命令可显示代码层面占用cpu的统计信息。下面我们通过几个简单的程序实例,说明opannotate命令的使用方法。



首先,我们需要一个消耗cpu的程序,该程序代码如下:

//op_test.c
extern void endless();
int main()
{
  int i = 0, j = 0;
  for (; i < 10000000; i++ )
     {
           j++;
     }
  endless();
  return 0;
}

该程序引用了外部函数endless,endless函数定义如下:

//end.c
void endless()
{
  int i = 0;
  while(1)
     {
         i++;
     }
}
复制代码
endless函数同样很简单,下面我们将定义了endless函数的end.c进行带调试信息地编译,并生成libend.so动态库文件:

linux # gcc -c -g -fPIC end.c
linux # gcc -shared -fPIC -o libend.so end.o
linux # cp libend.so /usr/lib64/libend.so
接着,带调试信息地编译op_test.c,生成op_test执行文件:

linux # gcc -g -lend -o op_test op_test.c


之后,我们开启oprofile进行检测,并拉起op_test进程:

linux # opcontrol --reset
linux # opcontrol --start
linux # ./op_test &
在程序运行一段时间后,导出检测数据,使用opannotate进行结果查看:
linux # opcontrol --dump
linux # opannotate -s op_test
/*
* Total samples for file : "/tmp/lx/op_test.c"
*
* 7046  100.00
*/
               : int main()
               :{ /*main total : 7046  100.000 */
              :    int i = 0, j = 0;
  91.4987 :    for (; i < 10000000; i++ )
               :    {
   8.5013 :          j++;
               :    }
             :    endless();
             :    return 0;
               :}
以上输出表明,在op_test程序的main函数中,主要消耗cpu的是for循环所在行代码,因该段代码不仅包含变量i的自增运算,还将i与10000000进行比较。



下面显示对自编动态库libend.so的检测结果:
linux # opannotate -s /usr/lib64/libend.so
/*
* Total samples for file : "/tmp/lx/end.c"
*
* 4361113  100.00
*/
                 : void endless()
                 : {
                :     int i = 0;
                :     while(1)
                 :     {
  25661   0.6652 :          i++;
4335452  99.3348 :     }
                 : }

猜你喜欢

转载自onmyway-1985.iteye.com/blog/2176656