天天看点

source性能分析工具Oprofile详细解析

Oprofile Introduction

内容概要

* oprofile 介绍

* .oprofile 安装及 Linux 内核编译

* oprofile 使用

* oprofile 实例演示及性能分析

* gprof 介绍

* Kprof 分析

* gcov 简介

一、 oprofile 介绍

      oprofile 是 Linux 平台上,类似 INTEL VTune 的一个功能强大的性能分析工具。 其支持两种采样 (sampling) 方式:基于事件的采样 (event based) 和基于时间的采样 (time based) 。

      基于事件的采样是 oprofile 只记录特定事件(比如 L2 cache miss )的发生次数,当达到用户设定的定值时 oprofile 就记录一下(采一个样)。这种方式需要 CPU 内部有性能计数器 (performace counter) 。现代 CPU 内部一般都有性能计数器;

      基于时间的采样是 oprofile 借助 OS 时钟中断的机制,每个时钟中断 oprofile 都会记录一次 ( 采一次样)。引入的目的在于,提供对没有性能计数器 CPU 的支持。其精度相对于基于事件的采样要低。因为要借助 Os 时钟中断的支持,对禁用中断的代码 oprofile 不能对其进行分析。

      oprofile 在 Linux 上分两部分,一个是内核模块 (oprofile.ko) ,一个为用户空间的守护进程 (oprofiled) 。前者负责访问性能计数器或者注册基于时间采样的函数 ( 使用 register_timer_hook 注册之,使时钟中断处理程序最后执行 profile_tick 时可以访问之 ) ,并采样置于内核的缓冲区内。后者在后台运行,负责从内核空间收集数据,写入文件。

二、 oprofile 安装及 Linux 内核编译

I. Oprofile 安装

Oprofile 包含在 Linux 2.6 版本的内核中,是用于 Linux 的若干种评测和性能监控工具中的一种。 也可从官方网站下载源码进行编译安装;具体安装步骤:

1. ./configure --with-kernel-support

注:在编译过程中,这一步出现的问题是:

( 1 )缺少 libiberty.h 头文件 ; ( 2 )缺少 popt ;

libiberty.h 头文件在 binutils-devel package 中,需要下载这个包进行安装;也可通过新立德 进行安装。可以用 sudo apt-get install binutil-dev 进行包安装。 Popt 存在 libpopt-devel package 中,同样需要进行下载安装; sudo apt-get install libpopt-dev;

 2. make

 3. make install

oprofile 安装完成后会生成以下工具集:

 /usr/bin/oprofiled 守护进程

/usr/bin/opcontrol 控制前端,负责控制与用户交互,用得最多

/usr/bin/opannotate 根据搜集到的数据,在源码或者汇编层面上注释并呈现给用户

/usr/bin/opreport 生成二进制镜像或符号的概览

/usr/bin/ophelp 列出 oprofile 支持的事件

/usr/bin/opgprof 生成 gprof 格式的剖析数据

opstack : 产生调用图 profile ,但要求 x86/2.6 的平台,并且 linux2.6 安装了 call-graph patch

oparchive : 将所有的原始数据文件收集打包,可以到另一台机器上进行分析。

op_import : 将采样的数据库文件从另一种 abi 转化成本地格式。

运行 oprofile 需要 root 权限,因为它要加载 profile 模块,启动 oprofiled 后台程序等。所以在运行之前, 就需要切换到 root 。

II. Linux 内核编译

由于 oprofile 是可对内核进行评测和性能监控的工具,所以需要有内核支持。但是在 ubuntu 下面并没有内核源码,所以需要下载本机对应版本的内核源码,进行编译安装;

编译新内核的步骤如下:

( 1 )将下载的内核放在 /usr/src 目录下;进行解压: tar -jxvf linux-source-2.6.27.tar.bz2;

(2 )接下来对内核进行配置: make menuconfig(or make xconfig), 我选择使用此命令;

在进行配置过程中 ,General Setup 中的 Local version - append to kernel release 是可以自己 定制自己喜欢的内核名字 ; Load an Alternate Configuration File 此选项可以引用系统中的配置文件; Save an Alternate Configuration File 是将重新配置的 .config 文件进行保存。 在配置之前,可以用以下命令进行清除。 Make clean :可以把以前编译的文件进行清除;

