OProfile(obsolete)

Table of Contents

#note@2014-05-08: 最后更新时间还是2003-07-08. 项目应该已经废弃了。

#note@2015-08-01: 最后更新时间居然变为了2014-09-11, 然后1.0.0版本发布。但是使用方式和之前版本有非常大的变化。打算 单开一个文档 来记录新版本是如何使用的。

1 Overview

http://www.ibm.com/developerworks/cn/linux/l-oprof/

oprofile是用于Linux的若干种评测和性能监控工具中的一种。它可以工作在不同的体系结构上,包括IA32,IA64和AMD Athlon系列。它的开销小,将被包含在(Linux)2.6版的内核中。

oprofile可以帮助用户识别诸如循环的展开、高速缓存的使用率低、低效的类型转换和冗余操作、错误预测转移等问题。它收集有关处理器事件的信息,其中包括TLB的故障、停机、存储器访问、位于 DCU(数据高速缓存单元)中的总线路数、一个 DCU 故障的周期数,以及不可高速缓存的和可高速缓存的指令的获取数量。oprofile是一种细粒度的工具,可以为指令集或者为函数、系统调用或中断处理例程收集采样。oprofile通过取样来工作。使用收集到的评测数据,用户可以很容易地找出性能问题。

oprofile由下面三个部分组成

1.1 内核驱动

内核驱动工作的时候分为三种模式:

  • 硬件事件计数器模式
  • RTC模式
  • TimeInterrupt模式

关于RTC模式和TimeInterrupt模式之间的差别并不是非常了解。RTC(RealTimeClock)和TimeInterrupt模式工作方式,大致都是在底层开启一个定时器并且定时触发中断,然后知道当时PC的位置,然后把这段时间都作为这条PC所执行的时间。定位相对来说粗糙的,但是对于大部分的情况都是管用的。但是如果执行代码部分是屏蔽了中断的话,那么实际上这个部分是不会被profile的。

硬件事件计数器模式需要CPU的支持。CPU内部维护一系列事件计数器。Profile针对某些事件把计数器设置为初始值比如C,然后一旦操作触发事件的话,那么计数器就会–。一旦计数器变为0的话,CPU内部就会触发一个不可屏蔽的中断(NMI/Non Maskable Interrupt)。这个时候我们了解到PC在什么位置,就把这条指令作为触发这个时间的指令。需要注意的是,如果计数器的C设置非常低的话,那么产生NMI就会非常频繁。这样会导致overhead过高,系统非常不稳定。使用硬件事件计数器的话,需要CPU开启APIC这个选项,否则允许指定参数的得不到采样结果。

上面三种模式都是基于采样的方式,理论上说采样方式得到的结果并不准确,但是实际工作很好。还需要注意就是,陷入中断时刻和发出中断时刻会存在一些误差,在这段时间内指令还在运行。也就是说发出中断之后,CPU可能继续执行了后面几条指令之后,才陷入中断得到的PC值就是不准确的。

>>> Linux Kernel Development

实时时钟(RTC)是用来持久存放系统时间的设备, 即便系统关闭后, 它也可以靠主板上的微型电池提供的电力保持系统的计时. 在PC体系结构中, RTC和CMOS集成在一起, 而且RTC的运行和BIOS的保存设置都是通过同一个电池供电的.

当系统启动时, 内核通过读取RTC来初始化墙上的时钟, 该时间存放在xtime变量中. 虽然内核通常不会在系统启动后再读取xtime变量, 但是有些体系结构(比如x86)会周期性地将当前时间存回RTC中. 尽管如此, 实时时钟主要作用仍是在启动时初始化xtime变量.

1.2 守护进程

守护进程(daemon)用于收集内核驱动采样数据,并且将采样数据dump到外部文件。对应的可执行程序是bin/oprofiled.

1.3 工具附件

通过读取dump出来的外部文件得到采样数据进行加工:

  • ophelp //显示帮助信息
  • opcontrol //oprofile控制程序
  • opreport //产生报表信息
  • opannotate //annotate代码,包括源代码和汇编代码

2 Getting Started

如果不是2.6以及以上版本Linux内核的话,那么必须提供源代码树来编译出oprofile驱动。

./configure --prefix=<install-dir> --with-linux=<linux-source-code-root>

如果是2.6以及以上版本Linux内核的话,那么可以使用不需要源代码

./configure --prefix=<install-dir> --with-kernel-support

因为这些版本的Linux内核已经自带了oprofile驱动。

使用步骤非常简单,大致如下:

  • 使用opcontrol启动daemon并且开始采样
  • 执行应用程序
  • 使用opcontrol dump出剖分数据
  • 使用opcontrol停止采样并且关闭daemon程序
  • 使用opreport和opannotate查看结果
  • #note: 使用oprofile需要root权限

之前写过一个脚本来包装整个过程。只用它来对应用程序做过profile, 没有对kernel做过profile. 脚本会:

  • 生成profile汇总报表
  • 对源代码进行标注
  • 对汇编代码进行标注
