from: http://blog.chinaunix.net/uid-13746440-id-3152484.html

0.引言
基于DPDK的发包工具的性能今天已经达到双向1900Wpps了,比昨天又高了200Wpps,正是得益于
oProfile检测与调优的结果,而且今天还只是很简单的用了一下(类似于下面的示例),跟踪出对几个结构体字段的访问比较缓慢,于是对结构体字段进行了仔细的顺序调整与Cache对齐(之前急于功能实现,没顾及这些字段的排布),结果性能马上飙升了200Wpps,开心死我了。后天(明天外出办理其它事情)到公司再利用oProfile细细跟踪一下,特别是cache命中、 pipeline阻塞、prefetch预取等,看最终我的发包工具到底能达到什么性能。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/,中断模式的设置为:

1
# modprobe oprofile timer=1

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:

1
2
3
4
[root@localhost oprofile-0.9.6]# opcontrol --init
FATAL: Module oprofile not found.
FATAL: Module oprofile not found.
Kernel doesn't support oprofile

需要编辑内核配置文件:

1
2
[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)
同时也应确保另外几个配置选项被选中:

1
2
3
4
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环境下进行,具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[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时会给出错误提示,比如当我输入如下命令敲回车后提示:

1
2
3
4
5
6
7
[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:

1
2
3
4
[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错误:

1
2
3
4
5
6
7
8
9
10
11
[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文件:

1
2
3
4
5
6
[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:

1
2
3
4
5
6
[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分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
[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的基本步骤如下所示,其中中间的步骤是经常使用的,前后几个步骤无需重复多次:

1
2
opcontrol --init   #加载模块
opcontrol --vmlinux=/usr/src/linux-2.6.37.2/vmlinux  #是否对kernel进行profiling
1
2
3
4
5
opcontrol --reset  #清楚当前会话中的数据
opcontrol --start  #开始profiling
./multiply   #运行应用程序,oprofile会对它进行profiling
opcontrol --dump  #把收集到的数据写入文件
opcontrol --stop  #停止profiling
1
2
opcontrol --shutdown  #关闭守护进程oprofiled
opcontrol --deinit  #卸载模块

2) 对于oProfile收集的统计信息,可以使用opreport、opgprof、opannotate这些工具进行分析并获取相关信息,比如从上面的示例中可以看到函数slow_multiply就相对占用了较多的计算时间。

转载请保留地址:http://lenky.info/2012/03/26/oprofile%e7%9a%84%e5%ae%89%e8%a3%85%e4%b8%8e%e4%bd%bf%e7%94%a8/