Make mrproper : 可以清除以前的配置文件;建议新手不用;最好对以前的 .config 文件进行配置。

.config 文件是内核源码中自带的 ./usr/src/linux-source-2.6.27/arch/x86/configs 下

面的 config 文件再根据 menuconfig 中的选项进行裁减后获得的最终的 config 文件。

(3) make 进行内核编译,编译完成后会在 /boot 目录下生成几个重要的文件; vmlinuz-2.6.27 、 System.map 等;其中 config 文件生成在 /linux-source-2.6.27 目录下,需要拷贝到 /boot 目录下面。

( 4 ) make modules_install 安装配置中选定的模块。

( 5 ) make install

( 6 )检查是否生成 initrd 镜像文件,在 ubuntu 下并没有生成,需要在 /boot 下面使用命令:

mkinitramfs -o initrd.img.2.6.27.18 生成镜像文件。

( 7 )在 /boot/grub 下面编辑 menu.lst 文件,按照格式将新内核信息进行添加;只有在此处对新内核 添加,系统启动后才会被引导进入新内核。如果配置符合系统要求,按照这个顺序就可以编译一个新内核了。

三、 oprofile 使用

oprofile 要在 root 权限下使用:

a. 初始化

opcontrol --init

该命令会加载 oprofile.ko 模块, mount oprofilefs 成功后会在 /dev/oprofile/ 目录下导出些文件和目录如: cpu_type, dump, enable, pointer_size, stats.

b. 配置

1. 首先,配置 OProfile 是否应该监视内核。这是在启动 OProfile 前需要的配置选项。

以及对计数事件和样本计数的设置,计数的 CPU 模式(用户态、核心态)是系统默认项;

如果 oprofile 监视评测内核自身:

opcontrol –vmlinux=/src/urc/linux-source-2.6.27/vmlinux

如果 oprofile 不用监视评测内核:

opcontrol --no-vmlinux .

在此体系结构 Family10h 中默认的配置为:

opcontrol –event=CPU_CLK_UNHALTED:100000

如果配置守护进程写入文件的方式,用以下命令:

opcontrol --separate=<choice>

<choice> 可以是以下之一:

none — 不要分离档案(默认)

library — 为库生成每个应用程序的档案

kernel — 为内核和内核模块生成每个应用程序的档案

all — 为库生成每个应用程序的档案,为内核和内核模块生成每个应用程序的档案

注意问题:

( 1 ) oprofile 可以在此进行事件设置;

通过 opcontrol --list-events 命令可以查看此结构中支持的事件;

通过 opcontrol --event=L2_CACHE_MISS:500 --event=L1_DTLB_MISS_AND_L2_DTLB_HIT:500

--event=......... 命令来进行事件的设置;

此命令 --event 参数必须依次给出,无论有多少个,不可分行,切记!

通过 opcontrol –status 命令可以查看自己已经配置的事件;

( 2 )对于每一次测试过程中,如果需要进行事件的重新设置,就必须重启 daemon, 它是

一个守护进程。也就是说,收集数据完成后,要用 --shutdown 命令来停止 daemon, 而不是 -- stop( 此命令只是停止 profiling) ,这样再次进行— start 命令就可以使用新的事件设置;

( 3 )一次评测结束后,旧的 profiling data 还是存在的,用— reset 或者— save 命令来清理或者保存数据;

c. 启动

opcontrol --start

d. 运行待分析之程序

gcc -o wls wls.c ;生成 wls.o 文件; ./wls

e. 取出数据

opcontrol –dump

数据被写进 /var/lib/oprofile/samples /oprofiled.log

f. 停止评测

opcontrol --stop

g. 使用 opreport 分析结果

opreport -l ./wls

根据设置的事件进行评测,将评测的结果一一显示出来。

h. 使用 opannotate 分析结果

如果对源码进行分析,可以通过 opannotate 工具实现

编译: gcc -g wls.c -o wls

分析: opannotate --source ./wls

总结一下:

  1. opcontrol –init 加载模块, mout /dev/oprofile 创建必需的文件和目录
  2. opcontrol --no-vmlinux 或者 opcontrol --vmlinux=/boot/vmlinux-`uname -r` 决定是否对 kernel 进行 profiling
  3. opcontrol --reset 清楚当前会话中的数据
  4. opcontrol --start 开始 profiling
  5. ./wls 运行应用程序, oprofile 会对它进行 profiling
  6. opcontrol --dump 把收集到的数据写入文件
  7. opcontrol --stop 停止 profiling
  8. opcotrol -h 关闭守护进程 oprofiled
  9. opcontrol --shutdown 停止 oprofiled
  10. opcontrol --deinit 卸载模块

