Hi35xxVxxx 调试工具使用指南
前言
本文档为使用linux perf工具进行性能分析的使用指导。
本文档(本指南)主要适用于以下工程师:
- 技术支持工程师
- 软件开发工程师
说明:
Hi3516CV610的工具链为arm-v01c02-linux-musleabi-。
在本文中可能出现下列标志,它们所代表的含义如下。
perf工具
perf介绍
概述
perf是linux下的一款性能分析工具,能够进行函数级与指令级的热点查找。它由一个叫“performance counters”的内核子系统实现,基于事件采样原理,以性能事件为基础,支持针对处理器相关性能指标与操作系统相关性能指标的性能剖析,可用于性能瓶颈的查找与热点代码的定位。
perf不但可以分析特定应用程序的性能问题,还可以分析内核的性能问题,当然也可以同时分析应用程序和内核,从而全面理解应用程序中的性能瓶颈。
综合来看,perf工具有以下优势:
- perf是linux kernel自带的系统性能优化工具,与linux kernel的紧密结合;
- 工具能够对应用、内核、整个系统进行性能分析,查找性能瓶颈,发现性能问题的原因及热点代码;
- 是一款综合性能分析工具,大到系统全局性能,小到进程、线程,甚至函数、汇编级别。
更多perf的信息参考:https://perf.wiki.kernel.org/index.php/Tutorial
perf依赖介绍
zlib
zlib是一个广泛使用的压缩库,它提供了一种高效的压缩和解压缩数据的方法。
elfutils
elfutils是一个用于操作ELF(Executable and Linkable Format)二进制文件的开源工具集,包括查找和处理GNU Linux上进程和核心文件的DWARF调试数据、符号、线程状态和堆栈跟踪。
binutils
binutils是一组用于创建、操作和转换二进制文件的工具集。它包括了一些常用的工具,如汇编器、链接器、反汇编器、目标文件复制器等等。
perf编译使用说明
本文档以Hi3519DV500芯片为例。
编译perf glibc版本及其依赖库
因直接在Linux内核tools/perf路径下编译出的perf会缺少依赖库,进而导致perf在使用时,无法解析得到函数名,只能得到地址;通过预先编译好perf的依赖库,然后在perf编译时设置好所编译好的依赖库的路径,即可解决该问题。
perf依赖源码下载
-
下载zlib-1.2.13.tar.gz
-
下载elfutils-0.188.tar.bz2
链接:https://sourceware.org/elfutils/ftp/0.188/elfutils-0.188.tar.bz2
-
下载binutils-2.38.tar.gz
编译环境准备
-
新建如图1所示目录结构,并且新建一个build_perf.sh放到perf_tool/build目录下,将下面的“脚本内容”拷贝到build_perf.sh脚本中,可复制执行以下命令。
命令如下:
mkdir perf_tool cd perf_tool mkdir build open_source cd build touch build_perf.sh cd .. cd open_source mkdir binutils elfutils zlib
脚本内容如下:
#!/bin/bash # user modify compile parameter # cross compiler CROSS_PERF=aarch64-v01c01-linux-gnu # linux-5.10 kernel root directory KERNEL_DIR=xxx/open_source/linux/linux-5.10 # color define BLACK="\e[30;1m" RED="\e[31;1m" GREEN="\e[32;1m" YELLOW="\e[33;1m" BLUE="\e[34;1m" PURPLE="\e[35;1m" CYAN="\e[36;1m" WHITE="\e[37;1m" DONE="\033[0m" # compile variable set -e test -d $KERNEL_DIR || { echo -e "$YELLOW invalid kernel dir $DONE"; exit 1; } CUR_DIR=`pwd` BUILD_DIR=${CUR_DIR} ROOT_DIR=${CUR_DIR}/.. OUPUT_DIR=${BUILD_DIR}/output OUPUT_TEMP=${OUPUT_DIR}/temp_file OUPUT_INSTALL=${OUPUT_DIR}/board_install OPEN_SOURCE_DIR=${ROOT_DIR}/open_source BINUTILS_DIR=${OPEN_SOURCE_DIR}/binutils ELFUTILS_DIR=${OPEN_SOURCE_DIR}/elfutils ZLIB_DIR=${OPEN_SOURCE_DIR}/zlib PERF_DIR=${KERNEL_DIR}/tools/perf # compile function function perf_clean() { cd $OPEN_SOURCE_DIR/"$1" rm -rf out if [ -d "$2" ]; then cd "$2" make clean fi echo "clean $2" } function perf_distclean() { cd $OPEN_SOURCE_DIR/"$1" rm -rf out rm -rf $2 echo "distclean $2" } function build_zlib(){ echo -e "$YELLOW building zlib $DONE"; cd ${ZLIB_DIR} if [ ! -e "zlib-1.2.13.tar.gz" ]; then echo -e "$YELLOW can not find zlib-1.2.13.tar.gz, exit 1$DONE"; exit 1 fi if [ -d "out" ]; then echo -e "$YELLOW out is already exist, delete$DONE"; rm -rf out fi mkdir -p out ZLIB_OUTDIR=${ZLIB_DIR}/out if [ ! -d "zlib-1.2.13" ]; then tar xf zlib-1.2.13.tar.gz fi cd zlib-1.2.13 ZLIB_CROSS_CFLAGS="-fstack-protector-strong -fPIC"; ZLIB_CROSS_LDFLAGS="-Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack"; CC=${CROSS_PERF}-gcc CFLAGS=${ZLIB_CROSS_CFLAGS} \ LDFLAGS=${ZLIB_CROSS_LDFLAGS} \ ./configure --prefix=${ZLIB_OUTDIR}; make -j; make install; mkdir -p ${OUPUT_TEMP} cp -rf ${ZLIB_OUTDIR}/* ${OUPUT_TEMP}/ } function build_binutils(){ echo -e "$YELLOW building binutils $DONE"; cd ${BINUTILS_DIR} if [ ! -e "binutils-2.38.tar.gz" ]; then echo -e "$YELLOW can not find binutils-2.38.tar.gz, exit 1$DONE"; exit 1 fi if [ -d "out" ]; then echo -e "$YELLOW out is already exist, delete$DONE"; rm -rf out fi mkdir -p out BINUTILS_OUTDIR=${BINUTILS_DIR}/out if [ ! -d "binutils-2.38" ]; then tar xf binutils-2.38.tar.gz fi cd binutils-2.38 CFLAGS="-fstack-protector-strong -fPIC" \ ./configure --host=${CROSS_PERF} --disable-gdb --disable-gdbserver \ --disable-libdecnumber --disable-readline --disable-sim \ --enable-install-libbfd --enable-install-libiberty --enable-shared make -j; make DESTDIR=${BINUTILS_OUTDIR} install; cp -rf ${BINUTILS_OUTDIR}/usr/local/* ${OUPUT_TEMP}/ } function build_elfutils(){ echo -e "$YELLOW building elfutils $DONE"; cd ${ELFUTILS_DIR} if [ ! -e "elfutils-0.188.tar.bz2" ]; then echo -e "$YELLOW can not find elfutils-0.188.tar.bz2, exit 1$DONE"; exit 1 fi if [ -d "out" ]; then echo -e "$YELLOW out is already exist, delete$DONE"; rm -rf out fi mkdir -p out ELFUTILS_OUTDIR=${ELFUTILS_DIR}/out if [ ! -d "elfutils-0.188" ]; then tar xjf elfutils-0.188.tar.bz2 fi cd elfutils-0.188 ELFUTILS_CROSS=${CROSS_PERF}; ELFUTILS_CROSS_CFLAGS="-fstack-protector-strong \ -fPIC -I${OUPUT_TEMP}/include"; ELFUTILS_CROSS_LDFLAGS="-Wl,-z,relro -Wl,-z,now \ -Wl,-z,noexecstack -L${OUPUT_TEMP}/lib -lz"; CFLAGS="${ELFUTILS_CROSS_CFLAGS}" LDFLAGS="${ELFUTILS_CROSS_LDFLAGS}" \ ./configure --host=${ELFUTILS_CROSS} --prefix=${ELFUTILS_OUTDIR} \ --with-zlib --without-bzlib --without-lzma \ --disable-silent-rules --disable-dependency-tracking \ --disable-debuginfod --disable-libdebuginfod --enable-nls; make -j; make install; cp -rf ${ELFUTILS_OUTDIR}/* ${OUPUT_TEMP}/ } function build_perf(){ cd ${PERF_DIR} sed -i "s/CORE_CFLAGS += -Wall/CORE_CFLAGS += -w/g" Makefile.config make ARCH=arm64 CROSS_COMPILE=${CROSS_PERF}- clean make ARCH=arm64 CROSS_COMPILE=${CROSS_PERF}- \ EXTRA_CFLAGS="-Wno-deprecated-declarations -I${OUPUT_TEMP}/include \ -I${OUPUT_TEMP}/include/elfutils" \ LDFLAGS+="-L${OUPUT_TEMP}/lib -lz -lelf -lbfd" } function generate_install(){ rm -rf ${OUPUT_INSTALL} mkdir -p ${OUPUT_INSTALL}/bin/ mkdir -p ${OUPUT_INSTALL}/lib/ cp -f ${OUPUT_TEMP}/bin/objdump ${OUPUT_INSTALL}/bin cp -f ${PERF_DIR}/perf ${OUPUT_INSTALL}/bin cp -f ${OUPUT_TEMP}/lib/libz.so* ${OUPUT_INSTALL}/lib cp -f ${OUPUT_TEMP}/lib/libelf*so* ${OUPUT_INSTALL}/lib cp -f ${OUPUT_TEMP}/lib/libdw*so* ${OUPUT_INSTALL}/lib cp -f ${OUPUT_TEMP}/lib/libbfd*.so ${OUPUT_INSTALL}/lib cp -f ${OUPUT_TEMP}/lib/libopcodes*.so ${OUPUT_INSTALL}/lib cp -f ${OUPUT_TEMP}/lib/libctf.so* ${OUPUT_INSTALL}/lib tree ${OUPUT_INSTALL} } # compile paremeter if [ ! -n "$1" ] || [ "$1" == "-h" ] || [ "$#" -gt "1" ]; then echo -e "${YELLOW}./build_perf.sh all :build perf for linux-5.10${DONE}"; echo -e "${YELLOW}./build_perf.sh clean :build clean${DONE}"; echo -e "${YELLOW}./build_perf.sh distclean :build distclean${DONE}"; exit 0 fi if [ "$1" == "clean" ]; then # clean perf dependence perf_clean "binutils" "binutils-2.38" perf_clean "elfutils" "elfutils-0.188" perf_clean "zlib" "zlib-1.2.13" # clean output rm -rf ${OUPUT_DIR} # clean sdk cd ${PERF_DIR} make clean; fi if [ "$1" == "distclean" ]; then # distclean perf dependence perf_distclean "binutils" "binutils-2.38" perf_distclean "elfutils" "elfutils-0.188" perf_distclean "zlib" "zlib-1.2.13" # clean output rm -rf ${OUPUT_DIR} # distclean sdk cd ${PERF_DIR} make clean; fi if [ "$1" == "all" ]; then build_zlib; build_binutils; build_elfutils; build_perf; generate_install; fi -
将perf依赖源码下载下载的perf依赖源码,放入图1的perf_tool目录下。

