Perf编译和使用
1. Perf基本原理
Perf可以对程序进行函数级别的采样,从而了解程序的性能瓶颈点。其基本原理:每隔一个固定的时间,就是CPU上产生一个中断,查看当前是哪个进程、哪个函数,然后给对应的进程和函数加一个统计值,这样就知道CPU有多少时间再某个进程或函数上。
PMU 允许软件针对某种硬件事件设置 counter,此后处理器便开始统计该事件的发生次数,当发生的次数超过 counter 内设置的值后,便产生中断。比如 cache miss 达到某个值后,PMU 便能产生相应的中断。捕获这些中断,便可以考察程序对这些硬件特性的利用效率了。
Perf 是内置于Linux (2.6+)内核源码树中的性能剖析(profiling)工具。它基于事件采样原理,以性能事件为基础,支持针对处理器相关性能指标与操作系统相关性能指标的性能剖析。可用于性能瓶颈的查找与热点代码的定位。
Perf可监测三种事件:
- Hardware Event由PMU部件产生,在特定的条件下探测性能事件是否发生以及发生的次数。比如cache命中。
- Software Event是内核产生的事件,分布在各个功能模块中,统计和操作系统相关性能事件。比如进程切换,tick数等。
- Tracepoint Event是内核中静态tracepoint所触发的事件,这些tracepoint用来判断程序运行期间内核的行为细节,比如slab分配器的分配次数等。