常用的是 3→7 这几个过程,得到性能数据之后,可以使用 opreport, opstack, opgprof, opannotate几个工具进行分析,我常用的是 opreport, opannotate 进行分析。

四、实例演示

下面以 AMR 的解码源码为例,来分析代码的性能。

首先,要对即将分析的代码进行编译:

(1) 编译源代码的库文件:

在目录 /home/wls/ 应用软件 /AMREngineC_OKI_source/AMRDecEngine 下执行 make 命令; 生成文件: libAMRDecEngine.a 此文件为归档库文件。

( 2 )编译源代码的主文件:

在目录 /home/wls/ 应用软件 /AMREngineC_OKI_source/TestAMRDec/Win32 下执行 make 命令; 生成文件: TestAMR 此文件为可执行文件。

将 TestAMR 复制到上层目录: cp TestAMR ..

执行命令: ./TestAMR AMRDec.par 生成 TestAMRDec.wav

其次,执行 oprofile 的命令并进行相应设置:

[email protected]:~/TestAMRDec# opcontrol --init

[email protected]:~/TestAMRDec# opcontrol --no-vmlinux

[email protected]:~/TestAMRDec# opcontrol --event= CPU_CLK_UNHALTED:5000

--event=DATA_CACHE_MISSES:1000 --event=INSTRUCTION_CACHE_MISSES:1000

--event=MEMORY_REQUESTS:1000

[email protected]:~/TestAMRDec# opcontrol --status

Daemon running: pid 14979

Event 0: CPU_CLK_UNHALTED:5000:0:1:1

Event 1: DATA_CACHE_MISSES:1000:0:1:1

Event 2: INSTRUCTION_CACHE_MISSES:1000:0:1:1

Event 3: MEMORY_REQUESTS:1000:131:1:1

Separate options: none

vmlinux file: none

Image filter: none

Call-graph depth: 0

[email protected]:~/TestAMRDec# opcontrol –start

Using 2.6+ OProfile kernel interface.

Reading module info.

Using log file /var/lib/oprofile/samples/oprofiled.log

Daemon started.

Profiler running.

[email protected]:~/TestAMRDec# ./TestAMR AMRDec.par

===================================================

Audio Length [msec] : 10020

Audio Frames [frame] : 501

Audio Packets [packet] : 501

Decoded Bytes [byte] : 7014

Average Bitrate [kbps] : 5.600

Decoding Time [msec] : 40 (250.500 Times)

===================================================

[email protected]:~/TestAMRDec# opcontrol --dump

[email protected]:~/TestAMRDec# opcontrol --stop

Stopping profiling.

[email protected]:~/TestAMRDec# opreport -l ./TestAMR

数据分析结果如下:

source性能分析工具Oprofile详细解析

如果设置如下:

[email protected]:~/TestAMRDec# opcontrol –vmlinux=/src/urc/

linux-source-2.6.27/vmlinux

source性能分析工具Oprofile详细解析

[email protected]:~/TestAMRDec# opcontrol --shutdown

Stopping profiling.

Killing daemon.

 [email protected]:~/TestAMRDec# opcontrol --deinit

Daemon not running

Unloading oprofile module

现将 oprofile 用到的各种性能参数在此予以解释:

 通过 op_help 或者 opcontrol –list-events 显示出 oprofle 所支持的事件列表;

Oprofile 是通过性能计数器对设置的事件进行间隔性采样记录,获取每个函数相应事件所占百分比; 事件采样率越高,其消耗时间就越多。时间和采样率是成正比例的。

以下将常见事件汇总如下:

cpu family :代表处理器属于哪一个系列, 6 指的是 6 系列,主要包括: Pentium Pro 、 Pentium II 、 Pentium II Xeon 、 Pentium III 和 Pentium III Xeon 处理器

