http://www.cnblogs.com/bangerlee/archive/2012/08/30/2659435.htmlhtml
引言python
cpu无故占用高?应用程序响应慢?苦于没有分析的工具?linux
oprofile利用cpu硬件层面提供的性能计数器(performance counter),经过计数采样,帮助咱们从进程、函数、代码层面找出占用cpu的"罪魁祸首"。下面咱们经过实例,了解oprofile的具体使用方法。缓存
经常使用命令app
使用oprofile进行cpu使用状况检测,须要通过初始化、启动检测、导出检测数据、查看检测结果等步骤,如下为经常使用的oprofile命令。less
初始化函数
检测控制工具
查看检测结果性能
opreport输出解析spa
正如以上命令解析所言,不加参数的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。