oprofle在android中的交叉编译及使用3

来源:互联网 发布:手机桌面软件 知乎 编辑:程序博客网 时间:2024/06/03 20:33

转载

Android应用的性能如何测试?JAVA层面可以用 TraceView,可是用NDK开发出来的是so,TraceView跟踪不了怎么办?问了Google大神,答案是OProfile!

Oprofile 是Linux系统下一个低开销的系统全局的性能监视工具,利用处理器上所包含的专用的性能监视硬件(若没有性能监视硬件则使用一个基于计时器的代用品)来 收集与性能相关的数据样品。它获得关于内核以及系统上的可执行文件的信息,例如内存是何时被引用的;L2缓存请求的数量;收到的硬件中断数量等。

Oprofile的特点如下:

l         无需重新编译源代码,如果不进行源代码及分析,连调试信息(-g option to gcc)也不是必须的。

l         只在内核中插入一个模块。

l         可以分析运行于系统之上的所有代码(禁用中断的代码除外)

l         系统的额外开销小,Oprofile会增加1%-8%的系统开销(取决于采样频率)

l         兼容所有2.2,2.4,2.6内核,可以运行在SMP系统之上

l         支持主流CPU架构,包括X86、arm、AVR32、mips、powerpc等

Oprofile要想跑在Andorid上,要满足下面的条件:

1.内核要支持

2.要将Oprofile移植到Arm平台上

下面是移植的全过程:

一、Oprofile移植

用到的交叉编译工具如下:

arm-2010.09-50-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2

用到的库如下:

popt-1.14.tar.gz 

binutils-2.21.tar.gz

oprofile-0.9.6.tar.gz

$ tar xvfz arm-2010.09-50-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2 -C ~/

修改~/.bashrc,添加

export PATH=${PATH}:/home/louieli/arm-2010.09/bin

$ tar zxvf popt-1.14.tar.gz 

$ cd popt-1.14

$ ac_cv_va_copy=yes ./configure --with-kernel-support --host=arm-none-linux-gnueabi --prefix=/home/louieli/work/popt

$ make

$ make install

$ tar zxvf binutils-2.21.tar.gz

$ cd binutils-2.21/

$ ./configure --with-kernel-support --host=arm-none-linux-gnueabi --prefix=/home/louieli/work/binutils --enable-shared

$ make LDFLAGS="-all-static"

可能会出现 cc1: warnings being treated as errors,找到出错文件的Makefile文件,将-Werror去掉

$ make install

$ tar zxvf oprofile-0.9.6.tar.gz

$ cd oprofile-0.9.6/

$ ./configure --with-kernel-support --host=arm-none-linux-gnueabi --prefix=/home/louieli/work/oprofile/ --with-extra-libs=/home/louieli/work/popt/lib/ --with-extra-includes=/home/louieli/work/popt/include/ --with-binutils=/home/louieli/work/binutils

$ make LDFLAGS="-all-static -L/home/louieli/work/binutils/lib -Xlinker -R -Xlinker /home/louieli/work/binutils/lib  -L/home/louieli/work/popt/lib/"

$ make install

用file 命令查看,我们需要的oprofile文件都已经变成可以在android上跑的静态链接文件了

install.sh: Bourne-Again shell script text executable

opannotate: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, for GNU/Linux 2.6.16, not stripped

oparchive:  ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, for GNU/Linux 2.6.16, not stripped

opcontrol:  a /system/bin/sh script text executable

opgprof:    ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, for GNU/Linux 2.6.16, not stripped

ophelp:     ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, for GNU/Linux 2.6.16, not stripped

opimport:   ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, for GNU/Linux 2.6.16, not stripped

opjitconv:  ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, for GNU/Linux 2.6.16, not stripped

opreport:   ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, for GNU/Linux 2.6.16, not stripped

oprofiled:  ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, for GNU/Linux 2.6.16, not stripped

二、编译linux内核映像

a)准备交叉编译工具链

android代码树中有一个prebuilt项目,包含了我们编译 内核所需的交叉编译工具。

b)设定环境变量

$ emacs ~/.bashrc

增加如下两行:

export PATH=$PATH:~/android/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin

export ARCH=arm

保存后,同步变化:

$ source ~/.bashrc

c)获得合适的内核源代码

$ cd ~/android

获得内核源代码仓库

$ git clone git://android.git.kernel.org/kernel/common.git kernel

$ cd kernel

$ git branch

显示

* android-2.6.27

说明你现在在android-2.6.27这个分支上,也是 kernel/common.git的默认主分支。

显示所有head分支:

$ git branch -a

显示

* android-2.6.27

remotes/origin/HEAD -> origin/android-2.6.27

remotes/origin/android-2.6.25

remotes/origin/android-2.6.27

remotes/origin/android-2.6.29

remotes/origin/android-goldfish-2.6.27

remotes/origin/android-goldfish-2.6.29

我们选取最新的android-goldfish-2.6.29,其 中goldfish是android的模拟器模拟的CPU。

$ git checkout -b android-goldfish-2.6.29 origin/android-goldfish-2.6.29

$ git branch

显示

android-2.6.27

* android-goldfish-2.6.29

我们已经工作在android-goldfish-2.6.29分支 上了。

d)设定交叉编译参数

打开kernel目录下的Makefile文件,把 CROSS_COMPILE指向刚才下载的prebuilt中的arm-eabi编译器

CROSS_COMPILE ?= arm-eabi-

LDFLAGS_BUILD_ID = $(patsubst -Wl$(comma)%,%,\

$(call ld-option, -Wl$(comma)Cbuild-id,))

这一行注释掉,并且添加一个空的LDFLAGS_BUILD_ID定 义,如下:

LDFLAGS_BUILD_ID =

