Perf Events
Perf Event 是面向事件的观察工具。简要来说,Perf Events 可以用于解决下面的问题:
- 为什么内核占用这么多 CPU 时间?具体是哪一个代码段耗时?
- 哪部分代码导致 CPU 会导致 L2 的缓存失效?
- 是否 CPU 被 memory I/O 所害?
- 哪段代码在疯狂分配内存?
- 到底谁导致了 TCP 的重传?
- 是否内核中的某一个方法被调用了,有多频繁?
一共有两种 Perf Events:
- 第一种是直接利用现有提供的 Events,其覆盖了大部分可能会用到的事件
- 如果刚好里面没有想要的,则需要利用第二种方式,自己写新的 Perf_Events。
而针对如何如何利用 Perf Events 进行测量,一共有三种方法:
- 直接利用 Counting Event 计数,可以利用 perf 工具直接对发生的次数进行计数。这种方法不会生成 perf.data 文件,直接利用 perf stat 命令即可。
- 在指定的时间进行取样,使用这种方法会将 Perf Events 数据写到内核缓存里面,然后再由 Perf 隔一段时间写入 perf.data 文件中。最后利用 perf report 或者 perf script 读取。但是利用这种方法进行采样,report 文件的大小 overhead 比较高(文件很大)。
- 最后一种方法是利用 BPF 触发用户自己写的程序,这种方法最灵活。但是同时这种方法也比较复杂,需要自己写触发程序,在后面的 eBPF 章节中进行描述。
Events
如图所示,Linux Perf Events 可以分为以下几类:
- Hardware Events: CPU PMU 性能检测计数器
- Software Events: 利用 kernel counters 低层次的 events,例如 CPU 迁移, minor faults, major faults.
- Kernel Tracepoint Events: 有些内核态的 tracepoint 被硬件编码到内核中的一些地方。
- User Statically-Defined Tracing (USDT): 用户态程序中的静态 tracepoint。
- Dynamic Tracing: 利用 kprobe 和 uprobe 在任意位置创建 event。
- Timed Profiling: 可以间隔一段时间时间进行快照。
目前 perf 支持的事件可以用 perf list 列举出来,如果使用动态追踪,可以追踪其他的事件。
|
|
Software Events
下面是一些 software event:
|
|
你也可以通过 perf_event_open(2) 的 man page 查看这些事件:
|
|
内核也支持 tracepoints,这和 software events 非常像,但是有一个更加灵活的 API。
software event 有一个默认的周期,这意味着当你使用 perf 采样的时候,你获得的是所有事件的一个子集,而不是追踪每一个事件。使用 -vv 参数你可以看到 sample_period 参数,这些字段的含义可以在 perf_event_open(2) 的 man page 中看到。这里的含义是,内核会调整采样的速率使得每秒采样到 4000 次上下文切换。
|
|
如果你想采样到所有事件,可以使用 -c 1 参数。
|
|
Hardware Events
硬件事件是利用处理器的 performance Monitoring Unit(PMU) 实现,读取其中的 Performace Monitoring Counters(PMCs) 或者称为 Performance instrumentation counters(PICs) 。这些 Counter 可以跟踪一些底层的动作,如 CPU cycles,instructions retired, memory stall cycles, level 2 cache misses 等等。
PMCs are documented in the Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 3B: System Programming Guide, Part 2 and the BIOS and Kernel Developer’s Guide (BKDG) For AMD Family 10h Processors. There are thousands of different PMCs available.
这些硬件事件的特点是只有其中少数几个事件可以同时被记录。 这时因为硬件资源有限,需要手动指定它们记录哪些 event。如何使用 Hardware Events 使用硬件 Raw Counter 的格式是 rUUEE,其中 UU 是 umask,EE 是 e vent number。而大部分好用的直接加到了 perf list 中,直接用对应的事件就行。
Kernel Tracepoints
kernel tracepoints 是 hard coded 到内核中的追踪点,具体可以参考 我的这篇文章。
|
|
这里面包括的 tracepoints 有:
- block: block device I/O
- ext4: file system operations
- kmem: kernel memory allocation events
- random: kernel random number generator events
- sched: CPU scheduler events
- syscalls: system call enter and exits
- task: task events
USDT
和 kernel tracepoints,USDT(User Level Statically Defined Tracing) 也是被 hardcode 到用户态应用程序中,并且有一个稳定的 API。很多用户程序已经支持 tracepoints 来支持 DTrace,但是有时候需要用户显式通过 --with-dtrace来编译:
|
|
编译完成后可以查看可以探测的点:
|
|
Dynamic Tracing
tracepoints 和 dynamic tracing 的区别如下图所示:
tracepoint是固定添加在某些位置的,往往比较稳定dynamic tracing可以看到所有的点,因为使用的是raw code所以不太稳定
- 如果是动态追踪内核,需要开启
CONFIG_KPROBES=y和CONFIG_KPROBE_EVENTS=y - 如果是动态追踪用户空间,需要开启
CONFIG_UPROBES=y和CONFIG_UPROBE_EVENTS=y
Examples
CPU Statistics
perf stat 命令统计 PMC 相关参数,如下所示:
|
|
这里面包含 IPC,也就是每个时钟周期执行的命令数,这是一个很常用的指标,IPC 值高(超过 1.0) 表明任务执行顺畅。但是也要注意执行的命令是什么,比如在一个 spin loop 里面,IPC 可能很高,但是实际执行的工作却很少。
通过 -d 可以获得 perf stat 更详细的命令输出:
|
|
Timed Profiling
per_events 可以通过定期采样 instruction pointer 来对 CPU usage 进行 profile,以下命令以 99 Hz 的频率对 CPU Stacks 采样:
|
|
通过 perf report --stdio 命令可以将 perf.data 展示出来:
|
|
Event Profiling
除了定期的采样,直接从 CPU 硬件计数器采样也是另一种 CPU profiling 的形式,下面列出了可以采样的事件,
|
|
这里面的很多事件,如果每次发生都采样将会带来巨大的开销,因此一般只是采样一小部分,可以通过 -c count 命令指定,比如:
|
|
Static Kernel Tracing
下面的例子显示了计算系统调用的次数,可以看到其中主要是 sys_enter_read 和 sys_enter_write 的系统调用:
|
|
另一种方法是使用 strace -c ,但是对比 perf 和 strace ,perf 带来的性能开销要小得多:
|
|
Static User Tracing
这里是追踪 Node.js 的 USDT Probe 的例子:
|
|
Dynamic Tracing
通过下列命令添加 tcp_sendmsg 的 tracepoints event:
|
|
接下来使用 perf record 采样:
|
|
显示报告如下:
|
|
Scheduler Analysis
通过 perf sched 命令可以对内核调度器进行分析:
|
|
通过 perf script 可以查看详细的调度事件,此处暂时不具体分析。
|
|
eBPF
使用 BPF 可以更加灵活地指定或筛选自己需要用到的内容。利用 eBPF 的方法简要描述如下:
- 在
/proc/kallsyms中找到想要追踪的地址。 - 写自己的 BPF 程序:
|
|
-
直接用 perf 指定用自己的程序进行 trace
1# perf record -e bpf-output/no-inherit,name=evt/ -e ./kca_from.c/map:channel.event=evt/ -a -- sleep 1
参考资料
-
No backlinks found.