先通过一个例子看看perf 可以分析什么。
#include<stdio.h>
#include <sys/time.h>
int longa()
{
int i,j;
for(i = 0; i < 10000; i++)
j=i;
return j;
}
void func1()
{
int i,j;
for(i=0 ; i < 1000000; i++)
j=longa();
printf("longa=%d \n",j);
}
void func2()
{
int i,j;
for(i = 0; i< 2000000; i++)
j=longa();
printf("longa=%d \n",j);
}
int main(void)
{
func1();
func2();
}
观察程序[code1]的热点代码在函数longa()中。运行./main &后,在命令行中执行:
$perf top
Perf 会给出如下结果:
![图1-2. Perf 对程序[code1]的分析结果](https://i-blog.csdnimg.cn/direct/9e0f5382a206497ab0952831b4bb5868.png)
“perf top”命令利用默认的性能事件“cycles”对[code1]进行热点分析。“cycles”是处理器周期事件。这条命令能够分析出消耗处理器周期最多的代码,在处理器频率稳定的前提下,我们可以认为perf 给出热点代码的就是消耗时间最多的代码段。
从上图可以看到,在[code1]执行期间,函数longa函数消耗了97%的CPU周期,是消耗处理器周期最多的热点代码。这跟我们预想的一样。
执行perf top的命令时,perf 会通过系统调用sys_perf_event_open在内核中注册一个监测 “cycles”事件的性能计数器。内核根据perf 提供的信息在PMU 上初始化一个硬件性能计数器(PMC: Performance Monitoring Counter)。PMC随着CPU 周期的增加而自动累加。在PMC 溢出时,PMU 触发一个PMI(Performance Monitoring Interrupt)中断。内核在PMI 中断的处理函数中保存PMC 的计数值,触发中断时的指令地址(Register IP:Instruction Pointer),当前时间戳以及当前进程的PID,TID,comm 等信息。我们把这些信息统称为一个采样(sample)。内核会将收集到的sample 放入用于跟用户空间通信的Ring Buffer。用户空间里的perf 分析程序采用mmap 机制从ring buffer 中读入采样,并对其解析。perf 根据pid,comm 等信息可以找到对应的进程。根据IP 与ELF 文件中的符号表可以查到触发PMI 中断的指令所在的函数。为了能够使perf 读到函数名,我们的目标程序必须具备符号表。如果读者在perf 的分析结果中只看到一串地址,而没有对应的函数名时,可能是由于在编译时利用strip 删除了ELF 文件中的符号表,或是perf安装时缺少部分插件。建议读者在性能分析阶段,保留程序中的symbol table,debug info等信息。关于perf的安装可参考本文下一部分。
根据上述的perf 采样原理可以得知,perf 假设两次采样之间,即两次相邻的PMI 中断之间系统执行的是同一个进程的同一个函数。这种假设会带来一定的误差,当读者感觉perf 给出的结果不准时,不妨提高采样频率,perf 会给出更加精确的结果。
但必须要考虑到perf等剖析工具的自身开销对系统或应用程序造成的影响,这种影响越小越好。采样频率越高,得到的剖析结果就越可信,但是采样过程对系统造成的干扰就越高。
2. Perf功能介绍

Perf 是一个包含22 种子工具的工具集,功能很全面,下表给出了各个子工具的功能描述。
| 序号 | 功能 | 说明 |
|---|---|---|
| 1 | annotate | 根据数据文件,注解被采样到的函数,显示指令级别的热点。 |
| 2 | archive | 根据数据文件中记录的build-id,将所有被采样到的ELF文件打成压缩包。利用此压缩包,可以在任何机器上分析数据文件中记录的采样数据。 |
| 3 | bench | Perf 中内置的benchmark,目前包括两套针对调度器和内存管理子系统的benchmark。 |
| 4 | buildid-cache | 管理perf 的buildid 缓存。每个ELF 文件都有一个独一无二的buildid。Buildid 被perf 用来关联性能数据与ELF文件。 |
| 5 | buildid-list | 列出数据文件中记录的所有buildid。 |
| 6 | diff | 对比两个数据文件的差异。能够给出每个符号(函数)在热点分析上的具体差异。 |
| 7 | evlist | 列出数据文件中的所有性能事件。 |
| 8 | inject | 该工具读取perf record 工具记录的事件流,并将其定向到标准输出。在被分析代码中的任何一点,都可以向事件流中注入其它事件。 |
| 9 | kmem | 针对内存子系统的分析工具。 |
| 10 | kvm | 此工具可以用来追踪、测试运行于KVM 虚拟机上的Guest OS。 |
| 11 | list | 列出当前系统支持的所有性能事件。包括硬件性能事件、软件性能事件以及检查点 |
| 12 | lock | 分析内核中的加锁信息。包括锁的争用情况,等待延迟等。 |
| 13 | record | 收集采样信息,并将其记录在数据文件中。随后可通过其它工具对数据文件进行分析。 |
| 14 | report | 读取perf record 创建的数据文件,并给出热点分析结果。 |
| 15 | sched | 针对调度器子系统的分析工具。 |
| 16 | script | 执行perl 或python 写的功能扩展脚本、生成脚本框架、读取数据文件中的数据信息等. |
| 17 | stat | 剖析某个特定进程的性能概况,包括CPI、Cache 丢失率等。 |
| 18 | test | Perf 对当前软硬件平台的测试工具。可以用此工具测试当前的软硬件平台(主要是处理器型号和内部版本)是否能支持perf 的所有功能。 |
| 19 | timechart | 生成一幅描述处理器与各进程状态变化的矢量图。 |
| 20 | top | 类似于Linux 的top 命令,对系统性能进行实时分析。 |
| 21 | trace | strace inspired tool. |
| 22 | probe | 用于定义动态检查点。 |
3. Perf工具生成
3.1. 内核支持
3.1.1. 内核配置文件
Linux内核提供了丰富的配置选项来决定是否支持某些特性,因此在内核编译时,需要打开如下选项:
# for perf_events:
CONFIG_PERF_EVENTS=y
# for stack traces:
CONFIG_FRAME_POINTER=y
# kernel symbols:
CONFIG_KALLSYMS=y
# tracepoints:
CONFIG_TRACEPOINTS=y
# kernel function trace:
CONFIG_FTRACE=y
# kernel-level dynamic tracing:
CONFIG_KPROBES=y
CONFIG_KPROBE_EVENT=y
# user-level dynamic tracing:
CONFIG_UPROBES=y
CONFIG_UPROBE_EVENTS=y
# full kernel debug info:
CONFIG_DEBUG_INFO=y
# kernel lock tracing:
CONFIG_LOCKDEP=y
# kernel lock tracing:
CONFIG_LOCK_STAT=y
如果flash空间有限,对uImage大小有要求,可考虑去掉以下的内核配置:
CONFIG_TRACEPOINTS=y
CONFIG_FTRACE=y
CONFIG_KPROBES=y
CONFIG_KPROBE_EVENTS=y
CONFIG_UPROBES=y
CONFIG_UPROBE_EVENTS=y
CONFIG_LOCK_STAT=y
3.1.2. 设备树
在设备树中添加pmu的描述符节点,主要包括:
pmu {
/* CPU核信息,具体匹配名查看arch/arm/kernel/perf_event_v7.c */
compatible = "arm,cortex-a7-pmu";
/* pmu中断信息,每个CPU对应1个pmu中断号 */
interrupts = <GIC_SPI 4 4>, <GIC_SPI 6 4>;
/* 设置中断亲和力 */
interrupt-affinity = <&cpu0>, <&cpu1>;
};
3.2. 编译perf
perf需要一些依赖插件才可发挥性能,如果未安装这些插件,在编译perf可执行文件时会提示:“No libelf found”,“No liblzma found”等warning。导致编译出的perf可执行文件在使用过程中会遇到“不能显示函数名”或“不能显示调用关系”等问题。其中最为重要的就是 elf, unwind 库,安装时需要注意交叉编译器的选择,以及指定插件的安装路径。

3.2.1. 相关修改
修改点一(如果没有报错可以不改):由于perf在执行过程中会到指定目录下查找ko文件,默认路径是/lib/modules/内核版本号。如果该目录没有ko文件,则运行时可能报错。因此在编译perf工具时,可以先修改把ko路径指定到挂载目录下。
修改kernel/tools/perf/util/machine.c文件的machine__set_modules_path函数
static int machine__set_modules_path(struct machine *machine)
{
char *version;
char modules_path[PATH_MAX];
version = get_kernel_version(machine->root_dir);
if (!version)
return -1;
#if 0
snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s",
machine->root_dir, version);
#else
snprintf(modules_path, sizeof(modules_path), "%s/tmp/perf_H8/mpp_modules",
machine->root_dir);
#endif
free(version);
return map_groups__set_modules_path_dir(&machine->kmaps, modules_path, 0);
}
修改点二(如果没有报错可以不改):在perf编译时,会去交叉编译工具链的路径查找liblzma库,但是由于我们没有交叉编译工具/opt目的权限,无法将该库复制到指定路径。当前暂时修改perf的编译规则,添加liblzma库的链接路径。
修改kernel/tools/perf/Makefile.perf
增加,lzma库的路径,默认在kernel/tools/lib目录
LDFLAGS+=-L /data1/chenlei10/work/h8/trunk/kernel/kernel_sdk/tools/lib
修改点三(如果没有报错可以不改):在perf编译时,如果出现:in function post_process_module_probe_trace_events': perf/util/probe-event.c:669: undefined reference to post_process_probe_trace_point’的类似错误(是因为配置没开trace[但是这个开了之后可能影响驱动。需要重新编译]),因此这里直接将该函数返回-1,不在支持驱动trace