e)编译内核映像

$ cd ~/android/kernel

$ make goldfish_defconfig

$ make menuconfig

修改内核配置如下

General setup --->

[*] Profiling support (EXPERIMENTAL)

[ ] Activate markers

[*] OProfile system profiling (EXPERIMENTAL) 

这是把OProfile直接编进内核,也可以选择[M] OProfile system profiling (EXPERIMENTAL)会在arch/arm/oprofile文件夹下生成oprofile.ko,oprofile.ko需要用insmod载 入。

$make 

f)测试生成的内核映像

$ emulator -avd myavd -kernel ~/android/kernel/arch/arm/boot/zImage

 

 

 

 

三、Oprofile在android模拟器中 的使用

1.先看一下opcontrol的参数

# opcontrol

opcontrol: usage:

   -l/--list-events list event types and unit masks

   -?/--help        this message

   -v/--version     show version

   --init           loads the oprofile module and oprofilefs

   --setup          give setup arguments (may be omitted)

   --status         show configuration

   --start-daemon   start daemon without starting profiling

   -s/--start       start data collection

   -d/--dump        flush the collected profiling data

   -t/--stop        stop data collection

   -h/--shutdown    stop data collection and kill daemon

   -V/--verbose[=all,sfile,arcs,samples,module,misc,ext]

                    be verbose in the daemon log

   --reset          clears out data from current session

   --save=name      save data from current session to session_name

   --deinit         unload the oprofile module and oprofilefs

   -e/--event=eventspec

      Choose an event. May be specified multiple times. Of the form

      "default" or "name:count:unitmask:kernel:user", where :

      name:     event name, e.g. CPU_CLK_UNHALTED or RTC_INTERRUPTS

      count:    reset counter value e.g. 100000

      unitmask: hardware unit mask e.g. 0x0f

      kernel:   whether to profile kernel: 0 or 1

      user:     whether to profile userspace: 0 or 1

   -p/--separate=type,[types]

       Separate profiles as follows :

       none:     no profile separation

       library:  separate shared library profiles per-application

       kernel:   same as library, plus kernel profiles

       thread:   per-thread/process profiles

       cpu:      per CPU profiles

       all:      all of the above

   -c/--callgraph=#depth         enable callgraph sample collection with a maximum depth.

                                 Use 0 to disable callgraph profiling.

   --session-dir=dir             place sample database in dir instead of

                                 default location (/var/lib/oprofile)

   -i/--image=name[,names]       list of binaries to profile (default is "all")

   --vmlinux=file                vmlinux kernel image

   --no-vmlinux                  no kernel image (vmlinux) available

   --kernel-range=start,end      kernel range vma address in hexadecimal

   --buffer-size=num             kernel buffer size in sample units

   --buffer-watershed            kernel buffer watershed in sample units (2.6 only=

   --cpu-buffer-size=num         per-cpu buffer size in units (2.6 only)

   --note-table-size             kernel notes buffer size in notes units (2.4 only)

   --xen                         Xen image (for Xen only)

   --active-domains=<list>       List of domains in profiling session (for Xen only)

                                 (list contains domain ids separated by commas)

2.使用方法

将我们之 前编译好的oprofile和busybox装入模拟器

执行oprofile目录中的install.sh 将oprofile装入模拟 器

adb push busybox /data/busybox

$adb shell  //进入模拟 器shell

#chmod 777 /data/busybox

# /data/busybox --install /data/busybox

#export PATH=/data/busybox:$PATH:/data/oprofile

# mount -o remount rw /

# mount -o rw,remount -t yaffs2 /dev/mtdblock3 /system

# touch /etc/mtab

# echo nodev /dev/oprofile oprofilefs rw 0 0>/etc/mtab

# mkdir /dev/oprofile

# mount -t oprofilefs nodev /dev/oprofile    //这一句很 重要,没有这一句会出现下面的错误

# opcontrol --init      

cat: can't open '/dev/oprofile/cpu_type': No such file or directory

Unable to open cpu_type file for reading

Make sure you have done opcontrol --init

cpu_type 'unset' is not valid

you should upgrade oprofile or force the use of timer mode

# opcontrol --init     //初始化, 只需运行一次

# opcontrol --setup --callgraph=2 --session-dir=/data/first --no-vmlinux

Using 2.6+ OProfile kernel interface.

Using log file /data/first/samples/oprofiled.log

Daemon started.

Profiler running.

# opcontrol --status

Daemon running: pid 637

Separate options: none

vmlinux file: none

Image filter: none

Call-graph depth: 2

# opcontrol --start     //启动profiler

Using 2.6+ OProfile kernel interface.

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

Daemon started.

Profiler running.

# /data/test/test     //运行我们 的程序 ( 我的测试 程序通过这条指令编译arm-none-linux-gnueabi-gcc -g -o test test.c -static -fno-omit-frame-pointer)

in c

in a

in b

in a

in c

in b

in a

in a

# opcontrol --dump   //收集采样 数据

# opcontrol --stop //停止profiler

Stopping profiling.

#opreport --session-dir=/data/first -l /data/test/test       //查看报告

CPU: CPU with timer interrupt, speed 0 MHz (estimated)

Profiling through timer interrupt

samples  %        symbol name

11291    79.9589  a

1129      7.9952  b

853       6.0406  main

848       6.0052  c

现在我们就可以根据oprofile的输出对我们的程序进行优化了。

如果有哪 位同学也想试一把的话,一定要用linux。这种移植环境很 重要,我之前就在测试机(win7+cygwin)上浪费了很多时间。这里有打包好的工具,大家可以下载。其中kernel-qemu就是我们 之前编译好 的内核,替换掉Android SDK中的kernel-qemu就行了。 祝各位好运