Linux(09)之perf(1)基础知识总结
Author:Onceday Date:2023年1月31日
漫漫长路,才刚刚开始…
参考文档:
文章目录
1. 概述
这张图是来自Brendan Gregg大佬提供的linux分析工具的应用场景,可以看出几乎包含了系统每个地方应该用什么工具去分析。
这里我们只考虑perf这一种工具,重点在于使用该工具来理解程序在Linux系统上的性能消耗情况。
1.1 Perf背景历史
Perf
全名是Performance Event, 是一个功能强大的性能分析工具,它主要用于测量和分析Linux操作系统中的软件性能。perf
是基于Linux内核的性能监控功能实现的,它可以帮助开发者找到程序的性能瓶颈、优化代码以及提高系统的整体性能。
perf
工具的诞生要追溯到2008年,当时Linux内核社区为了改善内核性能分析的体验,开始开发了一个基于内核的性能监控子系统,即Performance Monitoring Unit(PMU)。随着时间的推移,perf
工具在内核社区的不断贡献和改进下,功能日益完善,逐渐成为Linux平台下必备的性能分析工具。
perf
工具具有广泛的功能作用,包括但不限于:
-
CPU性能监控:
perf
可以收集和报告各种CPU性能事件,如缓存命中率、分支预测错误等,从而帮助开发者了解程序的运行效率。 -
内存性能监控:
perf
可以分析程序的内存访问模式,如内存分配与释放,帮助开发者优化内存使用。 -
系统调用追踪:
perf
可以追踪程序的系统调用,帮助开发者发现潜在的性能问题。 -
热点函数分析:通过采样程序执行过程中的函数调用,
perf
可以找到程序中执行时间最长的函数,为开发者提供优化方向。 -
硬件事件统计:
perf
还可以统计硬件事件,如CPU缓存、分支预测等,以评估程序的硬件利用效率。
perf
工具自2008年诞生以来,经历了多次重要的更新:
-
2.6.31内核:
perf
初始版本发布,支持基本的性能监控功能。 -
2.6.35内核:引入了
perf trace
命令,支持系统调用追踪。 -
3.0内核:支持动态追踪技术,提高了
perf
的灵活性。 -
4.1内核:引入了
perf c2c
命令,支持缓存行级别的分析。
随着Linux内核的不断升级,perf
工具也在不断地扩展和完善其功能,以满足更多性能分析的需求。
虽然perf
是为Linux操作系统设计的,但它也可以在其他类Unix系统上运行,如FreeBSD、macOS等。然而,由于这些平台的内核实现和硬件架构差异,perf
在不同平台上的功能和性能可能会有所差异。因此,在使用perf
进行性能分析时,需要考虑到目标平台的特点和限制。
1.2 PMU(性能监控单元)
PMU(Performance Monitoring Unit,性能监视单元)是现代 CPU 中的一个重要部件,它用于监控和收集与处理器性能相关的硬件事件。PMU 可以帮助开发人员了解程序在特定处理器上的运行情况,从而进行针对性的优化。
PMU 通常由多个硬件性能计数器(Hardware Performance Counter,HPC)组成。这些计数器用于统计特定硬件事件发生的次数,例如:
- CPU 周期(Cycles)
- 指令数(Instructions)
- 缓存命中/未命中(Cache Hits/Misses)
- 分支预测成功/失败(Branch Predictions Correct/Incorrect)
在使用 PMU 进行性能分析时,需要选择要监控的事件。然后,PMU 会将事件发生的次数记录在对应的性能计数器中。通过收集和分析这些数据,可以找出程序的瓶颈。
在 Linux 系统下,Perf 工具可以访问 PMU 并利用其提供的性能数据。Perf 提供了多种命令和选项来配置和分析 PMU 数据。在前面的 Perf 教程中,我们已经讨论了如何使用 Perf 进行性能分析。
需要注意的是,不同处理器的 PMU 可能会有所不同,支持的事件种类和性能计数器数量也可能有差异。不过,Perf 工具已经对大部分流行的处理器进行了抽象,使得在多种处理器上都能使用统一的接口来进行性能分析。
1.3 Perf的原理
perf功能强大:它可以测量CPU性能计数器、跟踪点、kprobes和uprobes(动态跟踪)。它能够进行轻量级剖析。它也包含在Linux内核的tools/perf下,并经常更新和增强。
perf开始时是一个用于在Linux中使用性能计数器子系统的工具,并经过了各种增强以添加跟踪功能。性能计数器是CPU硬件寄存器,对硬件事件进行计数,例如执行的指令、缓存未命中或分支预测错误。它们构成了分析应用程序以跟踪动态控制流和识别热点的基础。Perf为特定于硬件的功能提供了丰富的通用抽象。其中,它提供了每个任务、每个CPU和每个工作负载的计数器,并在这些计数器和源代码事件注释的基础上进行采样。
**跟踪点(tracepoints)**是位于代码中逻辑位置的插桩点,例如系统调用、TCP/IP事件、文件系统操作等。在不使用时,它们的开销可以忽略不计,并且可以通过perf命令启用,以收集包括时间戳和堆栈跟踪在内的信息。Perf还可以使用kprobes和uprobes框架动态创建跟踪点,用于内核和用户空间的动态跟踪。它们的可能性是无限的。
-
kprobes
(Kernel Probes)是一种用于追踪内核代码执行的动态追踪技术。通过在内核中插入kprobes
,开发者可以监控内核函数的调用和返回,收集关键事件的信息,如参数值、返回值等。kprobes
的工作原理是基于内核的断点机制。在插入kprobes
时,内核会在目标地址处插入一个断点指令。当程序执行到该指令时,内核会触发一个异常,并将控制权转交给kprobes
的处理函数。处理函数可以访问当前的CPU寄存器和内存,获取事件信息。执行完处理函数后,程序会继续从目标地址处执行。kprobes
的优势在于,它可以在运行时动态地追踪内核代码,而无需修改或重新编译内核。此外,kprobes
对系统性能的影响较小,因为它只在关键事件发生时触发。 -
uprobes
(User Probes)是一种用于追踪用户空间代码执行的动态追踪技术。与kprobes
类似,uprobes
也可以在运行时插入探针,监控用户程序的函数调用和返回。uprobes
的工作原理与kprobes
相似,也是基于断点机制。不同之处在于,uprobes
插入的断点位于用户空间代码中,因此需要内核与用户空间的协同处理。当用户程序执行到断点时,内核会触发一个异常,将控制权转交给uprobes
的处理函数。处理函数可以访问当前的CPU寄存器和内存,获取事件信息。执行完处理函数后,程序会继续从目标地址处执行。uprobes
的优势在于,它可以在运行时动态地追踪用户空间代码,而无需修改或重新编译程序。类似于kprobes
,uprobes
对系统性能的影响也较小。
Perf有两种工作模式:
- 计数模式(Couting Mode),Counting Mode 将会精确统计一段时间内 CPU 相关硬件计数器数值的变化。为了统计用户感兴趣的事件,Perf Tool 将设置性能控制相关的寄存器。这些寄存器的值将在监控周期结束后被读出。
- 采样模式(Sampling Mode),Sampling Mode 将以定期采样方式获取性能数据。PMU 计数器将为某些特定事件配置溢出周期。当计数器溢出时,相关数据,如 IP、通用寄存器、EFLAG 将会被捕捉到。
1.4 Perf性能事件
Perf事件大概分成以下几个类别:
- 硬件事件 (Hardware Events): 这些事件是由处理器硬件本身产生的。例如:
- instructions: 已执行的指令数
- cycles: 处理器周期数
- cache-references: 缓存引用次数
- cache-misses: 缓存未命中次数
- branch-instructions: 分支指令数
- branch-misses: 错误预测的分支指令数
- 硬件缓存事件 (Hardware Cache Events): 这些事件与处理器的缓存相关,例如:
L1-dcache-loads
: L1 数据缓存加载次数L1-dcache-load-misses
: L1 数据缓存加载未命中次数L1-dcache-stores
: L1 数据缓存存储次数L1-dcache-store-misses
: L1 数据缓存存储未命中次数L1-dcache-prefetches
: L1 数据缓存预取次数L1-dcache-prefetch-misses
: L1 数据缓存预取未命中次数
- 软件事件 (Software Events): 这些事件是由操作系统产生的,例如:
context-switches
: 上下文切换次数cpu-migrations
: CPU 迁移次数page-faults
: 页错误次数minor-faults
: 次要页错误次数major-faults
: 主要页错误次数
- 跟踪点事件 (Tracepoint Events): 这些事件是由内核中的跟踪点生成的,可以通过
perf list
命令获取完整的跟踪点事件列表。跟踪点事件可以用于监控诸如调度器行为、内存管理等内核子系统的性能。 - 调查事件(Probe events): 用户自定义的事件, 动态插入到内核之中。
1.5 Perf性能事件的属性
以下原文请参考perf学习总结 - 知乎 (zhihu.com)。
硬件性能事件由处理器中的PMU提供支持。由于现代处理器的主频非常高,再加上深度流水线机制,从性能事件被触发,到处理器响应 PMI中断,流水线上可能已处理过数百条指令。那么PMI中断采到的指令地址就不再是触发性能事件的那条指令的地址了,而且可能具有非常严重的偏差。为了解决这个问题,Intel处理器通过PEBS机制实现了高精度事件采样。PEBS通过硬件在计数器溢出时将处理器现场直接保存到内存(而不是在响应中断时才保存寄存器现场),从而使得 perf能够采到真正触发性能事件的那条指令的地址,提高了采样精度。在默认条件下,perf不使用PEBS机制。
用户如果想要使用高精度采样,需要在指定性能事件时,在事件名后添加后缀”:p”或”:pp”。Perf在采样精度上定义了4个级别,如下表所示。
- 0: 无精度保证
- 1: 采样指令与触发性能事件的指令之间的偏差为常数(:p)
- 2: 需要尽量保证采样指令与触发性能事件的指令之间的偏差为0(:pp)
- 3: 保证采样指令与触发性能事件的指令之间的偏差必须为0(:ppp)
性能事件的精度级别目前的X86处理器,包括Intel处理器与AMD处理器均仅能实现前 3 个精度级别。
除了精度级别以外,性能事件还具有其它几个属性,均可以通过”event:X”的方式予以指定。
- u: 仅统计用户空间程序触发的性能事件
- k: 仅统计内核触发的性能事件
- h: 仅统计Hypervisor触发的性能事件
- G: 在KVM虚拟机中,仅统计Guest系统触发的性能事件
- H: 仅统计 Host 系统触发的性能事件
- p: 精度级别
2. Perf实际命令
2.1 perf命令
该命令支持的参数如下:
# perf命令的一般形式
perf [--version] [--help] [OPTIONS] COMMAND [ARGS]
# 目前支持的选项参数:
--help, Run perf help command. 运行perf help命令.
--version, 显示perf的版本信息
-vv, 打印库的编译状态。
--exec-path, Display or set exec path. 显示或者设置执行路径
--html-path, Display html documentation path. 显示html文档的路径
--paginate, Set up pager.
--no-pager, Do not set pager.
--debugfs-dir, Set debugfs directory or set environment variable PERF_DEBUGFS_DIR.
--buildid-dir, Setup buildid cache directory. It has higher priority than buildid.dir config file option.
--list-cmds, List the most commonly used perf commands.
--list-opts, List available perf options.
--debug, Setup debug variable (see list below) in value range (0, 10).
对于debug模式可以按照以下的方式进行设定:
--debug verbose # 表示设置verbose = 1
--debug verbose=2 # 表示设置verbose = 2
# 如verbose这样被允许设置的变量如下:
verbose - general debug messages
ordered-events - ordered events object debug messages
data-convert - data convert command debug messages
stderr - write debug output (option -v) to stderr in browser mode
perf-event-open - Print perf_event_open() arguments and return value
Perf Tool 是一个用户态工具集,包含了多种子工具集,下表具体介绍每种工具的基本功能:
2.2 perf list 查看支持的性能事件
perf工具支持一系列可测量的事件。该工具和底层内核接口可以测量来自不同来源的事件。例如,有些事件是纯内核计数器,在这种情况下称为软件事件。例如:上下文切换、小故障。
事件的另一个来源是处理器本身及其性能监控单元(PMU)。它提供了一个事件列表来测量微体系结构事件,如周期数、指令退役、L1缓存缺失等。这些事件被称为PMU硬件事件或简称硬件事件。它们因处理器类型和型号而异。
perf_events接口还提供了一组常用的硬件事件名称。在每个处理器上,如果这些事件存在,则将它们映射到CPU提供的实际事件上,否则无法使用事件。有些令人困惑的是,这些事件也称为硬件事件(hardware event)和硬件缓存事件(hardware cache event)。
最后,还有由内核ftrace基础设施实现的tracepoint事件。这些仅在2.6.3 3x和更新的内核中可用。
命令帮助信息如下:
onceday->~:# perf list -h
Usage: perf list [<options>] [hw|sw|cache|tracepoint|pmu|sdt|metric|metricgroup|event_glob]
-d, --desc Print extra event descriptions. --no-desc to not print.
-v, --long-desc Print longer event descriptions.
--debug Enable debugging output
--deprecated Print deprecated events.
--details Print information on the perf event names and expressions used internally by events.
下面是一个实际输出,该命令显示可在使用-e选项的各种perf命令中选择的符号事件类型:
onceday->~:# perf list
List of pre-defined events (to be used in -e):
branch-misses [Hardware event]
bus-cycles [Hardware event]
cache-misses [Hardware event]
cache-references [Hardware event]
cpu-cycles OR cycles [Hardware event]
instructions [Hardware event]
alignment-faults [Software event]
bpf-output [Software event]
context-switches OR cs [Software event]
cpu-clock [Software event]
cpu-migrations OR migrations [Software event]
dummy [Software event]
emulation-faults [Software event]
major-faults [Software event]
minor-faults [Software event]
page-faults OR faults [Software event]
task-clock [Software event]
L1-dcache-load-misses [Hardware cache event]
L1-dcache-loads [Hardware cache event]
L1-dcache-store-misses [Hardware cache event]
L1-dcache-stores [Hardware cache event]
L1-icache-load-misses [Hardware cache event]
L1-icache-loads [Hardware cache event]
branch-load-misses [Hardware cache event]
branch-loads [Hardware cache event]
dTLB-load-misses [Hardware cache event]
dTLB-store-misses [Hardware cache event]
iTLB-load-misses [Hardware cache event]
node-loads [Hardware cache event]
node-stores [Hardware cache event]
armv8_cortex_a72/br_mis_pred/ [Kernel PMU event]
armv8_cortex_a72/br_pred/ [Kernel PMU event]
...(省略大量其他输出内容)...
PMU硬件事件是特定于CPU的,并由CPU供应商记录。如果链接到libpfm4, perf工具库会提供一些事件的简短描述。有关Intel和AMD处理器的PMU硬件事件列表,请参见:
- Intel® 64 and IA-32 Architectures Developer’s Manual: Vol. 3B
- BIOS and Kernel Developer’s Guide (BKDG) For AMD Family 10h Processors
perf list列出来的这些事件就是本机设备上受支持性能事件,后面中括号里面就是具体的事件类型,这些事件可能会非常多,不同的账户权限执行的结果也会有些不同。
对于非root用户,通常只有上下文切换的PMU事件可用。这通常只是cpu PMU中的事件、预定义的事件(如周期和指令)以及一些软件事件。其他pmu和全局测量通常仅为root
可用。一些事件限定符,如“any”,也是root
限定符。这可以通过设置kernel.perf_event_paranoid
为-1
来修改(使用sysctl),允许非root用户使用这些事件。为了访问跟踪点事件,perf
需要对/sys/kernel/debug/tracing具有读访问权限,即使perf_event_paranoid
处于宽松设置中也是如此。
2.3 perf 性能事件修饰符
对于任何受支持的事件,perf可以在流程执行期间保持运行计数。在计数模式中,事件的发生只是聚合在一起,并在应用程序运行结束时显示在标准输出上。要生成这些统计信息,可以使用perf的stat命令。例如:
onceday->~:# perf stat -B dd if=/dev/zero of=/dev/null count=1000000
1000000+0 records in
1000000+0 records out
512000000 bytes (512 MB, 488 MiB) copied, 4.01987 s, 127 MB/s
Performance counter stats for 'dd if=/dev/zero of=/dev/null count=1000000':
2239.106840 task-clock (msec) # 0.556 CPUs utilized
10041 context-switches # 0.004 M/sec
0 cpu-migrations # 0.000 K/sec
145 page-faults # 0.065 K/sec
2665504250 cycles # 1.190 GHz
2338072047 instructions # 0.88 insn per cycle
<not supported> branches
6692430 branch-misses
4.027916520 seconds time elapsed
在没有指定事件的情况下,perf stat收集上面列出的常见事件。有些是软件事件,如上下文切换,有些是一般的硬件事件,如循环。
可以在每次运行perftool时测量一个或多个事件。事件是用它们的符号名和可选的单位掩码和修饰符来指定的。事件名称(Event names)、单元掩码(unit masks)和修饰符(modifiers)不区分大小写。
默认情况下,事件是在用户和内核级别度量的:
perf stat -e cycles dd if=/dev/zero of=/dev/null count=100000
若要仅在用户级别进行度量,则需要传递一个修饰符(u):
perf stat -e cycles:u dd if=/dev/zero of=/dev/null count=100000
要测量用户和内核(显式地):
perf stat -e cycles:uk dd if=/dev/zero of=/dev/null count=100000
事件可以通过附加冒号和一个或多个修饰符来选择具有修饰符。修饰符允许用户限制何时对事件进行计数。修饰符如下:
p修饰符可用于指定指令地址的精确程度。p修饰符可以被指定多次:
- 0 - SAMPLE_IP可以任意滑动
- 1 - SAMPLE_IP必须有恒定的滑动
- 2 - SAMPLE_IP要求有O滑块
- 3 - SAMPLE_IP必须有0滑块,或者使用随机化来避免样本副作用效果。
对于英特尔系统,精确事件采样是用PEBS实现的,它支持精确级别2,在某些特殊情况下支持精确级别3。
在AMD系统上,它是使用IBS实现的(最高精确级别到2)。精确修饰符与事件类型0x76 (cpu-cycles,CPU时钟未停止)和0xC1(micro-ops retired)一起工作。
2.4 测量特定硬件上的PMU事件
即使现在在perf中没有符号形式的事件,也可以用特定于每个处理器的方式对其进行编码。
比如对于X86CPUs,要测量CPU硬件供应商文档中提供的实际PMU,可以传递十六进制参数代码:
perf stat -e r1a8 -a sleep 1
perf record -e r1a8 ...
有些处理器,比如AMD的处理器,支持大于一个字节的事件代码和单元掩码。在这种情况下,与事件配置参数对应的位可以参考下面命令的结果:
cat /sys/bus/event_source/devices/cpu/format/event
比如可能的命令如下:
perf record -e r20000038f -a sleep 1
perf record -e cpu/r20000038f/ ...
perf record -e cpu/r0x20000038f/ ...
有关于特定硬件上的PMU事件,需要参考处理器的说明文档来确定使用方法。
在下面的路径可以查看可用的PMUs和它们的原始参数:
ls /sys/devices/*/format
一些pmu不与核心相关联,而是与整个CPU socket
相关联。这些pmu上的事件通常不能采样,只能使用perf stat -a
进行全局计数。它们可以绑定到一个逻辑CPU,但是会测量同一个插槽中的所有CPU。
本例在Intel Xeon系统的socket 0
上的第一个内存控制器上每秒测量内存带宽:
perf stat -C 0 -a uncore_imc_0/cas_count_read/,uncore_imc_0/cas_count_write/ -I 1000 ...
每个内存控制器都有自己的PMU。测量整个系统带宽需要指定所有imc pmu(请参阅perf list output),并将这些值相加。为了简化多个事件的创建,在PMU名称中支持前缀和全局匹配,并且在执行匹配时也忽略前缀uncore_。因此,上面的命令可以通过使用以下语法扩展到所有内存控制器:
perf stat -C 0 -a imc/cas_count_read/,imc/cas_count_write/ -I 1000 ...
perf stat -C 0 -a *imc*/cas_count_read/,*imc*/cas_count_write/ -I 1000 ...
2.5 参数化的性能事件
有一些pmu事件列出来的时候,其显示字符中带有?
号。如下:
hv_gpci/dtbp_ptitc,phys_processor_idx=?/
这意味着当作为事件提供时,?
所指示的内容必须也可提供。
perf stat -C 0 -e 'hv_gpci/dtbp_ptitc,phys_processor_idx=0x2/' ...
此外还有可能指定额外的事件修饰符(percore):
perf stat -e cpu/event=0,umask=0x3,percore=1/
上面命令即汇总一个核心中所有硬件线程的事件计数。
2.6 事件组测量
当活动事件的数量超过硬件性能计数器的数量时,Perf支持基于时间的事件复用。当工作负载更改其执行配置文件时,多路复用可能导致测量错误。
当使用来自事件计数的公式计算度量时,确保始终将一些事件作为一个组一起测量以最小化多路错误是很有用的。事件组可以使用{}
指定。
perf stat -e '{instructions,cycles}' ...
可用性能计数器的数量取决于CPU。一个组不能包含比可用计数器更多的事件。例如,Intel Core cpu通常有四个通用的核心性能计数器,加上三个固定的instructions
、cycles
和ref-cycles
计数器。一些特殊事件对它们可以调度的计数器有限制,并且可能不支持单个组中的多个实例。当组中指定的事件太多时,其中一些事件将无法测量。
全局固定事件可以限制其他组可用的计数器数量。在x86系统上,NMI看门狗默认固定一个计数器。NMI看门狗可以在root用户下禁用:
echo 0 > /proc/sys/kernel/nmi_watchdog
来自多个不同pmu的事件不能混合在一个组中,软件事件除外。
perf还支持使用:S
说明符进行组领导抽样(group leader sampling
)。
perf record -e '{cycles,instructions}:S' ...
perf report --group
通常情况下,所有事件都在一个事件组样本中,但是使用:S
时,只有第一个事件(leader)进行采样,它只读取组中其他事件的值。然而,在AUX区域事件(例如Intel PT或CoreSight)的情况下,AUX区域事件必须是先导事件,因此第二个事件采样,而不是第一个事件。
2.7 perf list性能事件分类
默认情况下,perf list列出所有的已知事件。也可以通过下面的类别来列出其中某一类事件:
(本文章为系列文章,未完待续)