3.2.2. 编译方法
将依赖的库文件复制到kernel/tools目录,主要包括:
Binutils:https://mirrors.aliyun.com/gnu/binutils/
bzip2:https://sourceware.org/bzip2/downloads.html
elfutils:https://sourceware.org/elfutils/
libtool:https://www.gnu.org/software/libtool/
libunwind:https://github.com/libunwind/libunwind/releases
xz:https://github.com/xz-mirror/xz/releases
zlib:https://github.com/madler/zlib/releases
然后执行下面脚本,开始自动编译。当脚本执行结束后,在kernel/tools/perf/目录下可查看到生成的成果物perf可执行程序。
将生成的perf可执行文件拷贝到设备中,如果在设备上执行的过程中提示缺少xxx.so时将服务器kernel/tools/lib目录想的so文件拷贝到设备的/lib目录下。一般会依赖以及编译的libunwind.so.8、libunwind-arm.so.8、liblzma.so.5、libelf.so.1、libz.so.1等。
#!/bin/bash
CPU_JOB_NUM=$(grep processor /proc/cpuinfo | awk '{field=$NF};END{print field+1}')
CURPATH=$(pwd)
ZLIB_VER=1.2.11 # 根据实际版本进行修改
ELFUTILS_VER=0.174
XZ_VER=5.2.5
UNWIND_VER=1.5.0
LIBTOOL_VER=2.4.6
BZIP2_VER=1.0.8
BINUTILS_VER=2.35.2
DO_PREPARE=1 # 默认执行build_prepare
usage() {
echo "***********************************************************"
echo "USAGE:"
echo "./zoo [platform] [options]"
echo "DESCIPTION"
echo " platform: arm/arm64/musl , default ''."
echo " options:"
echo " --no-prepare 跳过环境安装步骤"
echo "***********************************************************"
}
parse_args() {
PLATFORM=""
while [ "$#" -gt 0 ]; do
case "$1" in
--no-prepare)
DO_PREPARE=0
shift
;;
*)
# 第一个非选项参数视为平台
if [ -z "$PLATFORM" ]; then
PLATFORM="$1"
else
echo "错误: 无效参数: \$1"
usage
exit 1
fi
shift
;;
esac
done
if [ -z "$PLATFORM" ]; then
usage
exit 1
fi
parse_plat "$PLATFORM"
}
parse_plat() {
elif [ "$1" == "arm64" ] ;then
CROSS_COMPILE=aarch64-linux-gnu
ARCH=arm64
elif [ "$1" == "arm" ]; then
CROSS_COMPILE=arm-linux-gnu
ARCH=arm
elif [ "$1" == "musl" ]; then
CROSS_COMPILE=aarch64-linux-musl
ARCH=arm64
else
usage
exit
fi
}
binutils_install() {
echo "#################Start Building binutils####################"
rm -rf binutils-${BINUTILS_VER}
tar -xf binutils-${BINUTILS_VER}.tar.xz
sleep 1
cd binutils-${BINUTILS_VER}/
CC=${CROSS_COMPILE}-gcc ./configure --host=${CROSS_COMPILE} --prefix=${CURPATH}
make -j${CPU_JOB_NUM} all
make -j${CPU_JOB_NUM} install
cd ..
echo "#################Building binutils done####################"
}
zlib_install() {
echo "#################Start Building zlib####################"
rm -rf zlib-${ZLIB_VER}
tar -xf zlib-${ZLIB_VER}.tar.xz
sleep 1
cd zlib-${ZLIB_VER}/
CC=${CROSS_COMPILE}-gcc ./configure --prefix=${CURPATH}
make -j${CPU_JOB_NUM}
make -j${CPU_JOB_NUM} install
cd ..
echo "#################Building zlib done####################"
}
bzip2_install() {
echo "#################Start Building bz2####################"
rm -rf bzip2-${BZIP2_VER}
tar -zxf bzip2-${BZIP2_VER}.tar.gz
sleep 1
cd bzip2-${BZIP2_VER}/
# CC=${CROSS_COMPILE}-gcc ./configure --prefix=${CURPATH}
# make -j${CPU_JOB_NUM}
make -j${CPU_JOB_NUM} CC=${CROSS_COMPILE}-gcc PREFIX=${CURPATH} install
cd ..
echo "#################Building bz2 done####################"
}
elfutils_install() {
echo "#################Start Building elfutils####################"
rm -rf elfutils-${ELFUTILS_VER}
tar -xf elfutils-${ELFUTILS_VER}.tar.bz2
sleep 1
cd elfutils-${ELFUTILS_VER}
sed -i 's/-Werror//g' $(find . -type f -exec egrep -l _no_Werror {} \;)
autoreconf -i
./configure CC=${CROSS_COMPILE}-gcc --prefix=${CURPATH} --host=aarch64-linux --program-prefix="eu-" \
CFLAGS=-I${CURPATH}/include LDFLAGS=-L${CURPATH}/lib LIBS=-lz --without-bzlib --without-lzma --disable-debuginfod
make -j${CPU_JOB_NUM}
# make install
make -j${CPU_JOB_NUM} install
cd ..
echo "#################Building elfutils done####################"
}
lzma_install() {
echo "#################Start Building lzma####################"
rm -rf xz-${XZ_VER}
tar -xf xz-${XZ_VER}.tar.bz2
sleep 1
cd xz-${XZ_VER}/
CC=${CROSS_COMPILE}-gcc ./configure --host=${CROSS_COMPILE} --prefix=${CURPATH} --enable-shared=yes
make -j${CPU_JOB_NUM}
make -j${CPU_JOB_NUM} install
cd ..
echo "#################Building elfutils done####################"
}
libunwind_install() {
echo "#################Start Building libunwind####################"
rm -rf libunwind-${UNWIND_VER}
tar -zxf libunwind-${UNWIND_VER}.tar.gz
sleep 1
cd libunwind-${UNWIND_VER}
CC=${CROSS_COMPILE}-gcc ./configure --host=${CROSS_COMPILE} --prefix=${CURPATH} --enable-shared=yes
make -j${CPU_JOB_NUM}
make -j${CPU_JOB_NUM} install
cd ..
echo "#################Building libunwind done####################"
}
libtool_install() {
echo "#################Start Building libtool####################"
rm -rf libtool-${LIBTOOL_VER}
tar -xf libtool-${LIBTOOL_VER}.tar.xz
sleep 1
cd libtool-${LIBTOOL_VER}
CC=${CROSS_COMPILE}-gcc ./configure --host=${CROSS_COMPILE} --prefix=${CURPATH}
make -j${CPU_JOB_NUM}
make -j${CPU_JOB_NUM} install
cd ..
echo "#################Building libtool done####################"
}
perf_build_musl() {
echo "#################Start Building perf musl####################"
make NO_LIBCAP=1 NO_DWARF=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_LIBNUMA=1 NO_LIBPERL=1 NO_LIBPYTHON=1 NO_LIBZSTD=1 WERROR=0 \
DEBUG=1 LIBDW_DIR=${CURPATH} LIBUNWIND_DIR=${CURPATH} ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE}- \
CFLAGS+=-I${CURPATH}/include LDFLAGS+=-L${CURPATH}/lib perf
echo "#################Building done####################"
}
perf_build() {
echo "#################Start Building perf glibc####################"
make WERROR=0 DEBUG=1 LIBDW_DIR=${CURPATH} LIBUNWIND_DIR=${CURPATH} ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE}- \
CFLAGS+=-I${CURPATH}/include LDFLAGS+=-L${CURPATH}/lib perf
echo "#################Building done####################"
}
parse_plat $1
CC=${CROSS_COMPILE}-gcc
CXX=${CROSS_COMPILE}-g++
CPP=${CROSS_COMPILE}-gcc
AS=${CROSS_COMPILE}-as
LD=${CROSS_COMPILE}-ld
GDB=${CROSS_COMPILE}-gdb
STRIP=${CROSS_COMPILE}-strip
RANLIB=${CROSS_COMPILE}-ranlib
OBJCOPY=${CROSS_COMPILE}-objcopy
OBJDUMP=${CROSS_COMPILE}-objdump
AR=${CROSS_COMPILE}-ar
NM=${CROSS_COMPILE}-nm
M4=m4
build_prepare() {
echo "#################Start build_prepare####################"
cd build/feature/
# make ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib CFLAGS+=-I${CURPATH}/include test-sdt.bin
# make ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib CFLAGS+=-I${CURPATH}/include test-libbfd.bin
# make ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib CFLAGS+=-I${CURPATH}/include test-libbfd-buildid.bin
echo "make test-elf feature!!"
make ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib CFLAGS+=-I${CURPATH}/include test-libelf.bin
make ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib CFLAGS+=-I${CURPATH}/include test-libelf-getphdrnum.bin
make ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib CFLAGS+=-I${CURPATH}/include test-libelf-gelf_getnote.bin
make ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib CFLAGS+=-I${CURPATH}/include test-libelf-getshdrstrndx.bin
echo "make test-elf feature ok"
make ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib CFLAGS+=-I${CURPATH}/include test-zlib.bin
make ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib CFLAGS+=-I${CURPATH}/include test-lzma.bin
make ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib CFLAGS+=-I${CURPATH}/include test-dwarf.bin
make ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib \
LDFLAGS+=-ldw LDFLAGS+=-lelf LDFLAGS+=-lebl LDFLAGS+=-lz LDFLAGS+=-llzma LDFLAGS+=-lbz2 LDFLAGS+=-ldl \
CFLAGS+=-I${CURPATH}/include test-dwarf_getlocations.bin
if [ ${ARCH} == "arm64" ]; then
echo "make test-libunwind-aarch64.bin"
make ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib \
LDFLAGS+=-lunwind LDFLAGS+=-lunwind-aarch64 CFLAGS+=-I${CURPATH}/include test-libunwind.bin
make ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib \
CFLAGS+=-I${CURPATH}/include test-libunwind-aarch64.bin
make ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib \
LDFLAGS+=-lunwind LDFLAGS+=-lunwind-aarch64 CFLAGS+=-I${CURPATH}/include test-libunwind-debug-frame.bin
make ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib \
LDFLAGS+=-lunwind LDFLAGS+=-lunwind-aarch64 CFLAGS+=-I${CURPATH}/include test-libunwind-debug-frame-aarch64.bin
else
echo "make test-libunwind-arm.bin"
make ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib \
LDFLAGS+=-lunwind LDFLAGS+=-lunwind-arm CFLAGS+=-I${CURPATH}/include test-libunwind.bin
make ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib \
CFLAGS+=-I${CURPATH}/include test-libunwind-arm.bin
make ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib \
LDFLAGS+=-lunwind LDFLAGS+=-lunwind-arm CFLAGS+=-I${CURPATH}/include test-libunwind-debug-frame.bin
make ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib \
LDFLAGS+=-lunwind LDFLAGS+=-lunwind-arm CFLAGS+=-I${CURPATH}/include test-libunwind-debug-frame-arm.bin
fi
make ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib \
LDFLAGS+=-ldw LDFLAGS+=-lelf LDFLAGS+=-lebl LDFLAGS+=-lz LDFLAGS+=-llzma LDFLAGS+=-lbz2 LDFLAGS+=-ldl \
CFLAGS+=-I${CURPATH}/include test-libdw-dwarf-unwind.bin
cd -
echo "#################build_prepare done####################"
}
parse_args "$@" # 解析命令行参数
echo "make start"
make perf_clean
# 根据参数决定是否安装环境
if [ $DO_PREPARE -eq 1 ]; then
echo "执行环境安装步骤..."
# 清除全部库相关的环境
echo "clear env"
rm -rf ${CURPATH}/lib/*
rm -rf ${CURPATH}/include/*
sleep 1
svn up
sleep 1
make build_clean
# binutils_install
bzip2_install
zlib_install
elfutils_install
lzma_install
libunwind_install
libtool_install
else
echo "跳过build_prepare步骤"
fi
build_prepare
if [[ "${CROSS_COMPILE}" == *"musl"* ]]; then
echo "检测到musl工具链,使用musl专用构建"
perf_build_musl
else
echo "检测到glibc工具链,使用标准构建"
perf_build
fi
4. Perf火焰图
使用perf report展示record记录的结果,但这种格式很不直观。因此可以使用火焰图展示结果。
Flame Graph项目位于GitHub上:https://github.com/brendangregg/FlameGraph
用git将其clone下来:git clone https://github.com/brendangregg/FlameGraph.git
flamegraph的使用方法:
-
一、监测性能事件,生成记录文件。
./perf record -F 89 -ag --call-graph dwarf
-ag为记录全部线程,如果要指定线程,则使用参数-g -p 进程号,如果指定线程,则使用-g –t 线程号。
注意执行该指令后,确保执行中没有诸如no symbol、CPU I/O overload报错产生,否则生成的火焰图中会有大量不能识别的符号表。
Ctrl+c结束执行后,在当前目录下会生成采样数据perf.data。一般建议采集时间为20秒到50秒。可以使用参数指定采样时间如:
./perf record -F 89 -ag --call-graph dwarf – sleep 30 -
二、用perf script工具对perf.data进行解析
perf script -i perf.data &> perf.unfold -
三、将perf.unfold拷贝到FlameGraph-master目录,对其中的符号再进行折叠:
./stackcollapse-perf.pl perf.unfold &> perf.folded -
四、最后生成svg火焰图,使用chrome浏览器打开:
./flamegraph.pl perf.folded > perf.svg
242

被折叠的 条评论
为什么被折叠?