model :型号,可用来确定处理器的制作技术以及属于该系列的第几代设计(或核心,通常和 cpu family 配合使用

stepping :步进编号用来标识处理器的设计或制作版本,有助于控制和跟踪处理器的更改,步进还可以让最终用户更具体地识别其系统安装的处理器版本,确定微处理器的内部设计或制作特性。

siblings :逻辑处理器的数目,这里是 2 ,即一个处理器核上有分成两个逻辑核

core id: 是我们通常说的 CPU ID

cpu cores :这个处理器上有几个核

apic id: 高级可编程中断控制器的编号

cpuid level: 这个不是很清楚

下面对常用的事件进行解释:

 1 CPU_CLK_UNHALTED: (counter: all)

Clock cycles when not halted (min count: 6000)

Unit masks (default 0x0)

----------

0x00: Unhalted core cycles

0x01: Unhalted bus cycles

0x02: Unhalted bus cycles of this core while the other core is halted

在采样的这段时间内,不在 halted 状态(挂起或停机状态)下的机器周期数,具体有分为一下三种情况:

( 1 )不在 unhalted 状态的 cpu 的周期数

( 2 )不在 unhalted 状态的总线的周期数

( 3 )当其他的核在 halted 状态时,本地核上的不在 halted 状态的周期数

2 INST_RETIRED.ANY_P: (counter: all)

number of instructions retired (min count: 6000)

引退指令的数目,很多资料上说,当一条指令执行完后,把它从指令队列中删除,才叫指令的引退

 3 L2_RQSTS: (counter: all)

number of L2 cache requests (min count: 500)

Unit masks (default 0x7f)

----------

0xc0: core: all cores

0x40: core: this core

0x30: prefetch: all inclusive

0x10: prefetch: Hardware prefetch only

0x00: prefetch: exclude hardware prefetch

0x08: (M)ESI: Modified

0x04: M(E)SI: Exclusive

0x02: ME(S)I: Shared

0x01: MES(I): Invalid

L2 cache 请求的数目,又分为以下几种情况:

( 1 )所有核的 L2 cache 的请求

( 2 )当前核的 L2 cache 的请求

( 3 )所有核预取时对 L2 cache 的请求

( 4 )仅包含硬件预取时, L2 cache 的请求

( 5 )预取时,除硬件预取之外的其他的 L2 cache 请求

( 6 )访问处于 M 态的 L2 cache 请求的次数

( 7 )访问处于 E 态的 L2 cache 请求的次数

下面的其他状态类似

4 LLC_MISSES: (counter: all)

L2 cache demand requests from this core that missed the L2 (min count: 6000)

Unit masks (default 0x41)

----------

0x41: No unit mask

当前核上, L2 请求缺失的次数

5 LLC_REFS: (counter: all)

L2 cache demand requests from this core (min count: 6000)

Unit masks (default 0x4f)

----------

0x4f: No unit mask

当前核上 L2 请求的数量

6 MISALIGN_MEM_REF: (counter: all)

number of misaligned data memory references (min count: 500)

未对齐的数据内存引用的数量

7 SEGMENT_REG_LOADS: (counter: all)

number of segment register loads (min count: 500)

段寄存器加载的数量

8 DTLB_MISSES: (counter: all)

DTLB miss events (min count: 500)

Unit masks (default 0xf)

----------

0x01: ANY Memory accesses that missed the DTLB.

0x02: MISS_LD DTLB misses due to load operations.

0x04: L0_MISS_LD L0 DTLB misses due to load operations.

0x08: MISS_ST TLB misses due to store operations.

DTLB 未命中的数量,有分为以下几种情况:

( 1 )所有内存访问的 DTLB 的缺失

( 2 )加载操作时的 DTLB 的缺失

( 3 )加载操作时 L0 DTLB 的缺失数

( 4 ) store 操作时, TLB 的缺失数

9 PAGE_WALKS: (counter: all)

Page table walk events (min count: 500)

Unit masks (default 0x2)

----------

0x01: COUNT Number of page-walks executed.

0x02: CYCLES Duration of page-walks in core cycles.

( 1 )遍历页表的次数

( 2 )内核周期中遍历页表的时间

10

MUL: (counter: all)

number of multiplies (min count: 1000)

DIV: (counter: all)

number of divides (min count: 500)

CYCLES_DIV_BUSY: (counter: all)

cycles divider is busy (min count: 1000)

IDLE_DURING_DIV: (counter: all)

cycles divider is busy and all other execution units are idle. (min count: 1000)

上面的事件分别表示:

( 1 )乘法操作的执行次数

( 2 )除法操作的执行次数

( 3 )除法操作执行的时间

( 4 )乘法操作的执行时间

( 5 )除法操作执行但是其他运算不见空闲的时间

11 L2_ADS: (counter: all)

Cycles the L2 address bus is in use. (min count: 500)

Unit masks (default 0x40)

----------

0xc0: All cores

0x40: This core

L2 地址线的使用的 cycle 数

12 L2_DBUS_BUSY_RD: (counter: all)

Cycles the L2 transfers data to the core. (min count: 500)

Unit masks (default 0x40)

----------

0xc0: All cores

0x40: This core

L2 数据总线向内核传送数据的周期数

13 L2_LINES_IN: (counter: all)

number of allocated lines in L2 (min count: 500)

Unit masks (default 0x70)

----------

0xc0: core: all cores

0x40: core: this core

0x30: prefetch: all inclusive

0x10: prefetch: Hardware prefetch only

0x00: prefetch: exclude hardware prefetch

L2 line 分配的次数(应该是写分配的协议)

14 L2_M_LINES_IN: (counter: all)

number of modified lines allocated in L2 (min count: 500)

Unit masks (default 0x40)

----------

0xc0: All cores

0x40: This core

处于 M 态的 L2 line 分配的次数

15 L2_LINES_OUT: (counter: all)

number of recovered lines from L2 (min count: 500)

Unit masks (default 0x70)

----------

0xc0: core: all cores

0x40: core: this core

0x30: prefetch: all inclusive

0x10: prefetch: Hardware prefetch only

0x00: prefetch: exclude hardware prefetch

L2 line 被覆盖的次数

L2_M_LINES_OUT: (counter: all)

number of modified lines removed from L2 (min count: 500)

Unit masks (default 0x70)

----------

0xc0: core: all cores

0x40: core: this core

0x30: prefetch: all inclusive

0x10: prefetch: Hardware prefetch only

0x00: prefetch: exclude hardware prefetch

处于 M 态的 L2 line 被覆盖的次数,这时被覆盖的话,根据不同的 cache 一致性协议,或者协会内存,或者到别的状态。

gprof 介绍

一、 gprof 概念及目的:

Gprof 是 Linux 下一个强有力的程序分析工具。对于 C 、 Pascal 或者 Fortran77 语言的程序,它能够以“ 日志”的形式记录程序运行时的统计信息:程序运行中各个函数消耗的时间和函数调用关系,以及每个函数被调用的次数等 , 还可以显示“注释的源代码”,是程序源代码的一个复本,标记有程序中每行代码的执行次数。

程序分析是以某种语言书写的程序为对象,对其内部的运作流程进行分析。

程序分析的目的主要有三点:

一是通过程序内部各个模块之间的调用关系,整体上把握程序的运行流程,从而更好地理解程序,从中汲取有价值的内容。

二是以系统优化为目的,通过对程序中关键函数的跟踪或者运行时信息的统计,找到系统性能的瓶颈,从而采取进一步行动对程序进行优化。

最后一点,程序分析也有可能用于系统测试和程序调试中。当系统跟踪起来比较复杂,而某个 BUG 又比较难找时,可以通过一些特殊的数据构造一个测试用例,然后将分析到的函数调用关系和运行时实际的函数调用关系进行对比,从而找出错误代码的位置。

程序分析工具不同于调试器,它只产生程序运行时某些函数的调用次数、执行时间等等宏观信息,

而不是每条语句执行时的详细信息。但可以帮助程序员找出众多函数中耗时最多的函数,也可以

帮助程序员分析程序的运行流程。相信这些功能对于分析开源代码的程序员来说,有着相当大

的诱惑力。

二、 Gprof 实现原理:

通过在编译和链接你的程序的时候(使用 -pg 编译和链接选项), gcc 在你应用程序的每个函数中都加入了一个名为 mcount ( or“_mcount” , or“__mcount” , 依赖于编译器或操作系统 ) 的函数 (此函数在 libc 库中实现) ,也就是说你的应用程序里的每一个函数都会调用 mcount, 而 mcount 会在内存中保存一张函数调用图,并通过函数调用堆栈的形式查找子函数和父函数的地址。这张调用图也保存了所有与函数相关的调用时间,调用次数等等的所有信息。

三、 gprof 分析程序

用 gprof 对程序进行分析主要分以下三个步骤:

  • 用编译器对程序进行编译,加上 -pg 参数。
  • 运行编译后的程序。
  • 用 gprof 命令查看程序的运行时信息。

举例分析:

gpro f 是 gnu binutils 工具之一,默认情况下 linux 系统当中都带有这个工具。

  1. 使用 -pg 选项来编译 hello.c ,如果要得到带注释的源码清单,则需要增加 -g 选项。运行:
  2. gcc -pg -g -o hello hello.c
  3. 运行应用程序: ./hello 会在当前目录下产生 gmon.out 文件
  4. 使用 gprof 来分析 gmon.out 文件,需要把它和产生它的应用程序关联起来:
    1. gprof hello gmon.out -p 得到每个函数占用的执行时间
    2. gprof hello gmon.out -q 得到 call graph ,包含了每个函数的调用关系,调用次数,执行时间等信息。
    3. gprof hello gmon.out -A 得到一个带注释的“源代码清单”,它会注释源码,指出每个函数的执行次数。这需要在编译的时候 增加 -g 选项。

用 gprof 程序分析应用程序生成的数据时:如果内容太多,一屏显示不过来,可以通过 more/less 来分屏显示。

gprof -b hello gmon.out | less

四、常用的 Gprof 命令选项释义

-b 不再输出统计图表中每个字段的详细描述。

-q 只输出函数的调用图( Call graph 的那部分信息)。

-p 只输出函数的时间消耗列表。

-E Name 不再输出函数 Name 及其子函数的调用图,此标志类似于 -e 标志,但它在总时间和百分比

时间的计算中排除了由函数 Name 及其子函数所用的时间。

-e Name 不再输出函数 Name 及其子函数的调用图(除非它们有未被限制的其它父函数)。

可以给定多个 -e 标志。一个 -e 标志只能指定一个函数。

-F Name 输出函数 Name 及其子函数的调用图,它类似于 -f 标志,但它在总时间和百分比时间计算

中仅使用所打印的例程的时间。可以指定多个 -F 标志。一个 -F 标志只能指定一个函数。

-F 标志覆盖 -E 标志。

-f Name 输出函数 Name 及其子函数的调用图。可以指定多个 -f 标志。一个 -f 标志只能指定一个函数

-z 显示使用次数为零的例程(按照调用计数和累积时间计算)。

五、 使用注意:

1 )一般 gprof 只能查看用户函数信息。如果想查看库函数的信息,需要在编译是再加入“ -lc_p” 编译参数代替“ -lc” 编译参数,这样程序会链接 libc_p.a 库,才可以产生库函数的 profiling 信息。

