Perf 全名是 Performance Event,在 Linux 2.6.31 以后内建的系统性能分析工具,由内核社区维护和发展,不仅可以用于应用程序的性能统计分析,也可以应用于内核代码的性能统计和分析。
这里简单介绍。
简介
Perf 是一款随 Linux 内核代码一同发布和维护的性能诊断工具,由内核社区维护和发展,不仅可以用于应用程序的性能统计分析,也可以应用于内核代码的性能统计和分析。
性能调优工具如 Perf、Oprofile 等的基本原理都是对被监测对象进行采样,最简单的情形是根据 tick
中断进行采样,即在 tick
中断内触发采样点,在采样点里判断程序当时的上下文。
假如一个程序 90% 的时间都花费在函数 foo()
上,只要采样足够多,那么通常来说 90% 的采样点都应该落在函数 foo()
的上下文中。
相对 OProfile
和 GProf
来说,perf
的优势在于和内核紧密结合。
安装
首先确认内核是否支持。
$ cat "/boot/config-`uname -r`" | grep "PERF_EVENTS"
CONFIG_PERF_EVENTS_INTEL_UNCORE=y
CONFIG_HAVE_PERF_EVENTS=y
CONFIG_PERF_EVENTS=y
CONFIG_HAVE_PERF_EVENTS_NMI=y
可以通过发行版本的包管理工具进行安装。
----- CentOS
# yum install perf
----- Debain
# apt-get install linux-perf
或者在内核源码 tools/perf
目录下通过 make && make install
进行编译安装,安装时不需要 root
,将安装在 home
目录下。
常用命令
相关的 perf
命令通过 perf <subcmd>
执行,大概有二十多个子命令,可通过 pref help [subcmd]
查看,其中最常用的有 list
、stat
、top
、record
、report
等。
帮助文档也可以通过 man perf
或者 man perf-top
类似命令查看。
list
通过 list
子命令可以查看当前机器支持的事件,默认会打印所有支持事件,可以通过添加子集过滤所需要的事件,如 hw
、sw
、cache
、tracepoint
、pmu
、event_glob
等,更多通过 perf list --help
命令查看。
所有的 perf
源可以统称为 perf_events
,不同的系统支持的功能不同,大致可以分为如下三类:
Hardware Event
由 Performance Monitoring Unit, PMU 硬件产生的事件,比如 CacheMiss、BranchMiss 等,当需要了解程序对硬件特性的使用情况时,便需要对这些事件进行采样;Software Event
是内核软件产生的事件,与硬件无关,比如 ContextSwitches、PageFaults 等;Tracepoint Event
由内核中静态Tracepoint
所触发的事件,基于ftrace
用来判断程序运行期间内核的行为细节,比如slab
分配器的分配次数等。
注意,很多情况下虚机上是无法支持的。
如下是常用命令。
----- 查看与调度相关的 Tracepoint 事件
# perf list sched
# perf list 'sched:*'
----- 也可以简单过滤
$ perf list | grep stalled
stalled-cycles-frontend OR idle-cycles-frontend [Hardware event]
stalled-cycles-frontend OR cpu/stalled-cycles-frontend/ [Kernel PMU event]
----- 通过 debugfs 查看也可以
$ ls /sys/devices/cpu/events/
branch-instructions bus-cycles cache-references instructions mem-stores
branch-misses cache-misses cpu-cycles mem-loads stalled-cycles-frontend
$ cat /sys/bus/event_source/devices/cpu/events/stalled-cycles-frontend
event=0x0e,umask=0x01,inv,cmask=0x01
stat/record
可以简单查看某个程序执行的开销,或者先将采样数据保存下来再事后分析。
----- 某个进程的指标采集,可以通过-p <PID>指定正在运行的进程
# perf stat sleep 5
Performance counter stats for 'sleep 5':
0.36 msec task-clock # 0.000 CPUs utilized
1 context-switches # 0.003 M/sec
0 cpu-migrations # 0.000 K/sec
68 page-faults # 0.188 M/sec
<not supported> cycles
<not supported> instructions
<not supported> branches
<not supported> branch-misses
5.000664745 seconds time elapsed
0.000630000 seconds user
0.000000000 seconds sys
各个统计信息如下。
task-clock(msec)
占用时钟周期,该值高说明程序的多数时间花费在 CPU 计算上而非 IO 上。context-switches
发生了多少次上下文切换,应该尽量避免频繁的进程切换。cpu-migrations
发生了多少次 CPU 迁移。
或者通过 record
记录下采集事件,相关的参数有:
-F
每秒采集事件数-g
记录程序的调用栈-p/t
指定运行的进程或者线程ID-e
只记录指定的事件列表-a
采集所有 CPU 信息-f
强制覆盖perf.data
文件-o
默认输出为perf.data
文件,可以重新指定
例如。
----- 记录程序执行
perf record -F 99 -ag -- sleep 60
perf record -F 99 -ag -p PID
----- 记录指定的事件
perf record -e block:block_rq_issue -ag
然后可以通过 report
命令查看。
top
实时查看当前系统的统计信息。
----- 实时显示当前系统TopN进程、函数,通过-e指定关注的指标,默认是CPU
# perf top -e cache-misses
----- 显示消耗最多CPU周期的函数
# perf top -e cycles:k
----- 显示分配高速缓存最多的函数:
# perf top -e kmem:kmem_cache_alloc
常用参数有:
-e
指定性能事件。-a/C
显示在所有或者指定 CPU 上的性能统计信息。-p/-t
指定进程或者线程 ID 。-K/U
隐藏内核或者用户空间的统计信息。
bench
包含一些基础的压册工具,可以通过 perf bench
查看所有支持的分类,如下是常用命令。
----- 内存压测
# perf bench mem memcpy
# perf bench mem memset
其它
----- 查看两个的性能对比
# perf diff new old
----- 整理的统计指标,可能会出现not supported
# perf stat program args
# perf stat dd if=/dev/zero of=test.iso bs=10M count=1
输出指标:
task-clock 运行时占用CPU的时钟周期,可以判定CPU/IO Bound;
context-switches 上下文切换次数,包括了进程间切换以及内核态和用户态的切换;
cpu-migrations 运行期间发生CPU迁移次数,也即从一个CPU运行切换到另外的CPU;
page-faults 程序发生了缺页异常的次数。
----- 通过record+report统计程序性能TOP指标
# perf record -e cpu-clock -ag program args
# perf stat dd if=/dev/zero of=test.iso bs=10M count=1
参数:
-a 统计所有的CPU;
-g 记录函数的调用关系,也即调用栈;
-e cpu-clock 监控的指标为CPU调用周期;
# perf report -i perf.data
火焰图
通过火焰图可以有效观察资源消耗在那,不重要的会自然淡化甚至消失,而真正重要的代码路径则会突显,从而提供恰到好处的信息,不多也不少。
大部分的文章基本都是源自 Flame Graphs 中的介绍,可以从 Github FlameGraph 下载相关的脚本。
然后通过如下命令生成火焰图。
----- 记录Perf事件,其中-F指定频率,-p 指定进程ID
# perf record -F 99 -ag -p <PID>
----- 对采集数据进行解析,这样可以直接复制到本地机器处理
# perf script -i perf.data > perf.unfold
----- 将符号进行折叠
# FlameGraph/stackcollapse-perf.pl perf.unfold > perf.folded
----- 最后生成火焰图
# FlameGraph/flamegraph.pl perf.folded > perf.svg
如果出现 Unknown
问题,可以在 record
时增加 --call-graph dwarf
参数。
其它
源码解析
如上所述,源码在内核的 tools/perf
目录下,其中入口在 main()@perf.c
,会根据子命令调用不同的函数,如 cmd_list()@builtin-list.c
、cmd_stat()@builtin-stat.c
等。
例如 perf list
对应 cmd_list()
函数,该函数依赖 debugfs
,详见 tracing/events
目录下内容。
参考
- Perf Wiki Linux 内核官方的文档。
- Perf Examples 性能优化大神 Brendan Gregg’s 示例。
- Perf Tools 同样是 Brendan Greeg’s 开发的一些常用 Perf 工具。