生成CPU时间片火焰图
# 1. 安装 perf
sudo apt-get install linux-tools-common linux-tools-generic linux-tools-`uname -r`
# 2. 下载火焰图工具
## 建议切换到 commit d9fcc272b6a08c3e3e5b7919040f0ab5f8952d65 或之后
## 此工程应下载到pc机等非嵌入式环境(开发板)上
https://github.com/brendangregg/FlameGraph
# 3. 查看系统限制
sysctl kernel.kptr_restrict
# 4. 设置系统限制
sudo sysctl -w kernel.kptr_restrict=0
sudo sysctl -w kernel.perf_event_paranoid=-1
# 5. 采集信息
sudo su
## 如果你需要用非系统目录下的动态库,那一定执行 export LD_LIBRARY_PATH=xxx
## 应测试使用 fp, dwarf, lbr 三种栈调用解析器,找到解析结果正确的那一种。具体原理见下文“解析调用栈”
perf record -e cpu-cycles --call-graph=<fp/dwarf/lbr> <xxx / -p pid>
# 6. 生成中间文件
perf script -i perf.data &> perf.unfold
# 7. 使用火焰图工具工程中的脚本
## 在进行开发板or嵌入式平台测试时,此步骤应回到pc机上进行
export PATH=<FlameGraph工程目录>:$PATH
stackcollapse-perf.pl perf.unfold &> perf.folded
flamegraph.pl perf.folded > perf.svg
# 8. 使用浏览器打开即可
# 6 -> 7 一步完成的命令
perf script | stackcollapse-perf.pl | flamegraph.pl > out.svg
生成CPU时间片函数分布表
perf report -i perf.data
perf report -g --sort cpu -i perf.data
调研程序性能瓶颈 (包括Cache Miss)
当初次调研一个软件的性能时,应先确定程序性能状态。
即,CPU Bound、IO bound的情况。
perf stat -d -B -e cache-references,cache-misses,cycles:uk,instructions,branches,faults,migrations,branch-misses,page-faults <xxx>
以上统计信息的解释
- Task-clock-msecs:CPU 利用率,该值高,说明程序的多数时间花费在 CPU 计算上而非 IO。
- Context-switches:进程切换次数,记录了程序运行过程中发生了多少次进程切换,频繁的进程切换是应该避免的。
- CPU-migrations:表示进程 t1 运行过程中发生了多少次 CPU 迁移,即被调度器从一个 CPU 转移到另外一个 CPU 上运行。
- Cycles:处理器时钟,一条机器指令可能需要多个 cycles,
- Instructions: 机器指令数目。
- IPC:是 Instructions/Cycles 的比值,该值越大越好,说明程序充分利用了处理器的特性。
- Cache-references: cache 命中的次数
- Cache-misses: cache 失效的次数
- branch-misses: 分支预测失败的次数和耗时
Perf指导
被测软件的编译参数
有些编译优化项会忽略frame pointer,所以编译软件时必须指定 -fno-omit-frame-pointer ,才能跟踪完整的stack trace。
在编译软件时打开符号表的支持(gcc -g),开启annotate的支持(gcc -ggdb)。
这些选项的具体作用见下一小节”解析调用栈“
# 在CMakeLists.txt中加入此句话
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -g -ggdb ")
解析调用栈方式、
加入-fno-omit-frame-pointer -g -ggdb都是为了增加可被解析的栈痕迹。当加入了对应编译选项后,perf record便可以通过 --call-graph=<fp/dwarf/lbr>选项确定追踪栈信息的方式。具体对应关系如下
具体该使用哪个选项,应以实际测试结果为准。
fp 就是 Frame Pointer,即 x86 中的 EBP 寄存器,fp 指向当前栈帧栈底地址,此地址保存着上一栈帧的 EBP 值,具体可参考此文章的介绍,根据 fp 就可以逐级回溯调用栈。然而这一特性是会被优化掉的,而且这还是 GCC 的默认行为,在不手动指定 -fno-omit-frame-pointer 时默认都会进行此优化,此时 EBP 被当作一般的通用寄存器使用,以此为依据进行栈回溯显然是错误的。不过尝试指定 -fno-omit-frame-pointer 后依然没法获取到正确的调用栈,根据 GCC 手册的说明,指定了此选项后也并不保证所有函数调用都会使用 fp。从实际使用上来看,Orin上的perf即很好支持 fp 选项。
dwarf 是一种调试文件格式,GCC 编译时附加的 -g 参数生成的就是 dwarf 格式的调试信息,其中包括了栈回溯所需的全部信息,使用 libunwind 即可展开这些信息。dwarf 的进一步介绍可参考 “关于DWARF”,值得一提的是,GDB 进行栈回溯时使用的正是 dwarf 调试信息。
lbr 即 Last Branch Records,是较新的 Intel CPU 中提供的一组硬件寄存器,其作用是记录之前若干次分支跳转的地址,主要目的就是用来支持 perf 这类性能分析工具,其详细说明可参考 “An introduction to last branch records” & “Advanced usage of last branch records”。此方法是性能与准确性最高的手段,然而它存在一个很大的局限性,由于硬件 Ring Buffer 寄存器的大小是有限的,lbr 能记录的栈深度也是有限的,具体值取决于特定 CPU 实现,一般就是 32 层,若超过此限制会得到错误的调用栈。
触发记录的方式
perf记录信息需要触发。
- 周期性检测 // TODO
- 事件触发,见下文。
Perf能够观察的事件类型包括:
PERF_TYPE_HARDWARE :内核能够提供的“通用的”硬件事件PERF_TYPE_SOFTWARE:内核提供的软件定义的事件(即使没有硬件的支持)PERF_TYPE_TRACEPOINT:由Ftrace 架构提供,支持采样功能PERF_TYPE_HW_CACHE:这些是硬件事件,但是由于其复杂性,因而需要特殊的编码
PERF_TYPE_RAW:只有某些CPU支持的特定事件
其中,PERF_TYPE_HARDWARE类事件包括:
cpu-cycles:某段时间内的CPU cycle数;
instructions:某段时间内的CPU所执行的指令数;
cache misses:cache miss次数;
branch misses:分支预测错误次数;
等等
而PERF_TYPE_SOFTWARE包含的事件包括:
cpu-clock:某段时间内的cpu时钟数;
page faults:页错误次数;
context switches:上下文交换次数;
等等。
其中CPU cycles事件和cpu-clock事件因比较常用,我们说一下它们的区别:
cpu-clock可以用来表示程序执行经过的真实时间,而无论CPU处于什么状态(Pn(n非0)或者是C状态);
关于CPU的状态见: vstinner.github.io/intel-cpus.…
而CPU cycles则用来表示执行程序指令花费的时钟周期数,如果CPU处于Pn(n非0)或者是C状态,则cycles的产生速度会减慢。
也即,如果你想查看哪些代码消耗的真实时间多,则可以使用cpu-clock事件;而如果你想查看哪些代码消耗的时钟周期多,则可以使用CPU cycles事件。
获取受支持事件的列表:perf list
Perf report 阅读
perf report
# Events: 1K cycles
#
# Overhead Command Shared Object Symbol
# ........ ............... .............................. .....................................
#
28.15% firefox-bin libxul.so [.] 0xd10b45
4.45% swapper [kernel.kallsyms] [k] mwait_idle_with_hints
4.26% swapper [kernel.kallsyms] [k] read_hpet
2.13% firefox-bin firefox-bin [.] 0x1e3d
1.40% unity-panel-ser libglib-2.0.so.0.2800.6 [.] 0x886f1
[...]
“开销”列表示在相应函数中收集的总样本的百分比。第二列报告了收集样本的过程。在每线程/每进程模式下,这始终是受监视命令的名称。但在 CPU 范围模式下,命令可能会有所不同。第三列显示样本来源的 ELF 图像的名称。如果程序是动态链接的,则可能会显示共享库的名称。当样本来自内核时,则使用伪 ELF 映像名称 [kernel.kallsyms]。第四列指示采样的特权级别,即程序在中断时正在运行时:
- [.]:用户级别
- [k]:内核级别
- [g]:客户机内核级别(虚拟化)
- [u]: 来宾 os 用户空间
- [H]:虚拟机管理程序
查看系统perf支持
$gunzip -c /proc/config.gz | grep PERF
$cat /boot/config-`uname -r` |grep CONFIG_KALLSYMS // 查看系统是否支持内核代码的符号表
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
CONFIG_KALLSYMS_EXTRA_PASS=y
其他内核编译选项
# 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_EVENTS=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
# kernel dynamic tracepoint variables: CONFIG_DEBUG_INFO=y
检查系统性能
$ perf bench all
$ perf bench mem all
源码编译perf
确认系统支持perf特性后,即可通过源码进行编译。命令如下:
cd /usr/src
apt install -y libelf-dev systemtap-sdt-dev libaudit-dev libssl-dev libslang2-dev libperl-dev liblzma-dev libunwind-dev libdw-dev binutils-dev libiberty-dev asciidoc xmlto
apt install linux-source
tar -jxf linux-source-x.x.x.tar.bz2
apt install flex
cd linux-source-x.x.x/tools/perf
make
# 安装缺失的包后再make
ln -s /usr/src/linux-source-x.x.x/tools/perf/perf /usr/local/bin/perf
FAQ
A:[xxx] with build id 6424687e552b0911efcbb4a096bc8d70d3c9af04 not found, continuing without symbols
Q: 分为两种情况。1. xxx为内核部分(也可以report查看是否为k级别),说明内核编译时缺少这部分符号表 2. xxx为软件执行程序(如no symbols found in /sbin/iscsid),说明应该安装此软件的debuginfo版本
A:perf.data 内缺失所有符号等信息
Q:编译perf时注意on所有必须选项(安装必须的dev版本依赖)
Auto-detecting system features:
... dwarf: [ on ]
... dwarf_getlocations: [ on ]
... glibc: [ on ]
... gtk2: [ OFF ]
... libaudit: [ on ]
... libbfd: [ on ]
... libcap: [ OFF ]
... libelf: [ on ]
... libnuma: [ OFF ]
... numa_num_possible_cpus: [ OFF ]
... libperl: [ on ]
... libpython: [ OFF ]
... libcrypto: [ on ]
... libunwind: [ on ]
... libdw-dwarf-unwind: [ on ]
... zlib: [ on ]
... lzma: [ on ]
... get_cpuid: [ OFF ]
... bpf: [ on ]
... libaio: [ on ]
... libzstd: [ OFF ]
... disassembler-four-args: [ on ]
ref
Linux 性能诊断 perf使用指南 - Digoal.Zhou’s Blog (billtian.github.io)
Linux perf Examples (brendangregg.com)
系统讲解linux的性能热点分析手段 Performance Analysis and Optimization on Linux (cern.ch)