源码编译
-
进入perf_tool/build/的目录:修改脚本build_perf.sh中两个参数为当前环境对应值,如图1所示。

-
然后给脚本添加执行权限:chmod +x build_perf.sh
-
参考以下步骤进行编译,查看帮助:./build_perf.sh -h。
-
每次编译前建议先清理文件
- 清理文件:./build_perf.sh clean
- 深度清理文件:./build_perf.sh distclean
-
整体编译:./build_perf.sh all
执行整体编译后,会在perf_tool/build/output/board_install下生成perf工具及其依赖库,如下图。

编译perf musl版本
编译环境准备
- 将perf依赖源码下载下载的perf依赖源码,放在新建perf目录下。
-
新建一个build_musl_perf.sh放到perf_tool/build目录下,将下面的“脚本内容”拷贝到build_musl_perf.sh脚本中。
#!/bin/bash # user modify compile parameter # cross compiler CROSS_PERF=aarch64-v01c01-linux-musl # linux-5.10 kernel root directory KERNEL_DIR=XXX/open_source/linux/linux-5.10.y # perf dependency source directory DEPS_DIR=XXX/open_source/linux/perf # color define YELLOW="\e[33;1m" GREEN="\e[32;1m" DONE="\033[0m" # compile variable set -e test -d $KERNEL_DIR || { echo -e "$YELLOW invalid kernel dir $DONE"; exit 1; } CUR_DIR=`pwd` OUPUT_DIR=${CUR_DIR}/output TOOLCHAIN_BASE=XXX/aarch64-v01c01-linux-musl-gcc CROSS=${TOOLCHAIN_BASE}/bin/${CROSS_PERF}- SYSROOT=${TOOLCHAIN_BASE}/target BUILD_DIR=${CUR_DIR}/build_deps PREFIX=${CUR_DIR}/deps_install PERF_DIR=${KERNEL_DIR}/tools/perf export PATH=${TOOLCHAIN_BASE}/bin:$PATH function build_zlib(){ echo -e "$GREEN ====== [1/3] building zlib ====== $DONE"; mkdir -p $BUILD_DIR $PREFIX if [ ! -d "$BUILD_DIR/zlib-1.2.13" ]; then tar xzf $DEPS_DIR/zlib-1.2.13.tar.gz -C $BUILD_DIR fi cd $BUILD_DIR/zlib-1.2.13 CC="${CROSS}gcc -Wno-error" AR=${CROSS}ar RANLIB=${CROSS}ranlib \ CFLAGS="--sysroot=$SYSROOT -fPIC " \ ./configure --prefix=$PREFIX --static make -j$(nproc) CC=${CROSS}gcc AR=${CROSS}ar RANLIB=${CROSS}ranlib \ CFLAGS="--sysroot=$SYSROOT -fPIC -Wno-error" make install echo -e "$GREEN ====== zlib done ====== $DONE" } function build_elfutils(){ echo -e "$GREEN ====== [2/3] building elfutils ====== $DONE"; if [ ! -d "$BUILD_DIR/elfutils-0.188" ]; then tar xjf $DEPS_DIR/elfutils-0.188.tar.bz2 -C $BUILD_DIR fi cd $BUILD_DIR/elfutils-0.188 CC=${CROSS}gcc CXX=${CROSS}g++ AR=${CROSS}ar RANLIB=${CROSS}ranlib \ CFLAGS="--sysroot=$SYSROOT -I$PREFIX/include -fPIC -Wno-error" \ LDFLAGS="--sysroot=$SYSROOT -L$PREFIX/lib -L$SYSROOT/usr/lib" \ LIBS="-lobstack -largp -lfts" \ ac_cv_search__obstack_free="-lobstack" \ ac_cv_search_fts_close="-lfts" \ ac_cv_search_argp_parse="-largp" \ ./configure --prefix=$PREFIX --host=$CROSS_PERF \ --disable-nls --disable-debuginfod --disable-libdebuginfod \ --with-zlib --without-bzlib --without-lzma --without-zstd make -j$(nproc) CC=${CROSS}gcc CXX=${CROSS}g++ AR=${CROSS}ar RANLIB=${CROSS}ranlib \ CFLAGS="--sysroot=$SYSROOT -I$PREFIX/include -fPIC -Wno-error" \ LDFLAGS="--sysroot=$SYSROOT -L$PREFIX/lib -L$SYSROOT/usr/lib" \ LIBS="-lobstack -largp -lfts" || true mkdir -p $PREFIX/include/elfutils $PREFIX/lib cp libelf/libelf.a libdw/libdw.a libdwfl/libdwfl.a libdwelf/libdwelf.a \ libebl/libebl.a backends/libebl_backends.a $PREFIX/lib/ cp libelf/elf.h libelf/gelf.h libelf/libelf.h libelf/nlist.h $PREFIX/include/ cp libdw/dwarf.h libdw/libdw.h libdw/known-dwarf.h $PREFIX/include/ cp libdwfl/libdwfl.h libdwelf/libdwelf.h libebl/libebl.h $PREFIX/include/ echo -e "$GREEN ====== elfutils done ====== $DONE" } function build_perf(){ echo -e "$GREEN ====== [3/3] building perf ====== $DONE"; cd ${KERNEL_DIR} make -C tools/perf \ ARCH=arm64 CROSS_COMPILE=${CROSS_PERF}- \ CC=${CROSS_PERF}-gcc CXX=${CROSS_PERF}-g++ \ EXTRA_CFLAGS="--sysroot=$SYSROOT -I$PREFIX/include -I$SYSROOT/usr/include -L$PREFIX/lib -L$SYSROOT/usr/lib" \ EXTRA_LDFLAGS="--sysroot=$SYSROOT -L$PREFIX/lib -L$SYSROOT/usr/lib" \ LIBDW_DIR=$PREFIX WERROR=0 \ NO_LIBUNWIND=1 NO_LIBPYTHON=1 NO_LIBPERL=1 NO_SLANG=1 NO_GTK2=1 \ NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBPF=1 NO_JVMTI=1 NO_LIBCRYPTO=1 \ NO_LIBBABELTRACE=1 NO_LIBCAP=1 NO_CORESIGHT=1 echo -e "$GREEN ====== perf done ====== $DONE" } function generate_install(){ rm -rf ${OUPUT_DIR} mkdir -p ${OUPUT_DIR}/bin cp -f ${PERF_DIR}/perf ${OUPUT_DIR}/bin ${CROSS_PERF}-strip -s ${OUPUT_DIR}/bin/perf echo -e "$GREEN ====== output ====== $DONE" tree ${OUPUT_DIR} || ls -lR ${OUPUT_DIR} } # compile parameter if [ ! -n "$1" ] || [ "$1" == "-h" ] || [ "$#" -gt "1" ]; then echo -e "${YELLOW}./build_musl_perf.sh all : build perf for linux-5.10 (musl)${DONE}"; echo -e "${YELLOW}./build_musl_perf.sh clean : build clean${DONE}"; echo -e "${YELLOW}./build_musl_perf.sh distclean : build distclean${DONE}"; exit 0 fi if [ "$1" == "clean" ]; then rm -rf $BUILD_DIR $PREFIX rm -rf ${OUPUT_DIR} cd ${PERF_DIR} make clean; fi if [ "$1" == "distclean" ]; then rm -rf $BUILD_DIR $PREFIX rm -rf ${OUPUT_DIR} cd ${PERF_DIR} make clean; fi if [ "$1" == "all" ]; then build_zlib; build_elfutils; build_perf; generate_install; fi -
进入perf_tool/build/的目录:修改脚本build_musl_perf.sh中四个参数为当前环境对应值
CROSS_PERF=arm-v01c02-linux-musleabi
KERNEL_DIR=xxx/open_source/linux/linux-5.10.y
DEPS_DIR=xxx/open_source/linux/perf
TOOLCHAIN_BASE=xxx/arm-v01c02-linux-musleabi-gcc
源码编译
-
给脚本添加执行权限
chmod +x build_musl_perf.sh
-
查看帮助
./build_musl_perf.sh -h
-
清理(建议每次编译前执行)
./build_musl_perf.sh clean
-
一键编译
./build_musl_perf.sh all
-
执行整体编译后,会在perf_tool/output/bin下生成perf工具
打开perf配置选项,重编linux内核
使用perf工具时需要打开linux内核相关的perf配置选项。其步骤如下:
-
进入linux-5.10内核源码目录后,执行:
make ARCH=arm64 CROSS_COMPILE=aarch64-v01c01-linux-musl- menuconfig
注:当使用gnu工具链时,需将musl替换成gnu。
-
开启以下四个内核配置选项

