Perf编译和使用
Perf编译和使用
- 1. Perf基本原理
- 2. Perf功能介绍
- 3. Perf工具生成
- 3.1. 内核支持
- 3.1.1. 内核配置文件
- 3.1.2. 设备树
- 3.2. 编译perf
- 3.2.1. 相关修改
- 3.2.2. 编译方法
- 4. 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 会给出如下结果:
“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 0snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s",machine->root_dir, version);#elsesnprintf(modules_path, sizeof(modules_path), "%s/tmp/perf_H8/mpp_modules",machine->root_dir);#endiffree(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/bashCPU_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.2DO_PREPARE=1 # 默认执行build_prepareusage() {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 ]; docase "$1" in--no-prepare)DO_PREPARE=0shift;;*)# 第一个非选项参数视为平台if [ -z "$PLATFORM" ]; thenPLATFORM="$1"elseecho "错误: 无效参数: \$1"usageexit 1fishift;;esacdoneif [ -z "$PLATFORM" ]; thenusageexit 1fiparse_plat "$PLATFORM"
}parse_plat() {elif [ "$1" == "arm64" ] ;thenCROSS_COMPILE=aarch64-linux-gnuARCH=arm64elif [ "$1" == "arm" ]; thenCROSS_COMPILE=arm-linux-gnuARCH=armelif [ "$1" == "musl" ]; thenCROSS_COMPILE=aarch64-linux-muslARCH=arm64elseusageexitfi
}binutils_install() {echo "#################Start Building binutils####################"rm -rf binutils-${BINUTILS_VER}tar -xf binutils-${BINUTILS_VER}.tar.xzsleep 1cd binutils-${BINUTILS_VER}/CC=${CROSS_COMPILE}-gcc ./configure --host=${CROSS_COMPILE} --prefix=${CURPATH}make -j${CPU_JOB_NUM} allmake -j${CPU_JOB_NUM} installcd ..echo "#################Building binutils done####################"
}zlib_install() {echo "#################Start Building zlib####################"rm -rf zlib-${ZLIB_VER}tar -xf zlib-${ZLIB_VER}.tar.xzsleep 1cd zlib-${ZLIB_VER}/CC=${CROSS_COMPILE}-gcc ./configure --prefix=${CURPATH}make -j${CPU_JOB_NUM}make -j${CPU_JOB_NUM} installcd ..echo "#################Building zlib done####################"
}bzip2_install() {echo "#################Start Building bz2####################"rm -rf bzip2-${BZIP2_VER}tar -zxf bzip2-${BZIP2_VER}.tar.gzsleep 1cd 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} installcd ..echo "#################Building bz2 done####################"
}elfutils_install() {echo "#################Start Building elfutils####################"rm -rf elfutils-${ELFUTILS_VER}tar -xf elfutils-${ELFUTILS_VER}.tar.bz2sleep 1cd 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-debuginfodmake -j${CPU_JOB_NUM}# make installmake -j${CPU_JOB_NUM} installcd ..echo "#################Building elfutils done####################"}lzma_install() {echo "#################Start Building lzma####################"rm -rf xz-${XZ_VER}tar -xf xz-${XZ_VER}.tar.bz2sleep 1cd xz-${XZ_VER}/CC=${CROSS_COMPILE}-gcc ./configure --host=${CROSS_COMPILE} --prefix=${CURPATH} --enable-shared=yesmake -j${CPU_JOB_NUM}make -j${CPU_JOB_NUM} installcd ..echo "#################Building elfutils done####################"
}libunwind_install() {echo "#################Start Building libunwind####################"rm -rf libunwind-${UNWIND_VER}tar -zxf libunwind-${UNWIND_VER}.tar.gzsleep 1cd libunwind-${UNWIND_VER}CC=${CROSS_COMPILE}-gcc ./configure --host=${CROSS_COMPILE} --prefix=${CURPATH} --enable-shared=yesmake -j${CPU_JOB_NUM}make -j${CPU_JOB_NUM} installcd ..echo "#################Building libunwind done####################"
}libtool_install() {echo "#################Start Building libtool####################"rm -rf libtool-${LIBTOOL_VER}tar -xf libtool-${LIBTOOL_VER}.tar.xzsleep 1cd libtool-${LIBTOOL_VER}CC=${CROSS_COMPILE}-gcc ./configure --host=${CROSS_COMPILE} --prefix=${CURPATH}make -j${CPU_JOB_NUM}make -j${CPU_JOB_NUM} installcd ..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 perfecho "#################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 perfecho "#################Building done####################"
}parse_plat $1CC=${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=m4build_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.binecho "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.binmake ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib CFLAGS+=-I${CURPATH}/include test-libelf-getphdrnum.binmake ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib CFLAGS+=-I${CURPATH}/include test-libelf-gelf_getnote.binmake ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib CFLAGS+=-I${CURPATH}/include test-libelf-getshdrstrndx.binecho "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.binmake ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib CFLAGS+=-I${CURPATH}/include test-lzma.binmake ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib CFLAGS+=-I${CURPATH}/include test-dwarf.binmake 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.binif [ ${ARCH} == "arm64" ]; thenecho "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.binmake ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib \CFLAGS+=-I${CURPATH}/include test-libunwind-aarch64.binmake 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.binmake 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.binelseecho "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.binmake ARCH=${ARCH} CC=${CC} CROSS_COMPILE=${CROSS_COMPILE}- LDFLAGS=-static LDFLAGS+=-L${CURPATH}/lib \CFLAGS+=-I${CURPATH}/include test-libunwind-arm.binmake 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.binmake 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.binfimake 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.bincd -echo "#################build_prepare done####################"
}parse_args "$@" # 解析命令行参数echo "make start"
make perf_clean# 根据参数决定是否安装环境
if [ $DO_PREPARE -eq 1 ]; thenecho "执行环境安装步骤..."# 清除全部库相关的环境echo "clear env"rm -rf ${CURPATH}/lib/*rm -rf ${CURPATH}/include/*sleep 1svn upsleep 1make build_clean# binutils_installbzip2_installzlib_installelfutils_installlzma_installlibunwind_installlibtool_install
elseecho "跳过build_prepare步骤"
fibuild_prepareif [[ "${CROSS_COMPILE}" == *"musl"* ]]; thenecho "检测到musl工具链,使用musl专用构建"perf_build_musl
elseecho "检测到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