top(1) 显示一个进程正在消耗 CPU – 你接下来要做什么?根据应用程序和上下文,您可能会使用应用程序分析器来查看原因,或读取其日志,甚至终止它。但是,有一个适用于任何应用程序的答案,甚至是内核:使用像 Linux perf(又名 perf_events)这样的系统分析器。
perf不是随机工具:它是 Linux 内核的一部分,并且正在积极开发和增强。它也很强大:它可以检测硬件计数器、静态跟踪点和动态跟踪点。
在这篇文章中,我将限制自己使用一个特性:CPU 采样。让我们假设这是我们的目标:
顶部 - 04:38:41 上升 29 天,9 分钟,2 个用户,平均负载:2.82、3.26、1.67
任务:共 133 个,运行 9 个,睡眠 123 个,停止 0 个,僵尸 1 个
中央处理器:28.0%us、72.0%sy、0.0%ni、0.0%id、0.0%wa、0.0%hi、0.0%si、0.0%st
内存:总共 7629464k,已使用 1481328k,空闲 6148136k,285412k 缓冲区
交换:总共 0k,使用 0k,免费 0k,缓存 566712k
PID 用户 PR NI VIRT RES SHR S %CPU %MEM TIME+ 命令
6336 根 20 0 117m 976 488 R 25 0.0 0:01.35 fio
6338 根 20 0 117m 972 484 R 25 0.0 0:01.35 fio
6339 根 20 0 117m 976 488 R 25 0.0 0:01.35 fio
6340 根 20 0 117m 976 488 R 25 0.0 0:01.33 fio
6342 根 20 0 117m 976 488 R 25 0.0 0:01.33 fio
6335 根 20 0 117m 972 484 R 25 0.0 0:01.32 fio
6341 根 20 0 117m 972 484 R 25 0.0 0:01.33 fio
6337 根 20 0 117m 960 472 R 24 0.0 0:01.31 fio
2337 bgregg-t 20 0 149m 5608 4436 S 0 0.1 3:42.93 postgres
[…]
这个 top(1) 输出显示了几个fio进程,每个进程占用 25% 的 CPU。为什么?他们在做什么?
1.检查perf是否安装
没有参数,它应该打印一条帮助消息:
#性能
用法:性能 [–version] [–help] 命令 [ARGS]
最常用的 perf 命令是:
[…]
如果它不存在,您可能会发现它可以从 linux-tools-common 包中添加。它也在 Linux 内核源代码的 tools/perf 下。
2. 配置 CPU
sudo perf record -F 99 -a -g – sleep 20
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.560 MB perf.data (~24472 samples) ]
Options are:
-F 99: sample at 99 Hertz (samples per second). I’ll sometimes sample faster than this (up to 999 Hertz), but that also costs overhead. 99 Hertz should be negligible. Also, the value ‘99’ and not ‘100’ is to avoid lockstep sampling, which can produce skewed results.
-a: samples on all CPUs. Without it, it only samples a supplied command or PID.
-g: include stack traces.
–: skips providing a -g argument (in newer perf versions, -g can pick the stack unwinding method).
sleep 20: a dummy command, used to set the duration of our sampling.
As perf tells you, it writes a perf.data file.
3. Read profile
sudo perf report -n --stdio
[…]
Overhead Samples Command Shared Object Symbol
… … … … …
20.97% 208 fio [kernel.kallsyms] [k] hypercall_page
|
--- hypercall_page
check_events
|
|--63.94%-- 0x7fff695c398f
|
|--18.27%-- 0x7f0c5b72bd2d
|
--17.79%-- 0x7f0c5b72c46d
14.21% 141 fio [kernel.kallsyms] [k] copy_user_generic_string
|
--- 复制用户通用字符串
do_generic_file_read.constprop.33
generic_file_aio_read
do_sync_read
vfs_read
sys_read
system_call_fastpath
0x7f0c5b72bd2d
10.79% 107 线程 [vdso] [.] 0x7fff695c398f
|
--- 0x7fff695c398f
时钟获取时间
[…]
您可以只为交互式文本用户界面运行perf 报告,并使用箭头键向下钻取堆栈。我发现这种模式很费力,通常使用这种–stdio模式。-n打印样本计数。
百分比显示每个级别的细分。将它们相乘以查看每片叶子的百分比。例如,0x7fff695c398f(无论是什么)的采样率为 20.97% x 63.94%(= %13.40)。如果您希望perf为您进行乘法运算,并始终显示绝对百分比,请使用-g graph。
显示的代码性能对您来说可能是陌生的,但至少学习“最热门”(最常采样的)堆栈应该不会花很长时间。通常,函数名称就足以提供线索。clock_gettime可能是…得到时间。fio是否可以避免这样做,或者以不同的方式来消除这种开销?(是的,在这种情况下。)
常见问题
如果 perf_events 无法翻译符号,则会打印十六进制数字,这可能发生在剥离的二进制文件或 JIT 代码中。对于前者,查找 dbgsym 包(调试符号),或者重新编译并且不要剥离应用程序,并确保内核具有 CONFIG_KALLSYMS。对于后者,这是一个更大的话题,我将在另一个时间写(perf 的 JIT 支持)。
不完整的堆栈通常意味着使用了 -fomit-frame-pointer - 一种编译器优化,在现实世界中几乎没有积极的影响,但会破坏堆栈分析器。始终使用 -fno-omit-frame-pointer 进行编译。最近的perf有一个-g dwarf选项,用于使用备用 libunwind/dwarf 方法来检索堆栈。
虽然获得带有符号的完整堆栈可能需要一些工作,但部分工作的配置文件足以解决一些问题。例如,我可能无法在 JVM 中看到 Java 方法,但可以看到 JVM 系统库使用情况、内核 CPU 使用情况和 GC。
火焰图
如果perf 报告的输出太长而无法快速阅读,您可以使用perf 脚本重新处理 perf.data 文件并使用perf Flame Graphs将其可视化。我一直在使用它们。
但是等等,还有更多!
我想写一个简单、简短的perf示例,它不长且令人生畏。perf_events还有很多:请参阅我的perf 示例页面和官方perf wiki。
转载翻译自: https://www.brendangregg.com