linux性能工具perf工作原理简析

地址:https://my.oschina.net/u/2475751/blog/1823736背景
此前工作中,笔者使用perf测过CPU的CPI[1],cache miss, 内存带宽等性能指标 。另外,还移植过perf uncore[2]相关的补丁 。这些让我很好奇:perf大概是怎么工作的? 带着这个问题,笔者谨希望把自己的一点经验分享出来 。
perf-list
perf list列出的event有这几类:1. hardware,如cache-misses; 2. software, 如context switches; 3. cache, 如L1-dcache-loads;4. tracepoint; 5. pmu 。但是,perf list仅仅把有符号名称的事件列出来了,而缺了很多硬件相关的事件 。这些硬件相关事件叫作Raw Hardware Event, man perf-list有介绍 。
举个例子,PMU是一组监控CPU各种性能的硬件,包括各种core, offcore和uncore事件 。单说perf uncore, Intel处理器就提供了各种的性能监控单元,如内存控制器(IMC), 电源控制(PCU)等等,详见《Intel® Xeon® Processor E5 and E7 v4 Product Families Uncore Performance Monitoring Reference Manual》[3] 。这些uncore的PMU设备,注册在MSR space或PCICFG space[4],可以通过下面命令看到(抹掉同类别设备):
$ls /sys/devices/ | grep uncoreuncore_cbox_0uncore_ha_0uncore_imc_0uncore_pcuuncore_qpi_0uncore_r2pcieuncore_r3qpi_0uncore_ubox【linux性能工具perf工作原理简析】但是,使用perf list只能显示IMC相关事件:
$perf list|grep uncore uncore_imc_0/cas_count_read/ [Kernel PMU event] uncore_imc_0/cas_count_write/ [Kernel PMU event] uncore_imc_0/clockticks/ [Kernel PMU event] ...uncore_imc_3/cas_count_read/ [Kernel PMU event] uncore_imc_3/cas_count_write/ [Kernel PMU event] uncore_imc_3/clockticks/ [Kernel PMU event]为什么perf list没有显示其他uncore事件呢?从代码分析来看,perf list会通过sysfs去读取uncore设备所支持的event, 见linux/tools/perf/util/pmu.c:pmu_aliases():
/* * Reading the pmu event aliases definition, which should be located at: * /sys/bus/event_source/devices/<dev>/events as sysfs group attributes. */ static int pmu_aliases(const char *name, struct list_head *head)再看perf uncore的驱动代码,发现只有iMC uncore设备注册了events相关属性, 见arch/x86/events/intel/uncore_snbep.c:hswep_uncore_imc_events:
static struct uncore_event_desc hswep_uncore_imc_events[] = { INTEL_UNCORE_EVENT_DESC(clockticks, "event=0x00,umask=0x00"), INTEL_UNCORE_EVENT_DESC(cas_count_read, "event=0x04,umask=0x03"), INTEL_UNCORE_EVENT_DESC(cas_count_read.scale, "6.103515625e-5"), INTEL_UNCORE_EVENT_DESC(cas_count_read.unit, "MiB"), INTEL_UNCORE_EVENT_DESC(cas_count_write, "event=0x04,umask=0x0c"), INTEL_UNCORE_EVENT_DESC(cas_count_write.scale, "6.103515625e-5"), INTEL_UNCORE_EVENT_DESC(cas_count_write.unit, "MiB"), { /* end: all zeroes */ },};从实用性看,在所有uncore设备中,系统工程师可能最常用的就是iMC提供的内存带宽监测 。其它不常用到的uncore PMU事件,可以通过Raw Hardware Event的方式,查看Intel Uncore手册[5]来指定 。
在使用过程中,发现一个perf list存在的bug,iMC channel的编号不正确,发了个补丁得到了Intel工程师review,upstream还没有merge,见perf/x86/intel/uncore: allocate pmu index for pci device dynamically[6] 。这是一个很明显的问题,刚开始我不相信上游或Intel会允许这样明显的问题存在,虽然问题不大,通过解决这个问题的感受是perf可能隐藏一些问题,需要在测试中提高警惕,最好能通过其他测量方式进行粗略的对比验证 。
perf-stat
perf-stat是最常用到的命令,用man手册的原话就是Run a command and gathers performance counter statistics from it 。perf-record命令可看做是perf-stat的一种包装,核心代码路径与perf-stat一样,加上周期性采样,用一种可被perf-report解析的格式将结果输出到文件 。因此,很好奇perf-stat是如何工作的 。
perf是由用户态的perf tool命令和内核态perf驱动两部分,加上一个连通用户态和内核态的系统调用sys_perf_event_open组成 。
最简单的perf stat示例
perf工具是随内核tree一起维护的,构建和调试都非常方便:
$cd linux/tools/perf$make...$./perf stat ls... Performance counter stats for 'ls': 1.011337 task-clock:u (msec) # 0.769 CPUs utilized 0 context-switches:u # 0.000 K/sec 0 cpu-migrations:u # 0.000 K/sec 105 page-faults:u # 0.104 M/sec 1,105,427 cycles:u # 1.093 GHz 1,406,263 instructions:u # 1.27 insn per cycle 282,440 branches:u # 279.274 M/sec 9,686 branch-misses:u # 3.43% of all branches 0.001314310 seconds time elapsed以上是一个非常简单的perf-stat命令,运行了ls命令,在没有指定event的情况下,输出了几种默认的性能指标 。下面,我们以这个简单的perf-stat命令为例分析其工作过程 。
用户态工作流


推荐阅读