zoukankan      html  css  js  c++  java
  • 谁动了我的cpu——oprofile使用札记

    引言

    cpu无端占用高?应用程序响应慢?苦于没有分析的工具?

    oprofile利用cpu硬件层面提供的性能计数器(performance counter),通过计数采样,帮助我们从进程、函数、代码层面找出占用cpu的"罪魁祸首"。下面我们通过实例,了解oprofile的具体使用方法。

    常用命令

    使用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;
    6447   91.4987 :    for (; i < 10000000; i++ )
                   :    {
     599    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 :     }
                     : }
    复制代码

    查看c库代码占用cpu情况

    以上使用opannotate,分别查看了应用程序代码、自编动态库代码的cpu占用情况,对于c库中的代码,我们是否也能查看其消耗cpu的情况呢?

    在使用oprofile查看c库代码信息前,需要安装glibc的debuginfo包,安装debuginfo包之后,我们即可以通过opannotate查看到c库代码,以下展示了malloc底层实现函数_int_malloc的部分代码:

    复制代码
    linux # opannotate -s /lib64/libc-2.4.so
    /*
     ----------------malloc---------------------
     */
                    :Void_t *
                    :_int_malloc( mstate av, size_t bytes )
                    :{  /* _int_malloc total: 118396  94.9249 */
                         ⋯⋯
                    :       assert((fwd->size & NON_MAIN_ARENA) == 0);
    115460  92.5709 :       while((unsigned long)(size) < (unsigned long)(fwd->size)) {
      1161   0.9308 :            fwd = fwd->fd;
                    :            assert((fwd->size & NON_MAIN_ARENA) == 0);
                    :        }
                    :}
    复制代码

    在进行程序性能调优时,根据oprofile检测到的c库代码占用cpu的统计信息,可以判别程序性能瓶颈是否由c库代码引起。若oprofile检测结果显示cpu被过多地用于执行c库中的代码,我们可进一步地采用修改c库代码、升级glibc版本等方法解决c库引发的应用程序性能问题。

    小结

    本文介绍了使用oprofile工具从进程、函数和代码层面检测cpu使用情况的方法,对于代码层面,分别介绍了查看程序代码、自编动态库代码以及gblic代码cpu统计情况的方法,中间过程使用到opcontrol、opreport、opannotate三个常用的oprofile命令。

    当系统出现cpu使用率异常偏高情况时,oprofile不但可以帮助我们分析出是哪一个进程异常使用cpu,还可以揪出进程中占用cpu的函数、代码。在分析应用程序性能瓶颈、进行性能调优时,我们可以通过oprofile,得出程序代码的cpu使用情况,找到最消耗cpu的那部分代码进行分析与调优,做到有的放矢。另外,进行程序性能调优时,我们不应仅仅关注自己编写的上层代码,也应考虑底层库函数,甚至内核对应用程序性能的影响。

    关于oprofile工具可用于分析的场景,本文仅介绍了cpu使用情况一种,我们还可以通过oprofile查看高速缓存的利用率、错误的转移预测等信息,"opcontrol --list-events"命令显示了oprofile可检测到的所有事件,更多的oprofile使用方法,请参看oprofile manual

    Reference: oprofile manual

  • 相关阅读:
    Selenium简单测试页面加载速度的性能(Page loading performance)
    Selenium Page object Pattern usage
    Selenium如何支持测试Windows application
    UI Automation的两个成熟的框架(QTP 和Selenium)
    分享自己针对Automation做的两个成熟的框架(QTP 和Selenium)
    敏捷开发中的测试金字塔(转)
    Selenium 的基础框架类
    selenium2 run in Jenkins GUI testing not visible or browser not open but run in background浏览器后台运行不可见
    eclipse与SVN 结合(删除SVN中已经上传的问题)
    配置Jenkins的slave节点的详细步骤适合windows等其他平台
  • 原文地址:https://www.cnblogs.com/xumaojun/p/8532021.html
Copyright © 2011-2022 走看看