这段时间,领导一直要我做一个linux下性能分析工具的培训。说实话,我也没用过性能分析工具来分析程序的性能瓶颈问题。现实是,在LTE项目中,遇到了程序性能瓶颈问题。所以培训的目的:一是为部门的业绩考核,二是看是否可以应用这些工具来解决LTE项目中程序性能瓶颈问题。
linux下有两个主要的性能分析工具:oprofile和gprof 。gprof 是GNU工具之一,但了解后,很多文章说它不支持多线程。相比较而言,oprofile的功能更加强大。
在众多介绍oprofile工具的文章中,有一篇文章比较吸引我,故转载在此,并进行了排版整理。
1. 概述
oProfile是用于Linux的若干种评测和性能监控工具中的一种,它可以工作在不同的体系结构上,包括MIPS、ARM、IA32、IA64和AMD。oProfile包含在Linux2.5和更高版本的内核中,也包含在大多数较新的Linux版本中,包括RedHat9。
oProfile是Linux平台上的一个功能强大的性能分析工具,支持两种采样(sampling)方式:基于事件的采样(eventbased)和基于时间的采样(timebased)。
基于事件的采样是oProfile只记录特定事件(比如L2 cache miss)的发生次数,当达到用户设定的定值时oProfile就记录一下(采一个样)。这种方式需要CPU内部有性能计数器(performace counter)。
基于时间的采样是oProfile借助OS时钟中断的机制,每个时钟中断oProfile都会记录一次(采一次样),引入此种采样方式的目的在于提供对没有性能计数器的CPU的支持,其精度相对于基于事件的采样要低。因为要借助OS时钟中断的支持,对禁用中断的代码oProfile不能对其进行分析。
oProfile在Linux上分两部分,一个是内核模块(oprofile.ko),一个为用户空间的守护进程(oprofiled)。前者负责访问性能计数器或者注册基于时间采样的函数(使用register_timer_hook注册之,使时钟中断处理程序最后执行profile_tick时可以访问之),并采样置于内核的缓冲区内。后者在后台运行,负责从内核空间收集数据,写入文件。
2. 注意事项
1) 不建议在虚拟机里利用oProfile来测试性能,因为虚拟机对oProfile的支持并不好,比如在Vmware虚拟机里不支持性能计数器接口模式:http://oprofile.sourceforge.net/faq/,中断模式的设置为:
# modprobe oprofile timer=1
或
# echo "options oprofile timer=1" >> /etc/modprobe.conf
具体参看:http://oprofile.sourceforge.net/doc/detailed-parameters.html#timer
2) 调式的内核最好是原生内核(Vanilla kernel、香草内核),发行版Linux(比如redhat)自带的内核一般都是经过大量修改的,对oProfile的支持不好。所以,我们最好能从kernel官方网站下载原生源码后自行编译生成新内核,重启机器进行新内核环境后进行性能测试。另外,oProfile需要的是未经压缩的内核镜像,所以/boot目录的vmlinuz-2.x.xx是不能使用的,而需要使用Linux源码编译目录里的未镜像文件,比如/usr/src/linux-2.6.30/vmlinux
3) 内核需打开OPROFILE选项,否则无法运行oProfile:
[root@localhost oprofile-0.9.6]# opcontrol --init FATAL: Module oprofile not found. FATAL: Module oprofile not found. Kernel doesn't support oprofile
需要编辑内核配置文件:
[root@localhost ~]# cd /usr/src/linux-2.6.37.2 [root@localhost linux-2.6.37.2]# vi .config
将其中的# CONFIG_OPROFILE is not set改为CONFIG_OPROFILE=m(或者y)
同时也应确保另外几个配置选项被选中:
CONFIG_PROFILING=y CONFIG_X86_LOCAL_APIC=y CONFIG_X86_IO_APIC=y CONFIG_PCI_IOAPIC=y
然后编译内核重启机器即可。
4) 为了支持新的CPU类型,oProfile的更新会比较频繁,所以在使用oProfile时建议先去http://oprofile.sourceforge.net/news/看看是否有更新版本。
3.系统环境
此文中所有关于oProfile的介绍、测试均在CENTOS 5.4环境下进行,具体如下:
[root@localhost oprofile-0.9.7]# cat /etc/issue CentOS release 5.4 (Final) Kernel \r on an \m [root@localhost oprofile-0.9.7]# uname -a Linux localhost.localdomain 2.6.37.2 #1 SMP Thu Mar 15 18:32:12 CST 2012 x86_64 x86_64 x86_64 GNU/Linux [root@localhost oprofile-0.9.7]# gcc --version gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-46) Copyright (C) 2006 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. [root@localhost oprofile-0.9.7]# make --version GNU Make 3.81 Copyright (C) 2006 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This program built for x86_64-redhat-linux-gnu
4.oProfile的安装
oProfile的安装同普通Linux软件安装没有什么两样,照例是configure、make、make install三板斧。由于oProfile依赖的库比较多,如果系统中没有安装某些库则在configure时会给出错误提示,比如当我输入如下命令敲回车后提示:
[root@lenky oprofile-0.9.6]# ./configure --with-kernel-support … checking libiberty.h usability... no checking libiberty.h presence... no checking for libiberty.h... no checking for cplus_demangle in -liberty... no configure: error: liberty library not found
解决该问题的方法是首先从网站http://ftp.gnu.org/gnu/binutils/?C=M;O=D下载binutils包编译安装即可(同样是./configure 、make、make install)。
再configureoProfile:
[root@localhost oprofile-0.9.7]# ./configure --with-kernel-support … config.status: executing libtool commands Warning: QT version 3 was requested but not found. No GUI will be built.
提示没有图形界面,不用管它,直接make编译,我还遇到了这个make错误:
[root@localhost oprofile-0.9.7]# make … gcc -shared .libs/libopagent_la-opagent.o -lbfd -liberty -ldl -lz -Wl,--version-script=../libopagent/opagent_symbols.ver -Wl,-soname -Wl,libopagent.so.1 -o .libs/libopagent.so.1.0.0 /usr/local/bin/ld: /usr/local/lib/libbfd.a(archures.o): relocation R_X86_64_32 against `bfd_i386_arch' can not be used when making a shared object; recompile with -fPIC /usr/local/lib/libbfd.a: could not read symbols: Bad value collect2: ld returned 1 exit status make[2]: *** [libopagent.la] Error 1 make[2]: Leaving directory `/home/lenky/oprofile-0.9.6/libopagent' make[1]: *** [all-recursive] Error 1 make[1]: Leaving directory `/home/lenky/oprofile-0.9.6' make: *** [all] Error 2
该问题在于没有找到bfd的动态链接库,需要进入binutils 的bfd目录编译获取libbfd.so文件:
[root@localhost bfd]# pwd /home/lenky/binutils-2.20.1/bfd [root@localhost bfd]# ./configure --enable-shared [root@localhost bfd]# make clean [root@localhost bfd]# make [root@localhost bfd]# make install
一般,接下来还会遇到libiberty同样的问题,但是libiberty的configure没有提供–enable-shared选项,所以需要我们自己制作so文件,编辑Makefile文件,加上-fPIC编译选项,然后利用make、gcc生成so:
[root@localhost libiberty]# vi Makefile CFLAGS = -g -O2 -fPIC [root@localhost libiberty]# make clean [root@localhost libiberty]# make [root@localhost libiberty]# gcc -shared *.o -o libiberty.so [root@localhost bfd]# make install
由于之前编译过,所以注意不要落了make clean对先前编译结果进行清除,否则生成的libiberty.so不完整,要把so库拷贝到正确的系统路径,否则在执行oProfile程序时,可能出现“error while loading shared libraries”的错误信息。
最后再对oProfile进行make、make install即可,以上就是我在Linux 2.6.37.2内核上编译oProfile过程中遇到的问题,虽然摸索清楚后看似不复杂,其实总个过程也浪费了我不少时间。
5.oProfile工具集
安装好的oProfile包含有一系列的工具集,这些工具默认在路径/usr/bin之下,它们分别是:
1) op_help:列出可用的事件,并带有简短的描述。
2) opcontrol:控制oProfile的数据收集。
3) opreport:对结果进行统计输出。
4) opannaotate:产生带注释的源/汇编文件,源语言级的注释需要编译源文件时已加上调试符号信息的支持。
5) opgprof:产生如gprof相似的结果。
6) oparchive:将所有的原始数据文件收集打包,从而可以在另一台机器上进行分析。
7) opimport:将采样的数据库文件从另一种abi外部格式转化为本地格式。
6.oProfile使用小示例
下面是一个完整的小示例,其中multiply是测试程序,在进行gcc编译时加上了-g参数,便于opannotate分析:
[root@localhost test]# cat multiply.c /** * FileName: multiply.c * sh# gcc multiply.c -g -o multiply */ #include <stdio.h> int fast_multiply(x, y) { return x * y; } int slow_multiply(x, y) { int i, j, z; for (i = 0, z = 0; i < x; i++) z = z + y; return z; } int main(int argc, char *argv[]) { int i,j; int x,y; for (i = 0; i < 200; i ++) { for (j = 0; j < 30 ; j++) { x = fast_multiply(i, j); y = slow_multiply(i, j); } } printf("x=%d, y=%d\n", x, y); return 0; } [root@localhost test]# gcc multiply.c -g -o multiply [root@localhost test]# opcontrol --init [root@localhost test]# opcontrol --vmlinux=/usr/src/linux-2.6.37.2/vmlinux [root@localhost test]# opcontrol --reset [root@localhost test]# opcontrol --start Using default event: CPU_CLK_UNHALTED:100000:0:1:1 Using 2.6+ OProfile kernel interface. Reading module info. Using log file /var/lib/oprofile/samples/oprofiled.log Daemon started. Profiler running. [root@localhost test]# ./multiply x=5771, y=5771 [root@localhost test]# opcontrol --dump [root@localhost test]# opcontrol --stop Stopping profiling. [root@localhost test]# opcontrol --shutdown Killing daemon. [root@localhost test]# opcontrol --deinit Unloading oprofile module [root@localhost test]# opannotate --source ./multiply /* * Command line: opannotate --source ./multiply * * Interpretation of command line: * Output annotated source file with samples * Output all files * * CPU: Intel Architectural Perfmon, speed 1867 MHz (estimated) * Counted CPU_CLK_UNHALTED events (Clock cycles when not halted) with a unit mask of 0x00 (No unit mask) count 100000 */ /* * Total samples for file : "/home/lenky/test/multiply.c" * * 39 100.000 */ :#include <stdio.h> : :int fast_multiply(x, y) :{ : return x * y; :} :int slow_multiply(x, y) :{ /* slow_multiply total: 36 92.3077 */ : int i, j, z; 27 69.2308 : for (i = 0, z = 0; i < x; i++) 8 20.5128 : z = z + y; 1 2.5641 : return z; :} :int main() :{ /* main total: 3 7.6923 */ : int i,j; : int x,y; : for (i = 0; i < 200; i ++) { 2 5.1282 : for (j = 0; j < 30 ; j++) { 1 2.5641 : x = fast_multiply(i, j); : y = slow_multiply(i, j); : } : } : printf("x=%d, y=%d\n", x, y); : return 0; :} : [root@localhost test]# opreport -l ./multiply CPU: Intel Architectural Perfmon, speed 1867 MHz (estimated) Counted CPU_CLK_UNHALTED events (Clock cycles when not halted) with a unit mask of 0x00 (No unit mask) count 100000 samples % symbol name 36 92.3077 slow_multiply 3 7.6923 main [root@localhost test]# opreport CPU: Intel Architectural Perfmon, speed 1867 MHz (estimated) Counted CPU_CLK_UNHALTED events (Clock cycles when not halted) with a unit mask of 0x00 (No unit mask) count 100000 CPU_CLK_UNHALT...| samples| %| ------------------ 438541 99.4447 vmlinux 921 0.2088 oprofiled 630 0.1429 libc-2.5.so 355 0.0805 bash 342 0.0776 oprofile 55 0.0125 ld-2.5.so 39 0.0088 multiply 21 0.0048 igb 16 0.0036 ixgbe 12 0.0027 libpthread-2.5.so 9 0.0020 irqbalance 7 0.0016 gawk 7 0.0016 libglib-2.0.so.0.1200.3 7 0.0016 libpython2.4.so.1.0 6 0.0014 sshd 4 9.1e-04 libcrypto.so.0.9.8e 3 6.8e-04 grep 3 6.8e-04 libusb-0.1.so.4.4.4 2 4.5e-04 ls 2 4.5e-04 sendmail.sendmail 1 2.3e-04 cat 1 2.3e-04 libdbus-1.so.3.4.0 1 2.3e-04 libgthread-2.0.so.0.1200.3 1 2.3e-04 libselinux.so.1 1 2.3e-04 init 1 2.3e-04 libpopt.so.0.0.0 1 2.3e-04 _gobject.so 1 2.3e-04 nmbd [root@localhost test]#
7.oProfile反复使用以及示例结论
1) 使用oProfile的基本步骤如下所示,其中中间的步骤是经常使用的,前后几个步骤无需重复多次:
opcontrol --init #加载模块 opcontrol --vmlinux=/usr/src/linux-2.6.37.2/vmlinux #是否对kernel进行profiling
opcontrol --reset #清楚当前会话中的数据 opcontrol --start #开始profiling ./multiply #运行应用程序,oprofile会对它进行profiling opcontrol --dump #把收集到的数据写入文件 opcontrol --stop #停止profiling
opcontrol --shutdown #关闭守护进程oprofiled opcontrol --deinit #卸载模块
2) 对于oProfile收集的统计信息,可以使用opreport、opgprof、opannotate这些工具进行分析并获取相关信息,比如从上面的示例中可以看到函数slow_multiply就相对占用了较多的计算时间。
注:本文转载自:http://lenky.info/archives/2012/03/1371