注:

– lc 是链接 libc.a 这个库;而 – lc-p 是链接 libc_p.a .

-L 指定库的路径, -l 指定库的名字 libxml2 可以这样写 -lxm2

头文件的路径用 -I ,库文件路径用 -L ,比如说 -I /usr/myinclude –L /usr/mylib

在 GCC 使用 -pg 选项编译后, gcc 会在程序的入口处 (main 函数之前 ) 调用

void monstartup(lowpc, highpc)

在每个函数的入口处调用 void _mcount()

在程序退出时 ( 在 atexit () 里 ) 调用

void _mcleanup()

* monstartup :负责初始化 profile 环境,分配内存空间

* _mcount: 记录每个函数代码的 caller 和 callee 的位置

* _mcleanup :清除 profile 环境,保存结果数据为 gmon.out ,供 gprof 分析结果

2 ) gprof 只能在程序正常结束退出之后才能生成程序测评报告,原因是 gprof 通过在 atexit() 里注册了一个函数来产生结果信息,任何非正常退出都不会执行 atexit() 的动作,所以不会产生 gmon.out 文件。如果你的程序是一个不会退出的服务程序,那就只有修改代码来达到目的。如果不想改变程序的运行方式,可以添加一个信号处理函数解决问题(这样对代码修改最少),例如:

