perf简介
perf命令原名为Linux性能计数器(Performanc Counters for Linux,PCL),已经演化成为一整套剖析和跟踪的工具,现名为LInux性能事件(Linux Performance Events,LPE)。每个工具分别作为一个子命令,例如,perf stat执行stat命令,提供基于CPC的统计信息。perf可以跟踪到进程内部具体函数耗时情况,并可以指定内核函数进行统计。
perf安装
# yum安装
yum install -y perf
# 编译安装,perf在内核源码的tools/perf中,下载与内核版本一致的内核源码即可
uname -a
yum install -y gcc make bison flex elfutils libelf-dev libdw-dev libaudit-dev python-dev binutils-dev
wget https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.10.104.tar.xz
tar -xvf linux-3.10.104.tar.xz
cd linux-3.10.104/tools/perf/
make
perf子命令
命令 | 描述 |
annotate | 读取perf.data(由perf record创建)并显示注释过的代码 |
diff | 读取两个perf.data文件并显示两份剖析信息之间的差异 |
evlist | 列出一个perf.data文件里的事件名称 |
inject | 过滤以加强事件流,在其中加入额外的信息 |
kmem | 跟踪/测量内核内存(slab)属性的工具 |
kvm | 跟踪/测量kvm客户机操作系统的工具 |
list | 列出所有的符号事件类型,其中性能事件的包括: |
lock | 分析锁事件 |
probe | 定义新的动态跟踪点 |
record | 运行一个命令,收集采样信息,并把剖析信息记录在perf.data中 |
report | 读取perf.data(由perf record创建)并显示剖析信息 |
sched | 跟踪/测量调度器属性(延时)的工具 |
script | 读取perf.data(由perf record创建)并显示跟踪输出 |
stat | 运行一个命令并收集性能计数器统计信息,用于分析指定程序的性能概况。 task-clock:任务真正占用的处理器时间,单位为ms。 |
timechart | 可视化某一个负载期间系统总体性能的工具 |
top | 系统剖析工具。对于一个指定的性能事件(默认是CPU周期),显示消耗最多的函数或指令。perf top类似linux的top命令,可以排列展示当机的函数和指令消耗情况。perf top主要用于实时分析各个函数在某个性能事件上的热度,能够快速的定位热点函数,包括应用程序函数、模块函数与内核函数,甚至能够定位到热点指令。默认的性能事件为cpu cycles。 |
perf使用
系统剖析
perf可以用来剖析cpu调用路径,对cpu时间如何消耗在内核和用户空间进行概括总结。这项工作由record命令完成,该命令以一定间隔进行取样,并导出到一个perf.data文件,然后使用report命令查看文件。
在下面的例子里,所有的cpu(-a)以997Hz的频率(-F 997)对调用栈(-g)取样10s(sleep 10)。选项--stdio用来打印所有的输出,而非采用默认的交互模式操作。
完整的的输出长达多页,按照取样计数逆序排列。这些取样计数以百分数输出,展示了cpu时间的运算。这个例子中,72.98%的时间花在空闲线程,9.43%的时间花在dd进程。而在这9.43%时间中,87.5%的时间花在上面所示的栈中,函数为ext4_file_write()。
这些内核和进程符号只在它们的调试信息文件存在的情况下可用,否则只显示十六进制地址。
perf通过给cpu周期计数器设置一个溢出中断实现。由于现在处理器上的周期频率不断变化,因此使用了一个“按比例缩放的”常数计数器。
进程剖析
除了剖析系统里的所有cpu,我们也可以对单个进程进程剖析。下面的命令执行了command并创建文件perf.data:
# perf record -g command
和之前一样,perf需要调试信息文件,这样在查看报告时可以进行符号转译。
调度器延时
sched命令记录并报告调度器统计信息。例如:
上面显示了跟踪时期平均和最大的调度器延时。
调度器事件较为频繁,因此此类跟踪会导致cpu和存储的开销。本例中的perf.data文件即为跟踪10s的产物,大小达到了1.7GB。输出中的INFO行表示有些事件被丢弃了。这凸显了DTrace在内核中过滤和聚合模型的优势:它可以在跟踪时汇总数据并只把汇总结果传递给用户空间,把开销最小化。
stat
stat命令基于cpc为cpu周期行为提供了一个概要总结。下面的例子里它启动了gzip命令:
统计信息包括了周期的指令计数,以及IPC(CPI的倒数)。和前面描述的一样,这是一个对判断周期类型以及其中有多少停滞周期非常有用的概要指标。
下面列出了其他可以检查的计数器:
注意其中的”Hardware event“和”Hardware cache event“。这些是否可用取决于处理器的架构,并在处理器手册中有记录(例如,Intel软件开发者手册)。
这些事件可以使用选项-e指定。例如(来自Intel Xeon):
除了指令和周期之外,这个例子还测量了以下几个方面:
L1-dcache-load-misses:一级数据缓存负载未命中。这大概可以看出应用程序施加的内存负载,其中有一部分负载从一级缓存返回。可以与其他L1事件计数器进行对比以确定缓存命中率。
LLC-load-misses:末级缓存负载未命中。在末级缓存之后,就会直接存取主存,因此这实际测量了主存负载。从它与L1-dcache-load-missed之间的区别,可以大概看出(还需要其他计数器以确保完整性),除一级CPU缓存之外其他缓存的有效性。
dTLB-load-misses:数据转译后备缓冲器未命中。它展示了MMU为负载缓存页面映射的有效性,可以用来测量内存负载的大小(工作集)。
还有许多其他计数器可待查验。perf支持描述名(与这个例子里使用的一样)和十六进制值。后者可以在查看处理器手册中发现神秘计数器时派上用场,此类计数器一般没有描述名。
软件跟踪
perf record -e可以与各种软件性能测量点配合,用来跟踪内核调度器的活动。这些测量点包括了软件事件和跟踪点事件(静态探测器),由perf list列出。例如:
下面的例子使用上下文切换软件事件,以在应用程序离开cpu时进行跟踪,并收集了10s的调用栈:
这份截取的输出显示了两个应用程序,perl和tar,以及它们上下文切换时的调用栈。看看这个栈就能发现tar程序在文件系统(ext4)读的时候睡眠。而perl程序由于执行密集计算,进行了非自愿性上下文切换,尽管仅从这份输出并不能得出此结论。
使用sched跟踪点事件可以发现更多的信息。还可以使用动态跟踪点(动态跟踪)直接跟踪内核调度器函数,与静态探测器结合使用可以提供与前面DTrace类似的数据,虽然这可能需要更多的事后处理工作以得出预期结果。
第9章包含了另一个使用perf进行静态跟踪的例子。第10章中有一个使用perf动态跟踪tcp_sendmsg()内核函数的例子。
perf 静态跟踪
Linux上的工具perf提供了块跟踪点,可以用这些跟踪点来跟踪一些基本信息。这些跟踪点如下:
# perf list | grep block:
block:block_bio_backmerge [Tracepoint event]
block:block_bio_bounce [Tracepoint event]
block:block_bio_complete [Tracepoint event]
block:block_bio_frontmerge [Tracepoint event]
block:block_bio_queue [Tracepoint event]
block:block_getrq [Tracepoint event]
block:block_plug [Tracepoint event]
block:block_remap [Tracepoint event]
block:block_rq_abort [Tracepoint event]
block:block_rq_complete [Tracepoint event]
block:block_rq_insert [Tracepoint event]
block:block_rq_issue [Tracepoint event]
block:block_rq_remap [Tracepoint event]
block:block_rq_requeue [Tracepoint event]
block:block_sleeprq [Tracepoint event]
block:block_split [Tracepoint event]
block:block_unplug_io [Tracepoint event]
block:block_unplug_timer [Tracepoint event]
举个例子,为检查调用栈,用调用图来跟踪块设备问题。命令sleep 10用来作为跟踪的持续时间。
输出长达数页,展示了产生块设备I/O的不同代码路径。这里给出的部分是ext4的目录读取。
perf 动态跟踪
使用perf创建内核tcp_sendmsg()函数的动态跟踪点,然后跟踪它五秒并显示调用图(栈跟踪):
# perf probe --add='tcp_sendmsg'
Added new event:
probe:tcp_sendmsg (on tcp_sendmsg)
You can now use it in all perf tools, such as:
perf record -e probe:tcp_sendmsg -aR sleep 1
# perf record -e probe:tcp_sendmsg -aR sleep 30
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.429 MB perf.data (89 samples) ]
# perf report --stdio
# To display the perf.data header info, please use --header/--header-only options.
#
# Samples: 89 of event 'probe:tcp_sendmsg'
# Event count (approx.): 89
#
# Overhead Command Shared Object Symbol
# ........ ......... ................. ...............
#
97.75% nginx [kernel.kallsyms] [k] tcp_sendmsg
1.12% AliYunDun [kernel.kallsyms] [k] tcp_sendmsg
1.12% sshd [kernel.kallsyms] [k] tcp_sendmsg
#
# (For a higher level overview, try: perf report --sort comm,dso)
#
输出显示nginx的栈跟踪,它导致内核调用tcp_sendmsg()并由TCP连接发送数据。还有一些预定义的网络跟踪点事件,如下所示:
# perf list
[...]
skb:consume_skb [Tracepoint event]
skb:kfree_skb [Tracepoint event]
skb:skb_copy_datagram_iovec [Tracepoint event]
net:net_dev_queue [Tracepoint event]
net:net_dev_xmit [Tracepoint event]
net:netif_receive_skb [Tracepoint event]
net:netif_rx [Tracepoint event]
skb跟踪点用于套接字缓存事件,而net用于网络设备。这些也有助于网络调查。
perf火焰图
perf的数据可以进一步被加工为火焰图,这需要其他工具的支持,flameGap这个开源项目(https://github.com/brendangregg/FlameGraph.git)很不错,里面有很多的perl脚本。使用perf report -tui或者-stdio输出的文本不够直观的话,使用火焰图可以很直观的表现出哪些代码是瓶颈所在。
压测
$pgbench -M prepared -n -r -P 1 -c 32 -j 32 -T 100
收集统计信息
#perf record -a -g -v sleep 30
生成火焰图
# git clone https://github.com/brendangregg/FlameGraph # or download it from github
# mv perf.data FlameGraph/
# cd FlameGraph
# perf script | ./stackcollapse-perf.pl > out.perf-folded
# cat out.perf-folded | ./flamegraph.pl > perf-kernel.svg
或者通过如下命令:
perf record -g ls
perf script -i perf.data &> perf.unfold
./stackcollapse-perf.pl perf.unfold &>perf.folded
./stackcollapse-perf.pl perf.unfold >perf.svg
就可以得到截图中的perf.svg矢量图片了。
绘制perf热力图
压测
$pgbench -M prepared -n -r -P 1 -c 32 -j 32 -T 100
收集统计信息
#perf record -a -g -v sleep 30
生成热力图
# git clone https://github.com/brendangregg/HeatMap # or download it from github
# mv perf.data HeatMap/
# cd HeatMap
# perf script | awk '{ gsub(/:/, "") } $5 ~ /issue/ { ts[$6, $10] = $4 }
$5 ~ /complete/ { if (l = ts[$6, $9]) { printf "%.f %.f\n", $4 * 1000000,
($4 - l) * 1000000; ts[$6, $10] = 0 } }' > out.lat_us
# ./trace2heatmap.pl --unitstime=us --unitslat=us --maxlat=50000 out.lat_us > out.svg
perf常用命令
# 输出当前内核perf支持的预置events
perf list
# 跟踪进程内部函数级cpu使用情况
perf top -p pid -e cpu-clock
perf top -agv/-p
perf top -g -p pid
perf record -ag sleep 30;perf report
# 查看系统IO的请求,查找是什么原因导致的IO异常
perf record -e block:block_rq_issue -ag
perf report
# 解读前面收集到的perf.data
perf report -p pid
perf record -agv
perf record -e xxx -p pid
perf report -i perf.data
# 解读前面收集到的perf.data,辅以汇编指令显示。
perf annotate
# 生成火焰图
perf-script -Read perf.data
perf list | awk -F: '/Tracepoint event/ { lib[$1]++ } END {for (l in lib) { printf " %-16s %d\n", l, lib[l] } }' | sort | column
Wohh exactly what I was looking for, thanks for putting up.