Linux perf_events Off-CPU 时间火焰图

我被问过好几次了,所以这里有一篇快速的博客文章。
CPU 火焰图非常适合了解 CPU 使用情况,但是当程序被阻塞并且不在 CPU 上运行时,延迟的性能问题呢?有一种通用的方法来攻击这些,我称之为Off-CPU Analysis。这涉及在调度程序阻塞线程以及稍后将其唤醒时检测调用堆栈和时间戳。甚至还有Off-CPU Time Flame Graphs来可视化这个阻塞时间,这与 on-CPU 火焰图相对应。
非 CPU 时间火焰图可以解决(比如说)60% 的问题,其余的需要遍历线程唤醒以找到根本原因。我在关于火焰图的 LISA13 演讲(幻灯片、youtube)中解释了非 CPU 时间火焰图、这个唤醒问题和其他工作。
在这里,我将展示一种使用 Linux perf_events 进行非 CPU 时间火焰图的方法。示例(点击放大):
与 CPU 火焰图不同,在此图中,宽度跨越了代码路径处于休眠状态的总持续时间。捕获了“睡眠 1”命令,在最右侧显示为已睡眠 1,000 毫秒。
我在github上与 Nicolas 讨论如何执行这些操作,因为他发现 perf_events 几乎可以使用perf injection -s功能做到这一点,当时我注意到 perf_events 已在 perf 脚本中添加了“-f period”(大约在 3.17 中添加) . 这稍微简化了一些事情,因此步骤可以是:
#性能记录 -e sched:sched_stat_sleep -e sched:sched_switch
-e sched:sched_process_exit -a -g -o perf.data.raw sleep 1

perf injection -v -s -i perf.data.raw -o perf.data

perf script -f comm,pid,tid,cpu,时间,期间,事件,ip,sym,dso,trace | 啊啊啊

NF > 4 { 执行 = $1; period_ms = int($5 / 1000000) }
NF > 1 && NF <= 4 && period_ms > 0 { 打印 $2 }
NF < 2 && period_ms > 0 { printf "%s\n%d\n\n", exec, period_ms }' | \
./stackcollapse.pl | \
./flamegraph.pl --countname=ms --title="Off-CPU 时间火焰图" --colors=io > offcpu.svg

更新:在更新的内核上,使用“perf script -F …”而不是“perf script -f …”。您的内核还需要 CONFIG_SCHEDSTATS 才能使所有跟踪点都存在,这可能会丢失(例如,RHEL7)。
我正在使用的 awk 只是将 perf 脚本输出转换为 stackcollapse.pl 可以理解的内容。整个 perf 注入工作流程让我觉得有点奇怪,可以通过自己处理 perf 脚本输出(更多 awk!)来跳过此步骤,以将事件拼接在一起。如果我使用的是缺少 perf script -f 周期的旧内核,我会这样做。
警告:调度程序事件可能非常频繁,并且将它们转储到文件系统(perf.data)的开销在生产环境中可能会令人望而却步。仔细测试。这也是我在 perf 记录(设置持续时间的虚拟命令)中放置“sleep 1”的原因,以从少量跟踪数据开始。如果我必须在生产中执行此操作,我会考虑其他可以在内核中汇总数据以减少开销的工具,包括 perf_events 一旦它支持更多的内核编程 (eBPF)。
在某些情况下,当前的 perf_events 开销是可以接受的,特别是如果您匹配一个感兴趣的 PID(在 perf 记录中,使用“-p PID”而不是“-a”)。
更新 1:从 Linux 4.5 开始,您可能需要以下内容才能使其工作:

echo 1 > /proc/sys/kernel/sched_schedstats

更新 2:从 Linux 4.6 开始,您可以使用 BPF 更有效地执行此操作:在内核上下文中聚合堆栈(使用 4.6 中的 BPF 堆栈跟踪功能),并且只将摘要传递给用户级。我开发了 offcputime 工具bcc来做到这一点。我还写了一篇关于它的帖子,Off-CPU eBPF Flame Graph,尽管那是在 Linux 4.6 的堆栈跟踪支持之前编写的,所以我正在努力展开堆栈。

转载翻译自: https://www.brendangregg.com