static void sighandler( int sig_no )

{

exit(0);

}

signal( SIGUSR1, sighandler ) ;

当使用 kill -USR1 pid 后,程序退出,生成 gmon.out 文件。

六、实例演示

同样以 AMR 的解码代码为例,来分析它的数据显示情况:

执行代码,分析数据:

[email protected]:~/TestAMRDec$ ./TestAMR AMRDec.par

[email protected] :~/TestAMRDec$ gprof ./TestAMR -b

Flat profile:

<!-- @page { margin: 2cm } P { margin-bottom: 0.21cm } -->

Each sample counts as 0.01 seconds.

% cumulative self self total

time seconds seconds calls ms/call ms/call name

33.33 0.01 0.01 46640 0.00 0.00 multForSqrtANBD

33.33 0.02 0.01 501 0.02 0.02 bgnscdANBD

33.33 0.03 0.01 501 0.02 0.02 postProcANBD

0.00 0.03 0.00 50100 0.00 0.00 getBitsAMRD

0.00 0.03 0.00 11350 0.00 0.00 _normAMR

0.00 0.03 0.00 7943 0.00 0.00 memcpyAMR

0.00 0.03 0.00 5336 0.00 0.00 memsetAMR

0.00 0.03 0.00 4008 0.00 0.00 excitationANBD

