perf-prof 是一个Linux系统级性能分析工具,基于perf_event在内存中实时处理事件。本技能提供使用perf-prof分析系统问题的工作流程和方法。
使用前先检查 perf-prof 是否已安装:
bash
which perf-prof && perf-prof --version
如果系统中没有 perf-prof 二进制文件, 需要询问用户确认 是否可以下载源码并编译安装,然后执行以下步骤:
bash
# 1. 下载源码
git clone https://github.com/OpenCloudOS/perf-prof.git
cd perf-prof
# 2. 安装依赖包
yum install -y xz-devel elfutils-libelf-devel libunwind-devel python3-devel
# 3. 编译
make
# 4. 验证安装
./perf-prof --version
操作步骤:
使用系统工具初步诊断(可选): bash
top -1 # 查看CPU使用率、进程状态
mpstat -P ALL 1 # 查看各CPU利用率
vmstat 1 # 查看内存、IO、CPU综合状态
iostat -x 1 # 查看磁盘IO
根据决策树选择分析器: text
系统问题
├── CPU相关
│ ├── CPU使用率高 → profile (采样分析)
│ ├── 进程运行时间统计 → top (sched_stat_runtime)
│ └── CPU上运行进程监控 → oncpu
├── 调度相关
│ ├── 进程状态分析(R/S/D) → task-state
│ ├── 调度延迟分析 → rundelay
│ └── 唤醒延迟分析 → multi-trace
├── 内存相关
│ ├── 内核内存泄露 → kmemleak
│ ├── 内存分配统计 → kmemprof
| ├── 缺页异常 → page-faults
| ├── 跟踪内存写入 → breakpoint
| └── 读取内核内存 → kcore
├── IO相关
│ └── 块设备延迟 → blktrace
├── 系统调用
│ └── 系统调用耗时 → syscalls
├── 虚拟化
│ └── KVM退出延迟 → kvm-exit
├── 硬件相关
| ├── 断点分析 → breakpoint
| └── 硬件状态 → tlbstat、llcstat、hwstat
├── 事件计数
| ├── 高频计数、微突发检测 → hrcount
| └── 低频计数 → stat
├── 通用事件追踪 → trace
└── 自定义脚本分析 → python
如有必要,在选择具体分析器之前,先通过定界分析确定问题发生在用户态还是内核态,Guest还是Host:
定界分析路径:
text
问题定界
├── 方法1:syscalls分析系统调用耗时
│ ├── 系统调用耗时高 → 内核态问题 → 进入内核分析路径
│ └── 系统调用耗时低 → 用户态问题 → 进入用户态分析路径
│
├── 方法2:task-state分析进程运行状态
| ├── R状态(Running)占比高 → 用户态CPU消耗 → 进入用户态分析路径
| ├── S状态(Sleeping)占比高 → 等待唤醒 → 分析唤醒源
| ├── D状态(Disk Sleep)占比高 → IO/锁等待 → 进入内核分析路径
| └── RD状态(Runnable)占比高 → 调度延迟 → 进入内核分析路径
|
└── 方法3:kvm-exit分析虚拟化退出耗时
├── 虚拟化退出耗时高 → Host问题 → 进入内核分析路径
└── 虚拟化退出耗时低 → Guest问题 → 进入Guest内分析
内核态问题分析路径:
text
# 1. 调度延迟分析
# 2. 系统调用深入分析:哪个系统调用慢,系统调用内发生的进程切换等
# 3. IO延迟分析
# 4. 内核热点采样
# 5. 虚拟化退出耗时分析
用户态问题分析路径:
text
# 1. 用户态CPU热点采样
# 2. 用户态函数追踪(添加uprobe点)
# 3. 用户态函数耗时分析
# 4. 业务层面分析:结合业务日志、应用metrics等
根据问题类型选择对应分析器,必要时选择多个分析器相互验证。
以下分析器有文档,参考 references/ 目录下的详细文档,优先通过第三步获取概要信息及示例:
R,S,D,T,t,I,RD)的耗时分布以下分析器无文档,必须通过第三步获取概要信息及示例:
按功能领域分类
profile, cpu-util, oncpukmemleak, kmemprof, page-faultstask-state, rundelay, oncpublktracekvm-exit, kvmmmu, bpf:kvm_exithwstat, llcstat, tlbstat, split-lock, ldlat-stores, ldlat-loads, breakpointirq-off, watchdogsql, python, traceexpr, misc, kcore, list, usdt, help按分析技术分类
profilehrcount, stat, percpu-stat, hrtimer, num-dist
hrcountstattop, sqlmulti-trace, nested-trace
rundelaysyscallsblktracekvm-exittask-state, oncpu
task-statetracepythonbreakpointmulti-trace, trace, python按事件依赖程度分类
misc, kcore, list, usdt, helptlbstat, llcstat, hwstat, breakpoint, kvmmmu, irq-off, page-faults, ldlat-stores, ldlat-loads, oncpu, blktrace, kvm-exit, percpu-stat, watchdog, task-state, cpu-util, profile, split-lockexpr, stat, hrcount, event-care, hrtimer, rundelay, nested-trace, syscalls, kmemprof, multi-trace, top, num-dist, kmemleak, trace, sql, pythonebpf类 bpf:kvm_exit
操作步骤:
查看分析器帮助: bash
perf-prof <profiler> -h
帮助信息包含:
-e 事件选项阅读分析器文档:
参考 references/ 目录下对应的md文档,获取详细用法和典型场景。
判断事件需求: text
分析器类型判断:
├── 内建事件类(无需-e选项)→ 直接跳转第六步
│ 例如:profile, task-state, blktrace, kvm-exit, oncpu
└── 自定义事件类(需要-e选项,或其他需要事件的选项)→ 继续确定事件选项要求
例如:trace, top, multi-trace, kmemleak, sql
确定事件选项要求
使用 -e, --event 选项指定事件
使用 --alloc/--free 选项指定事件。只有kmemleak使用
示例:
bash
# 查看profile分析器帮助
perf-prof profile -h
# 查看top分析器帮助(需要-e选项)
perf-prof top -h
操作步骤:
列出系统事件: bash
# 列出所有tracepoint事件
perf-prof list
# 按类别筛选事件
perf-prof list | grep "^sched:" # 调度相关
perf-prof list | grep "^kmem:" # 内存相关
perf-prof list | grep "^block:" # 块设备相关
perf-prof list | grep "^syscalls:" # 系统调用相关
查看事件字段: bash
# 查看事件的字段定义(用于配置过滤器和表达式)
perf-prof trace -e <sys:name> help
# 示例:查看sched_wakeup事件字段
perf-prof trace -e sched:sched_wakeup help
常用事件速查: 分析场景推荐事件关键字段进程唤醒sched:sched_wakeuppid, comm, prio, target_cpu进程切换sched:sched_switchprev_pid, next_pid, prev_comm, next_comm运行时间sched:sched_stat_runtimepid, comm, runtime内存分配kmem:kmallocptr, bytes_alloc, call_site内存释放kmem:kfreeptr软中断irq:softirq_entry/exitvec系统调用raw_syscalls:sys_enter/exitid
动态探针(当系统事件不满足需求时): bash
# kprobe - 内核函数探针
-e 'kprobe:try_to_wake_up'
# kretprobe - 内核函数返回探针
-e 'kretprobe:try_to_wake_up'
# uprobe - 用户态函数探针,二进制路径带有'/',必须使用双引号
-e 'uprobe:func@"/path/to/binary"'
# uretprobe - 用户态函数返回探针,二进制路径带有'/',必须使用双引号
-e 'uretprobe:func@"/path/to/binary"'
新增动态探针(当系统事件不满足要求且动态探针无法使用或需要增加参数时):
通过 /sys/kernel/debug/tracing/kprobe_events 和 /sys/kernel/debug/tracing/uprobe_events 文件手动新增探针。
判断新增的动态探针是内核态的还是用户态的。
详细文档:
延迟分析:选择事件原则:
根据分析需求,使用下方 事件选择语法 配置事件。
配置流程:
确定事件格式:参考 基本格式 选择事件类型
sys:namekprobe:func、 uprobe:func@"file"配置过滤器(可选,内核态执行): 参考 过滤器语法 和 Event_filtering.md bash
# 格式:sys:name/filter/
-e 'sched:sched_wakeup/pid>1000 && prio<10/'
-e 'sched:sched_wakeup/comm~"java*"/'
配置属性(可选,用户态处理): 参考 事件属性 选择所需属性 bash
# 格式:sys:name/filter/ATTR/ATTR/
-e 'sched:sched_wakeup//stack/' # 无过滤器,有属性
-e 'sched:sched_wakeup/pid>1000/stack/alias=wk/' # 过滤器 + 属性
配置表达式(可选,复杂计算): 参考 expr.md bash
# 1. 查看expr帮助
perf-prof expr -h
# 2. 属性值使用表达式
-e 'sched:sched_stat_runtime//top-by=(runtime/1000)/'
-e 'sched:sched_wakeup//key=(prio<100?pid:0)/'
延迟分析:配置key属性(可选,仅用于multi-trace): key属性用来关联延迟路径上的事件。只有相同key的事件才可以计算延迟。
Key选择的本质问题:你希望延迟路径上的事件如何关联在一起?
从事件上下文理解Key选择
每个事件发生时,都有两个基本上下文:
核心问题:延迟路径上,上下文是否变化?
上下文变化情况Key选择典型场景同CPU、同线程默认(按CPU)或不指定软中断、hardirq可能跨CPU、同线程-k common_pid 或 -p/ -t系统调用(线程可能迁移)跨线程、可能跨CPUkey=资源标识调度延迟、跨进程通信
Key的作用 = 在"上下文可能变化"的情况下,找到"不变的关联标识"
Key选择判断流程 text
问:延迟路径上的事件,线程上下文是否相同?
是 → 问:CPU上下文是否相同?
│
├── 是 → 不需要key(默认按CPU)
│ 例:软中断、本地定时器
│
└── 否 → key=common_pid,需要 --order
例:系统调用(线程可能迁移)
否 → 必须用"业务字段"作为key,需要 --order
问:什么东西从起点事件"传递"到终点事件?
│
├── 进程ID → key=pid相关字段
│ 例:调度(pid → next_pid)
│
├── 资源指针 → key=ptr/address
│ 例:内存分配(kmalloc.ptr → kfree.ptr)
│
└── 请求标识 → key=request_id/bio
例:I/O请求(提交 → 完成)
注意:
rundelay 不需要设置key属性,内部会自动设置syscalls 不需要设置key属性,内部会自动设置常用属性速查:
属性用途适用分析器stack启用调用栈所有支持堆栈的分析器alias=str事件别名top, multi-trace, sqlkey=EXPR关联键multi-trace, toptop-by=EXPR排序字段topptr=EXPR指针字段kmemleaksize=EXPR大小字段kmemleak, kmemprofbatch=N每N个事件立即处理需要实时处理事件时使用
完整属性列表参考 事件属性。
通用选项参数:
bash
-m, --mmap-pages # perf ringbuffer大小(页数)
-i, --interval <ms> # 输出间隔
-C, --cpus # 监控指定的cpu
-p, --pids # 监控指定的进程
-o, --output <file> # 把stdout/stderr重定向到文件
--watermark <0-100> # 唤醒perf-prof的水位,设置可降低工具自身的cpu消耗
--perins # 按每个实例独立输出统计结果(见"--perins:按实例统计"小节)
根据分析器类型选择执行流程:
延迟分析采用 渐进式分析,从统计概览逐步深入到细节:
步骤1:基础统计(了解延迟分布)
bash
# 先执行基础统计,周期性输出延迟分布
perf-prof <profiler> [事件选项] -i 1000 --order
# 示例:系统调用延迟统计
perf-prof syscalls -e raw_syscalls:sys_enter -e raw_syscalls:sys_exit -i 1000
# 示例:调度延迟统计
perf-prof rundelay -e sched:sched_wakeup*,sched:sched_switch -e sched:sched_switch -p <pid> -i 1000
# 示例:块设备IO延迟统计
perf-prof blktrace -d /dev/sda -i 1000
解读输出:关注延迟分布(min/avg/max)、P99等百分位数,确定异常阈值。重点关注max等大的延迟毛刺。
阈值设置原则:
步骤2:阈值过滤(聚焦异常事件)
bash
# 根据步骤1的统计结果,设置--than参数过滤超过阈值的事件,避免设置的阈值过小造成大量输出。
perf-prof <profiler> [事件选项] -i 1000 --order --than <threshold>
解读输出:统计异常事件数量和分布,确认问题严重程度。
步骤3:细节跟踪(定位根因)
bash
# 加上--detail参数,筛选并输出异常事件范围内的中间事件
perf-prof <profiler> [事件选项] -i 1000 --order --than <threshold> --detail=<samecpu>
解读输出:分析每个异常事件的时间戳、进程、堆栈等信息,定位根因。
步骤4:丰富细节(定位根因)
bash
# 针对multi-trace、syscalls、rundelay:加上untraced事件,打开堆栈,还原延迟范围内的中间细节
perf-prof <profiler> -e <event> -e <event,event//untraced/stack/> -i 1000 --order --than <threshold> --detail=<samecpu>
解读输出:分析延迟范围的中间细节,定位根因。
bash
# 直接执行,周期性输出统计结果
perf-prof <profiler> [事件选项] -i 1000 [其他选项]
text
分析输出:
├── 问题定位清晰 → 完成分析
├── 出现事件丢失(stderr出现:lost xx events on) → 调整-m参数增加ringbuffer,或使用-o输出到文件避免终端瓶颈,重新执行
├── 延迟分析需要深入 → 按6.1流程逐步添加--than和--detail参数
├── 需要更细粒度 → 调整选项参数/过滤器/阈值,重新执行
├── 输出量太大 → 立即结束命令,调整选项参数/增加过滤器/调大阈值,重新执行
├── 需要不同视角 → 返回第二步,选择其他分析器
| └── 分析延迟根因 → multi-trace
└── 需要关联分析 → 使用multi-trace进行联合分析
选项参数分为三类:
-h 查看以下记录几个关键通用选项的使用技巧。
--watermark <0-100> 控制内核何时唤醒 perf-prof 读取 ringbuffer。值为 ringbuffer 大小的百分比,写满该比例时唤醒用户态。
核心权衡:实时性 vs CPU开销
值行为适用场景0每个事件立即唤醒需要实时处理事件的场景1-25低水位,较快唤醒对时效有一定要求50(默认附近)平衡一般统计分析75-100高水位,批量唤醒高频事件聚合统计,降低 perf-prof 自身 CPU 消耗
什么场景需要实时处理事件(--watermark 0):
/proc/<pid>/cmdline、 /proc/<pid>/status),进程可能随时退出什么场景不需要实时处理:
与 batch=N 事件属性的关系:
--watermark 是全局选项,对所有事件生效,按 ringbuffer 字节量控制唤醒batch=N 是 per-event 属性,按事件个数控制唤醒(如 batch=1 表示每个事件立即处理)batch=N,该事件不受 --watermark 控制,独立按事件计数唤醒--watermark 降低开销,个别需要实时处理的事件用 batch=1--order 启用基于时间戳的事件排序。多 CPU 系统上,不同 CPU 的 ringbuffer 独立投递,事件到达用户态可能乱序。启用排序会引入一定的性能开销,非必要不使用。
排序原理: --order 不引入额外的缓冲区,而是直接在各 CPU 的 ringbuffer 上做堆排序。当某个 ringbuffer 被唤醒时,以该 ringbuffer 最新事件的时间戳作为排序边界,将所有 ringbuffer 中早于该时间的事件按序投递给分析器。因此排序的时效性取决于 --watermark 的配置——水位越低,排序边界推进越快。
何时需要 --order:
排序的核心判断依据: 事件是否需要时序分析,且可能来自不同的 ringbuffer(实例)。
场景是否需要原因事件需要时序分析,跨多实例必须不同 ringbuffer 投递可能乱序事件需要时序分析,单实例(-C 0 或 -t tid)不需要单 ringbuffer,天然有序事件相互独立,用于计数/聚合/统计不需要不依赖事件顺序
判断流程:
text
问:事件是否需要时序分析(延迟配对、因果顺序、按时间打印)?
│
├── 否 → 不需要 --order
│ 例:计数、聚合、独立事件统计
│
└── 是 → 问:事件是否可能来自不同的 ringbuffer(多实例)?
│
├── 否(单实例:-C 0、-t tid) → 不需要 --order
│
└── 是(多 CPU 或多线程) → 需要 --order
原则: blktrace、 kvmmmu、 task-state 默认启用排序,无需手动指定。
-m <pages> 设置 perf ringbuffer 大小,单位为页(4KB/页)。值必须为 2 的幂次方。
注意: 这是每个实例分配的页数。实例由 -C(CPU)或 -p/ -t(进程/线程)决定(详见"--perins:按实例统计")。总内存消耗 = 实例数 × -m × 4KB。
核心权衡:perf-prof 自身内存消耗 vs 事件丢失
值内存/实例适用场景14KB极低频事件,节省内存832KB低频事件(每秒几十个)32128KB一般场景128512KB高频事件(每秒数千个)256+1MB+突发大量事件、带堆栈的事件
调整原则:
当 stderr 出现事件丢失警告时,需要增大 -m 值:
profiler: lost N events (total)profiler: lost N events on CPU #X 或 profiler: lost N events on thread #X应对事件丢失的手段:
-m 值(翻倍尝试:32 → 64 → 128)-o 输出到文件,避免终端成为瓶颈--watermark 加速消费--perins 是 "per instance" 的缩写,启用后会显示每个实例的独立统计结果。
实例的含义: 由 -C(CPU)或 -p/ -t(进程/线程)决定:
-C 0-3),每个 CPU 是一个实例,显示每个 CPU 的统计-p/ -t),每个线程是一个实例,显示每个线程的统计默认行为: 不指定 --perins 时,所有实例的数据汇总为一个统计结果输出。
适用场景:
bash
EVENT,EVENT,...
EVENT: sys:name[/filter/ATTR/ATTR/.../]
profiler[/option/ATTR/ATTR/.../]
kprobe:func[/filter/ATTR/ATTR/.../]
kretprobe:func[/filter/ATTR/ATTR/.../]
uprobe:func@"file"[/filter/ATTR/ATTR/.../]
uretprobe:func@"file"[/filter/ATTR/ATTR/.../]
filter: trace events filter
==, !=, <, <=, >, >=, &==, !=, ~(通配符)&&, ||, ()stack - 为指定的事件打开堆栈max-stack=int - 指定堆栈的深度alias=str - 事件别名,只用于 hrcount, hrtimer, multi-trace, num-dist, top, sqlbatch=N - 每批处理N个事件,N=1表示每个事件立即处理,默认使用watermark策略。用于需要实时处理事件的场景(如读取短命进程的/proc信息)cpus=cpu[-cpu] - 指定事件Attach到单独的cpu列表,与 -C, --cpus 指定的cpu列表取交集top-by=EXPR - 增加top显示字段,参与输出排序,只用于 toptop-add=EXPR - 增加top显示字段,不用于输出排序,只用于 topcomm=EXPR - 计算显示的进程名,只用于 topptr=EXPR - 计算内存分配返回的指针,用于内存分配事件,只用于 kmemleaksize=EXPR - 计算内存分配的字节,用于内存分配事件,只用于 kmemleak, kmemprofnum=EXPR - 计算分析数值分布的数值字段,只用于 num-distkey=EXPR - 设置关联键字段,只用于 multi-trace, topprintkey=EXPR - 打印key值,使用'key'作为变量,只用于 multi-trace, top(如:printkey=printf("%d",key))role=EXPR - 计算事件的角色,延迟关系中作为起始事件还是结束事件,Bit0置位作为起始事件,Bit1置位作为结束事件,同时置位作为中间事件,只用于 multi-tracevm=uuid - 指定事件来着某个虚拟机,通过uuid指定,只用于事件传播push= - 指定事件广播的位置,只用于事件传播。 [IP:]PORT 指定事件广播的服务端, chardev 事件写到字符设备(如: /push="/dev/virtio-ports/g.|qemu.perf0"/), file 事件写入文件pull= - 指定事件接受的位置,只用于事件传播。 [IP:]PORT 指定接收事件的服务端, chardev 接收事件的字符设备, file 接收事件的文件index=field - 指定 SQL的索引字段,只用于 sqlEXPR - 计算表达式,使用事件的字段作为变量,在用户态计算。执行 perf-prof expr -h 获得帮助perf-prof <profiler> -h 查看帮助kprobe_events.md 或 uprobe_events.md详细的分析器文档在 references/ 目录: