perf 是 Linux 内核自带的一个强大的性能分析工具,它能够深入到内核和用户空间,提供丰富的性能计数器和事件跟踪功能,帮助开发者和系统管理员精确地定位系统性能瓶颈。
perf 主要通过以下方式来收集性能数据:
#include <stdio.h>
#include <unistd.h>
void even(void)
{
printf("it is even\n");
}
void odd(void)
{
printf("it is odd\n");
}
int main()
{
int i;
for (i = 0; ;i++) {
usleep(500);
if (i % 2)
odd();
else
even();
}
}
# gcc -o -g evenodd evenodd.c
# perf probe --line even -x evenodd
<even@/root/code/evenodd.c:0>
0 void even(void)
{
2 printf("it is even\n");
3 }
void odd(void)
{
在 even() 函数的开头添加 perf 探针,请运行以下命令:
# objdump -t evenodd
evenodd: file format elf64-littleaarch64
SYMBOL TABLE:
0000000000400238 l d .interp 0000000000000000 .interp
0000000000400254 l d .note.gnu.build-id 0000000000000000 .note.gnu.build-id
0000000000400278 l d .note.ABI-tag 0000000000000000 .note.ABI-tag
0000000000400298 l d .gnu.hash 0000000000000000 .gnu.hash
...
# perf probe -x evenodd 'even'
要获取给定函数的执行时间,我们需要在函数的最后一行添加探针。如 perf probe -line 示例所示,是第 3 行。下面的命令在 evenondd 二进制文件的函数 event() 的第 3 行添加了名为 even_end event 的探针。
# perf probe -x evenodd 'even_end=even:3'
开始运行evenodd应用程序后,运行以下命令记录 even() 执行 10 秒的进入和退出探针:
# perf record -e probe_evenodd:even -e probe_evenodd:even_end -a sleep 10
记录结束后,可以使用以下命令打印跟踪结果:
# perf script
evenodd 2342328 [034] 1232877.416865: probe_evenodd:even: (4006c4)
evenodd 2342328 [034] 1232877.416875: probe_evenodd:even_end: (4006d8)
evenodd 2342328 [034] 1232877.417990: probe_evenodd:even: (4006c4)
evenodd 2342328 [034] 1232877.417996: probe_evenodd:even_end: (4006d8)
evenodd 2342328 [034] 1232877.419109: probe_evenodd:even: (4006c4)
...
还可以使用 perf python脚本自动计算执行时间。捕获这两个事件的跟踪后,以下命令将生成名为 perf-script.py 的 python 脚本框架:
# perf script -g python
然后编辑探测处理程序,计算执行时间:
# perf script event handlers, generated by perf script -g python
# Licensed under the terms of the GNU GPL License version 2
# The common_* event handler fields are the most useful fields common to
# all events. They don't necessarily correspond to the 'common_*' fields
# in the format files. Those fields not available as handler params can
# be retrieved using Python functions of the form common_*(context).
# See the perf-script-python Documentation for the list of available functions.
from __future__ import print_function
import os
import sys
sys.path.append(os.environ['PERF_EXEC_PATH'] + \
'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
from perf_trace_context import *
from Core import *
def trace_begin():
print("in trace_begin")
def trace_end():
print("in trace_end")
start=0
def probe_evenodd__even(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, common_callchain, __probe_ip, perf_sample_dict):
global start
start=(common_secs * 1000000000) + common_nsecs
def probe_evenodd__even_end(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, common_callchain, __probe_ip, perf_sample_dict):
now=common_secs * 1000000000 + common_nsecs
duration=now - start
print("even runtime: " + str(duration) + " ns")
def trace_unhandled(event_name, context, event_fields_dict, perf_sample_dict):
print(get_dict_as_string(event_fields_dict))
print('Sample: {'+get_dict_as_string(perf_sample_dict['sample'], ', ')+'}')
def print_header(event_name, cpu, secs, nsecs, pid, comm):
print("%-20s %5u %05u.%09u %8u %-20s " % \
(event_name, cpu, secs, nsecs, pid, comm), end="")
def get_dict_as_string(a_dict, delimiter=' '):
return delimiter.join(['%s=%s'%(k,str(v))for k,v in sorted(a_dict.items())])
然后,运行以下命令,就可以打印出函数 even 的执行时间:
# perf script -s perf-script.py | tail
even runtime: 6540 ns
even runtime: 7080 ns
even runtime: 6770 ns
even runtime: 7220 ns
even runtime: 6850 ns
even runtime: 6390 ns
even runtime: 6910 ns
even runtime: 6760 ns
even runtime: 7460 ns
in trace_end