0.00 0.03 0.00 3170 0.00 0.00 getSqrtRateANBD

0.00 0.03 0.00 2505 0.00 0.00 getSqrtEngyANBD

0.00 0.03 0.00 2505 0.00 0.00 intLog2ANBD

0.00 0.03 0.00 2004 0.00 0.00 adaptVecDecANBD

0.00 0.03 0.00 2004 0.00 0.00 adaptiveGainCtrlANBD

0.00 0.03 0.00 2004 0.00 0.00 decCodeGainANBD

0.00 0.03 0.00 2004 0.00 0.00 decLag4bitANBD

0.00 0.03 0.00 2004 0.00 0.00 decPitchGainANBD

0.00 0.03 0.00 2004 0.00 0.00 intPow2ANBD

0.00 0.03 0.00 2004 0.00 0.00 interLSFANBD

0.00 0.03 0.00 2004 0.00 0.00 lspToAzANBD

0.00 0.03 0.00 2004 0.00 0.00 mainAlgebraANBD

0.00 0.03 0.00 2004 0.00 0.00 phaseDispANBD

...........................

Call graph

granularity: each sample hit covers 4 byte(s) for 33.33% of 0.03 seconds

i ndex % time self children called name

0.00 0.03 502/502 main [3]

[ 1] 100.0 0.00 0.03 502 decFrameAMRD [1]

0.00 0.03 501/501 decCladANBD [2]

0.00 0.00 2505 / 50100 getBitsAMRD [10]

0.00 0.00 1503/1503 getByteAlignAMRD [29]

0.00 0.00 1002/1002 initBitReaderAMRD [31]

0.00 0.00 501/501 numGetBytesAMRD [42]

- ----------------------------------------------

0.00 0.03 501/501 decFrameAMRD [1]

[2] 100.0 0.00 0.03 501 decCladANBD [2]

0.00 0.02 501/501 decCoreANBD [5]

0.01 0.00 501/501 postFiltANBD [9]

0.00 0.00 47595 / 50100 getBitsAMRD [10]

0.00 0.00 1002/7943 memcpyAMR [12]

0.00 0.00 501/5336 memsetAMR [13]

0.00 0.00 501/501 bitToParamANBD [34]

0.00 0.00 501/501 homingTestANBD [40]

0.00 0.00 501/501 dtxRxHandlerANBD [38]

0.00 0.00 501/501 postProcANBD [43]

0.00 0.00 501/501 restoreANBD [45]

0.00 0.00 501/501 stateMachineANBD [46]

0.00 0.00 501/2505 getSqrtEngyANBD [16]

0.00 0.00 501/501 bgnscdANBD [33]

0.00 0.00 501/501 dtxActivityUpdateANBD [37]

-----------------------------------------------

0.00 0.03 1/1 __libc_start_main [4]

[3] 100.0 0.00 0.03 1 main [3]

0.00 0.03 502/502 decFrameAMRD [1]

0.00 0.00 501/501 unpackAMRD [47]

0.00 0.00 3/3 setParamAMRD [51]

0.00 0.00 3/8 freeAMR [48]

0.00 0.00 1/1 createEngineAMRD [59]

0.00 0.00 1/1 CAMRDecParam::CAMRDecParam() [642]

0.00 0.00 1/1 CAMRDecParam::getParameterFromFile(char*) [641]

0.00 0.00 1/1 createAudioBufferAMR [56]

0.00 0.00 1/1 createBstrmBufferAMR [57]