-
参考SDK版本包中bsp/readme_cn.txt 内核编译步骤进行编译;
-
生成的内核镜像即为支持perf的linux内核镜像,烧写到单板,然后配合编译perf glibc版本及其依赖库章节编译的perf工具及其依赖库,即可使用。
注:内核配置进行更改后,若业务代码存在ko,则需要根据打开了perf配置的内核对其进行重编ko。
perf使用说明
perf使用准备
-
将打开perf配置选项,重编linux内核编译生成的内核镜像烧写到单板。参考SDK版本包镜像烧写方式。
注:由于开了perf后,内核镜像会变大,uboot可能无法正常启动内核,因此,需要根据当前大小情况修改uboot的bootargs和bootcmd,以确保内核能正常起来。
-
重启单板,mount当前perf_tool/output/bin文件夹所在服务器。
-
mount完成后,将perf_tool/output/bin导出到单板的PATH,lib导出到单板的LD_LIBRARY_PATH环境变量中,即可执行perf命令,参考perf使用方式章节。
export PATH="xxx/bin:$PATH"
注意:
- 替换xxx路径,为当前实际路径
- xxx/bin目录下的可执行文件,需要添加执行权限:chmod +x -R xxx/bin/*
perf使用方式
常用命令
-
perf top
实时显示系统/进程的性能统计信息
-
perf list
查看支持的性能事件
-
perf --help
查看帮助
生成火焰图
-
采集数据:
perf record -a -F 99 -g sleep 20
参数解释:
- -a:采集所有cpu信息
- -F 99 :1秒钟进行采集99次
- -g:收集调用栈
- sleep 20:采集时间为20秒
-
解析数据:
perf script -i perf.data > out.unfold
-
服务器下载FlameGraph:
-
将out.unfold拷贝到服务器下载的FlameGraph目录下

-
在服务器上执行折叠符号:
./stackcollapse-perf.pl out.unfold | ./flamegraph.pl > out.svg
采集热点汇编指令
-
采集数据:
perf record -e cache-misses -t tid
参数解释:
-e cache-misses:指定cache-misses事件
-t tid:指定采集线程的tid
-
得到函数热点:
perf report > out_fun.txt
-
得到汇编指令热点:
perf annotate xxxFunc > out_annotate.txt
注:
- xxxFunc:需要分析的函数符号
-
汇编指令无对应C语言函数符号时,可以通过以下方式解决:
- 编译选项加-g
-
使用相应工具链的objdump,查看相应.o文件的函数汇编对应关系,如:
aarch64-v01c01-linux-gnu-objdump -d -l xxx/xxx.o > test1
注:当使用musl工具链时,需将gnu替换成musl。
注意事项
perf注意事项
- 编译perf相关依赖时,需要保证编译环境能正常编译出linux-5.10内核;
- 本文档仅支持linux-5.10内核版本。
FIQ工具
注意:
Hi3516CV610 不支持 FIQ 调试工具。
FIQ是一个调试工具,可以在操作系统卡死的时候触发一个FIQ,在操作系统异常发生时,将异常现场的寄存器信息,以及操作系统的调用堆栈打印出来;具体使用方法和注意事项详见SDK版本包smp/a55_linux/source/bsp/components/nmi_glue/readme_cn.txt。




