获取更多相关的嵌入式开发工具,可收藏系列博文,持续更新中:
参考:https://zhuanlan.zhihu.com/p/690363716
什么是 perf?
你是否曾经遇到过这样的场景:一个运行中的程序突然变慢,CPU 使用率飙升,但又不清楚瓶颈在哪里?或者,你希望优化代码,但不知道哪一部分消耗了最多的 CPU 时间?又或者,系统响应缓慢,你怀疑是内存访问、锁竞争还是 I/O 等待在作祟?
在这种情况下,你除了重启应用或盲目地修改代码,还做过什么吗?
你是否想过有一种工具,可以像手术刀一样精准地剖析程序的性能,告诉你每个函数、每行代码的执行时间、调用次数,甚至是 CPU 缓存的命中率?
你是否希望收集到足够详细的信息,来定位性能问题的根源?
-- 上述情况,可称之为"性能分析的迷雾"。
换句话来讲,程序在运行,但性能表现不符合预期,我们需要一种方法,能够深入到内核和 CPU 的层面,去观察程序运行时到底发生了什么。
perf (Performance Event) 正是这样一款工具。它是内建于 Linux 内核源码树中的性能分析工具,其核心是一个名为 perf_event 的系统调用,它利用 CPU 的性能监视单元(PMU,Performance Monitoring Unit)和内核中的软件事件,来收集系统和应用程序的性能数据。
perf的实现原理
perf 之所以强大,是因为它不仅仅是一个简单的性能计数器读取工具。它能够:
- 硬件级别分析:利用 CPU 的 PMU,可以精确地统计指令执行周期数、缓存命中/未命中次数、分支预测错误次数等底层硬件事件。
- 软件级别分析:可以追踪内核和用户空间的各种软件事件,如上下文切换、系统调用、页错误、进程创建等。
- 动态追踪:可以动态地跟踪内核函数、用户空间函数的调用,甚至可以动态地注入探针(probe)来监控任意代码位置。
- 采样与计数 :支持两种主要工作模式:
- 计数 (Counting):统计某个事件在整个程序或系统运行期间发生的总次数。
- 采样 (Sampling):按照固定频率(如每 1000 个周期)记录程序当前的执行上下文(如正在执行的函数、调用栈),然后通过统计分析,得出性能热点分布图。
通过 perf,你可以从宏观(系统级)到微观(单条指令)地理解程序的性能行为,快速定位性能瓶颈,为性能优化提供数据支撑。
如何安装与启用perf?
perf 工具通常作为 Linux 内核工具包的一部分发布。
编译perf工具
在大多数 Linux 发行版中,可以直接在kernel/tools/perf下直接编译perf源码
bash
# 配置交叉编译环境:
export ARCH=arm
export CROSS_COMPILE=/opt/novatek_soc/NT98533/arm-ca32-linux-uclibcgnueabihf-10.4.0/bin/arm-ca32-linux-uclibcgnueabihf-
# 如需配置静态编译,需修改Makefile.config
# 任意位置加上
LDFLAGS += -static
# 如遇到相关动态库报错,可以执行下面命令屏蔽
# 报错:
~: cannot find -lebl: No such file or directory
~: cannot find -llzma: No such file or directory
~: cannot find -lbz2: No such file or directory
# 屏蔽方法
export NO_LIBELF=1
export NO_LZMA=1
export NO_LIBZSTD=1
# 执行编译:
make
# 生成工具
$ ls -lh perf
-rwxrwxr-x 1 ... 12M Jun 3 19:45 perf
# 通过strip去除工具调试信息
.../bin/arm-ca32-linux-uclibcgnueabihf-strip perf
$ ls -lh perf
-rwxrwxr-x 1 ... 1.7M Jun 3 19:49 perf
内核配置支持perf工具
perf 的功能依赖于内核编译选项。大多数主流发行版的内核已经默认开启了必要的配置。如果遇到某些功能无法使用(例如,缺少硬件事件),可能需要检查内核配置。关键的配置项包括:
cpp
CONFIG_PERF_EVENTS=y: 开启 perf 事件子系统。
CONFIG_HW_PERF_EVENTS=y: 开启硬件性能事件支持。
CONFIG_FTRACE=y: 开启 ftrace 支持,perf 的许多高级功能(如动态追踪)依赖于此。
CONFIG_KPROBES=y 和 CONFIG_UPROBES=y: 分别开启内核和用户空间的动态探针支持。
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y :导出内核符号表,使 perf 能将地址解析为可读的函数名。
内核、内核模块重新编译
内核配置支持 perf 后,内核数据结构(如 task_struct)会新增字段,系统调用号与符号导出也可能变化。内核模块必须与当前内核的 ABI 完全一致,否则加载时可能因接口不匹配导致崩溃,因此需重新编译以适配新布局和符号表。
内核uImage、文件系统rootfs(带内核KO模块)、其他应用分区的内核KO模块都需要重新编译。
如何使用 perf?
前期准备
使用perf工具前,需要内核修改配置支持perf,内核模块重新编译,将perf工具放置在设备内
perf 是一个命令行工具,提供了许多子命令(subcommand)
bash
usage: perf [--version] [--help] [OPTIONS] COMMAND [ARGS]
The most commonly used perf commands are:
annotate Read perf.data (created by perf record) and display annotated code
archive Create archive with object files with build-ids found in perf.data file
bench General framework for benchmark suites
buildid-cache Manage build-id cache.
buildid-list List the buildids in a perf.data file
c2c Shared Data C2C/HITM Analyzer.
config Get and set variables in a configuration file.
data Data file related processing
diff Read perf.data files and display the differential profile
evlist List the event names in a perf.data file
ftrace simple wrapper for kernel's ftrace functionality
inject Filter to augment the events stream with additional information
kallsyms Searches running kernel for symbols
kmem Tool to trace/measure kernel memory properties
kvm Tool to trace/measure kvm guest os
list List all symbolic event types
lock Analyze lock events
mem Profile memory accesses
record Run a command and record its profile into perf.data
report Read perf.data (created by perf record) and display the profile
sched Tool to trace/measure scheduler properties (latencies)
script Read perf.data (created by perf record) and display trace output
stat Run a command and gather performance counter statistics
test Runs sanity tests.
timechart Tool to visualize total system behavior during a workload
top System profiling tool.
version display the version of perf binary
See 'perf help COMMAND' for more information on a specific command.
下面介绍最常用的一些命令和场景。
perf record
perf record 是 Linux 内核性能分析工具 perf 的一个重要子命令,用于收集指定进程或系统的性能数据,包括但不限于 CPU 性能事件、硬件性能计数器、内核动态追踪以及调用栈信息。以下是 perf record 命令的基本结构及一些关键选项的解释:
bash
perf record [options] [command]
常用选项包括:
-e, --event=EVENT: 指定要监控的特定性能事件,例如 CPU 时钟周期、指令计数器、缓存未命中的次数等。例如 perf record -e cycles my_program 会记录 my_program 运行过程中的 CPU 周期数。
-p, --pid=PID: 跟踪指定进程 ID 的性能数据,例如 perf record -p 12345 会监控进程ID为12345的进程。
-g, --call-graph=[fp| dwarf| lbr]: 启用调用栈跟踪,收集函数调用层级信息。不同的选项对应不同的调用图生成方式。
-F, --freq=N: 设置采样频率,比如 -F 99 表示每秒采样99次。
-a, --all-cpus: 监控所有CPU核心的性能数据。
sleep N: 结合命令一起使用时,会让 perf record 在执行指定命令后等待 N 秒再停止记录。
perf record 执行后会在当前工作目录下生成一个名为 perf.data 的二进制文件,其中包含了所有收集到的性能数据。
当您完成数据收集后,可以使用 perf report 命令来分析和展示这些数据。
perf report
perf report用于解析和展示由 perf record 命令收集的性能数据。这个命令读取 .data 格式的二进制性能数据文件,并生成易于理解和可视化的性能报告,帮助开发者和系统管理员识别程序或系统中消耗资源最多的部分,如CPU、内存、缓存或其他硬件相关的性能事件。
bash
perf report [-i perf.data] [options]
-i, --input=FILE:指定要读取的 perf 数据文件,默认通常是 perf.data。
几个关键选项和特性包括:
折叠模式(Collapse Mode): 显示函数层级关系和调用栈汇总,便于分析函数间的调用开销。
树状视图(Tree View): 展示函数之间的调用关系,直观地看到哪个函数调用了哪些其他函数以及每个函数所消耗的资源比例。
百分比模式(Percentages): 报告按照占用资源(如CPU时间)的百分比排列各项,帮助快速定位热点区域。
限制显示范围(Filtering Options): 可以通过模块名、函数名、地址范围等过滤条件来筛选需要关注的部分。
符号解析(Symbol Resolution): 自动或手动解决函数符号,以便准确知道哪些具体代码段消耗了较多资源。
调用图表(Call Graphs): 如果原始数据包含调用图信息,则报告中可以展示详细的调用层级和上下文切换信息。
perf report 输出通常包含以下几个部分:
概述:总体的统计数据,包括样本总数、各事件发生的总次数等。
函数列表:按样本数量排序的函数列表,展示了各个函数占整体执行时间的比例。
调用图:如果启用了 -g 参数,会展示函数调用关系图,这对于识别性能瓶颈和热点函数非常有用。
通过这种方式,开发者可以根据 perf record 和 perf report 的输出来优化代码性能,定位耗时操作,进而进行针对性的性能调优。
生成火焰图
火焰图(Flame Graph)是一种高性能分析工具,由Brendan Gregg创造,主要用于可视化程序执行时的CPU调用栈,展示程序在不同函数层级上的时间消耗情况。这种图形化的表示方法因其形状酷似燃烧的火焰而得名。
火焰图的特点如下:
- 可视化堆栈跟踪:火焰图将多个堆栈跟踪(stack traces)合并在一起,形成一个层次分明的图表。每个函数调用形成一个矩形,宽度表示函数在CPU上执行的时间占比,高度表示函数调用的层级。
- 直观揭示性能瓶颈:通过观察火焰图,可以直观地看到哪些函数在执行过程中占据了大量的CPU时间,从而快速定位潜在的性能瓶颈。
- 排序方式:x轴通常按照函数名称字母顺序排列,而不是时间顺序。这样做的好处是可以很容易地对比相似函数在整个程序中的开销。
- 颜色编码:图中的每个矩形(或称"火焰条")通常使用随机颜色,目的是在视觉上更容易区分相邻的函数调用。有些变种火焰图可能会采用不同的颜色方案来区分不同的代码区域或模块。
- 分析数据来源:火焰图的数据来源于性能分析工具(如Linux中的perf工具)收集的CPU采样数据,或者是通过其他性能分析手段得到的调用栈信息。
- 应用领域:火焰图广泛应用于性能优化、故障排查、代码重构等领域,尤其适合大型复杂软件系统,有助于工程师们快速理解和改进程序性能。
下载火焰图代码
git clone https://github.com/brendangregg/FlameGraph.git
使用perf采集一组数据
例如希望分析的进程的PID是1847
bash
perf record -F 100 -p 1847 -g -- sleep 30
这条命令是用来使用Linux的性能分析工具perf进行记录和分析特定进程的性能数据的。命令的组成部分及其功能解释如下:
perf record: perf工具的子命令,用于执行性能数据记录。它会在指定条件下收集有关系统或进程的性能数据,并将其保存到一个数据文件中(默认为perf.data)。
-F 100: 设置采样频率为每秒100次。这意味着每隔大约10毫秒就会进行一次采样,以此频率收集性能数据。
-p 1847: 指定要监控的进程ID(PID)为1847。perf将会收集这个进程的性能数据。
-g: 启用调用栈收集(call graph)。这意味着在每次采样时,perf不仅记录当前执行的指令地址,还会记录当时的函数调用栈,这对于分析函数间的调用关系和性能瓶颈非常有用。
--: 表示命令行选项的结束,之后的参数会被当作非选项参数传递给命令。在这种情况下,后面跟的是sleep 30命令。
sleep 30: 这是一个简单的命令,让它执行30秒。在此期间,perf会记录PID为1847的进程的性能数据。sleep命令的作用是为了确保有足够的采样数据被收集,同时不影响被监控进程的正常运行。
所以,整个命令的目的是在接下来的30秒内,以每秒100次的频率,对PID为1847的gnome-shell进程进行详细的性能数据采样(包括调用栈信息),并将这些数据存储起来供后续分析使用。
生成程序性能分析火焰图的一组流程
bash
# 生成折叠后的调用栈,该命令从名为 perf.data 的 perf 数据文件中提取详细的性能事件样本信息,并以可读的文本格式输出。
# 这一步生成的 perf.unfold 文件包含了未折叠的调用栈信息,即每次采样的函数调用链。
# perf.unfold 是上一步生成的包含详细调用栈信息的文件。
perf script -i perf.data > perf.unfold
# 生成火焰图
# ./stackcollapse-perf.pl 是 FlameGraph 工具集中的一部分,用于处理 perf 生成的未折叠调用栈数据。
# 经过
stackcollapse-perf.pl 处理后的文件内容是对原始调用栈进行了折叠统计,
# 即将同一调用路径下的事件次数累加,形成一种更紧凑的格式,便于生成火焰图。
./stackcollapse-perf.pl perf.unfold > perf.folded
# 最后生成 svg 图
# ./flamegraph.pl 是 FlameGraph 工具集中用于生成火焰图的脚本
# 将火焰图生成的输出重定向到了名为 perf.svg 的SVG矢量图形文件中。
./flamegraph.pl perf.folded > perf.svg
SVG是一种可缩放的矢量图形格式,生成的火焰图可以直接在浏览器中查看,形象地展示了各个函数在CPU执行过程中的调用关系及其占用时间比例,有助于定位性能瓶颈。