0.00 0.00 1/1 initEngineAMRD [71]

0.00 0.00 1/1 doneEngineAMRD [63]

0.00 0.00 1/1 releaseEngineAMRD [88]

0.00 0.00 1/1 releaseAudioBufferAMR [86]

0.00 0.00 1/1 releaseBstrmBufferAMR [87]

0.00 0.00 1/1 CWavHeader::CWavHeader() [639]

0.00 0.00 1/1 CWavHeader::MakeWavHeader(int, int) [638]

0.00 0.00 1/1 CWavHeader::~CWavHeader() [640]

0.00 0.00 1/1 CAMRDecParam::~CAMRDecParam() [643]

-----------------------------------------------

...............

Index by function name

[638] CWavHeader::MakeWavHeader(int, int) [14] excitationANBD [50] logoutAMRD

[639] CWavHeader::CWavHeader() [64] exitCladANBD [41] lsfToLspANBD

[640] CWavHeader::~CWavHeader() [48] freeAMR [23] lspToAzANBD

[641] CAMRDecParam::getParameterFromFile(char*) [10] getBitsAMRD [3] main

[636] CAMRDecParam::getLine(_IO_FILE*, char*, char*) [29] getByteAlignAMRD [24] mainAlgebraANBD

[637] CAMRDecParam::strToInt(char*) [39] getMedianANBD [49] mallocAMR

[642] CAMRDecParam::CAMRDecParam() [16] getSqrtEngyANBD [12] memcpyAMR

[643] CAMRDecParam::~CAMRDecParam() [15] getSqrtRateANBD [13] memsetAMR

[635] _normAMR [40] homingTestANBD [11] multForSqrtANBD

[18] adaptVecDecANBD [65] initAdaptANBD [42] numGetBytesAMRD

[19] adaptiveGainCtrlANBD [66] initAlgebraANBD [25] phaseDispANBD

[32] averagingLSPANBD [67] initBitOrderingANBD [9] postFiltANBD

[33] bgnscdANBD [31] initBitReaderAMRD [43] postProcANBD

[34] bitToParamANBD [68] initCladANBD [26] reconstSpeechANBD

[35] calcAz102ANBD [69] initCoreANBD [86] releaseAudioBufferAMR

[52] callocAMR [70] initDtxANBD [87] releaseBstrmBufferAMR

[56] createAudioBufferAMR [71] initEngineAMRD [88] releaseEngineAMRD

[57] createBstrmBufferAMR [72] initGainANBD [44] reorderANBD

对以上各信息进行解释:

时间统计信息中各字段的含义:

%

time

cumulative

seconds

slef

seconds

calls

self

ms/call

total

ms/call

name
函数消耗时间占所有时间的百分比 函数及其以上函数累计执行的时间。单位 : 秒

函数本身所执行的时间。

单位:秒

函数被调用

的次数

调用一次函数的平均时间:不包括它所调用的函数。

单位:毫秒

调用一次函数的平均时间。包括它所调用的函数。

单位:毫秒

函数名

Call graph 中各字段的含义:

index % time self children called name
索引值 函数消耗的时间占所有时间的百分比 函数本身所执行的时间

父函数调用子函数或者

执行子函数花费的时间

调用次数 函数名

使用小结:

在为 gprof 编译程序时:

1. 一般在命令行编译时,应该在编译器的命令行参数中加入“ -pg” 选项,编译时编译器会自动在目标代码中插入用于性能测试的代码片断,这些代码在程序在运行时采集并记录函数的相关信息,程序运行结束后,会在程序退出的路径下生成一个 gmon.out 文件。这个文件就是记录并保存下来的监控数据。

gcc -Wall -g –pg -lc_p example.c -o example -wall 命令给出提示信息。

2. 如果是对多个文件进行编译,需要修改 Makefile 文件,即在文件里面加入 -g 和 -pg 选项就可以了。如果有必要的话,需要加上 — disable-strip 这个开关 ;

3. gprof 的缺点:值得注意的是 gprof 不能对共享库以及多线程进行监测和性能分析。

执行 gprof 时

可以通过命令行方式的 gprof 或图形化的 Kprof 来解读这些数据并对程序的性能进行分析。

上面 已经介绍了通过命令行方式的 gprof 来解读数据,分析程序的性能;

接下来通过图形化的 kprof 来分析数据。

继续阅读