# deprecated !!!
easy_oprofile() {
    if [ $# -lt 5 ]; then
        echo "easy_oprofile image-file(ELF) command"
        exit 1
    fi
    local ifile=$1; shift
    local rfile="oprof.rpt"
    local srcdir="."
    local afile="oprof.asm"
    local command=$@
    echo "Image file: $ifile"
    echo "Report file: $rfile"
    echo "Source directory: $srcdir"
    echo "Assembly file: $afile"
    echo "Command: $command"

    sudo opcontrol --reset
    sudo opcontrol --no-vmlinux --separate=lib --start --image=$ifile
    $command
    sudo opcontrol --dump
    sudo opcontrol --shutdown
    sudo opreport -l image:$ifile -o $rfile
    sudo opannotate -s --output-dir=$srcdir $ifile
    sudo opannotate -a $ifile > $afile
}

3 Notes

3.1 opcontrol

常用命令参数如下:

  • –reset # 清除session数据
  • –no-vmlinux|–vmlinux=/boot/vmlinux-2.6.9.2_9-0-0-0 # 选择内核镜像剖分内核
  • –deinit # 卸载oprofile device driver
  • –init # 加载oprofile device driver
  • –setup # 设置oprofile默认参数,保存在/root/.oprofile/daemonrc下面
  • –status # 显示配置信息
  • –start-daemon # 启动daemon程序
  • –shutdown # 停止采样并且关闭daemon程序
  • –start # 启动daemon程序并且开始采样
  • –stop # 停止采样
  • –dump # 控制daemon程序dump所收集的sample数据
  • –list-event # 显示所支持事件
  • –help # 帮助
  • –buffer-size|–buffer-watershed|–cpu-buffer-size=<size> # 内核采样sample的buffer大小
  • –event=<eventspec> # 设置采样事件,可以指定多次
  • –separate=none|lib|kernel|cpu|all # 剖分信息划分(none)
    • none # 按照应用程序划分
    • lib # 按照library划分
    • kernel # 按照内核态和用户态划分
    • thread # 按照每个线程划分
    • cpu # 按照每个CPU划分
    • all # 按照上面所有都划分
  • –image=image,[image]|"all" # 只profile某些特定的binary
  • –session-dir=<dir> # 存放session目录(/var/lib/oprofile)
  • –save=<session-name> # 保存session

常用用法如下:

  • opcontrol –reset # 清除之前session
  • opcontrol –start –no-vmlinux –image=image –separate=lib # 开启daemon程序并且剖析
  • 执行程序
  • opcontrol –dump # dump剖析数据
  • opcontrol –shutdown # 停止采样并且关闭daemon程序

执行一次opcontrol –start,所使用的参数就会记录在/root/.oprofile/daemonrc文件。效果就等同于首先调用opcontrol –setup, 然后调用opcontrol –start.

3.2 opreport

opreport包含两类参数,一类是过滤参数用于通知应用打印出关于哪些方面的信息,一类是命令参数是关于如何打印出这方面的信息。

常用过滤参数如下,过滤参数值可以使用glob风格来指定:

  • session|session-exclude:sessionlist # 按照session名称过滤
  • image|image-exclude:imagelist # 按照imgae名字过滤
  • event:eventlist # 按照event进行过滤
  • count:countlist # 按照计数器进行过滤
  • unit-mask:masklist # 按照mask进行过滤
  • cpu:cpulist # 按照cpu编号过滤
  • tgid:pidlist # 按照进程号过滤
  • tid:tidlist # 按照线程号过滤

常用命令参数如下:

  • -g|–debug-info # 显示每个symbol的文件和行号
  • -D|–demangle=none|normal|smart # 对C++名字解构(normal)
  • -i|–include-symbols|-e|–exclude-symbols # 符号选择
  • -h|–help # 帮助信息
  • -f|–long-filenames # 显示长文件名
  • -r|–requote-sort # 反向输出
  • -l|–symbols # 显示symbol而不是binary image
  • -o|–output-file # 输出文件
  • -t|–threshold # sample的percentage超过这个值才显示
  • -X|–xml # 产生XML输出信息
  • –session-dir=<dir> # 存放session目录(/var/lib/oprofile)

常用用法如下:

  • opreport -l image:<image> -o profile.log # 产生profile.log.

3.3 opannotate

常用命令参数如下:

  • -a|–assembly # 产生汇编文件注释
  • -b|–base-dirs # 将被strip掉的调试信息中源文件绝对路径前缀
  • -d|–search-dirs # 源文件搜索路径
    • 这两个选项配合使用场景是,如果我是用/tmp/a.cc编译出来的话,那么debug信息里面存放的就是/tmp/a.cc的路径。如果我们源代码在其他路径比如/home/tmp/a.cc的话,那么就不能够进行annotate.
    • 这个问题可以使用上面两个参数解决。我们可以指定-b /tmp -d /home/tmp,这样debug信息中首先将/tmp stripped掉,然后拿a.cc在/home/tmp下面搜索。
  • -D|–demangle=none|normal|smart # 对C++名字解构(normal)
  • -i|–include-symbols|-e|–exclude-symbols # 符号选择
  • –include-file|–exclude-file # 文件选择
  • -t|–threshold # sample的percentage超过这个值才显示
  • -s|–source # 产生源文件注释
  • -o|–output-dir # 输出目录
  • –help|–usage # 帮助信息

常用方法如下:

  • opannotate -a <image> > profile.asm # 产生汇编文件注释.profile.asm是结果.
  • opannotate -s –output-dir=. <image> # 产生源文件注释.在当前目录下面产生.
comments powered by Disqus