2. 性能瓶颈分析工具:perf、ftrace、trace-cmd、PX4内置的perf_counter
做嵌入式开发,尤其是飞控这种实时系统,最怕什么?
怕程序跑着跑着突然卡一下,怕某个任务超时导致飞机抖一下,怕内存泄漏把堆栈撑爆。我见过太多团队,代码写完了,飞起来也能飞,但就是「手感不对」——延迟忽高忽低,日志里偶尔蹦个 watchdog 复位。这时候,你就需要一把趁手的「手术刀」。
这一章,我就带你把这四把刀磨快:perf、ftrace、trace-cmd,还有 PX4 自己带的 perf_counter。它们各有各的脾气,但组合起来,几乎没有查不出的性能问题。
核心观点:性能分析不是玄学,是「测量-定位-优化」的闭环。工具只是手段,关键是知道什么时候用哪个。
2.1 perf:系统级性能采样利器
perf 是 Linux 内核自带的性能分析工具。说白了,它就是个「系统级采样器」——每隔一段时间打断 CPU,看看当前在跑什么函数,然后统计出热点。
我个人习惯,拿到一个新板子,先跑一遍 perf top。这就像给系统做个体检,看看 CPU 时间都花在哪了。
# 在飞控终端上执行
perf top -a -g
# 或者采集一段时间的数据
perf record -a -g -- sleep 10
perf report
这里的关键参数:
-a:采集所有 CPU 核心-g:开启调用链(callchain),能看到谁调了谁-- sleep 10:采集 10 秒
我的经验:在 PX4 上跑 perf 时,记得先关掉一些不必要的后台服务。我遇到过好几次,perf 报告里排第一的居然是 WiFi 驱动的中断处理函数——那玩意儿在飞控上根本用不着。
perf 能告诉你什么?比如:
- 哪个函数占 CPU 最多?
- 中断处理花了多少时间?
- 缓存 miss 率高不高?
但 perf 也有短板。它只能看到「采样点」上的信息,如果某个函数执行时间很短但频率极高,perf 可能采样不到。这时候就需要 ftrace 出马了。
2.2 ftrace:内核跟踪的瑞士军刀
ftrace 是 Linux 内核内置的跟踪框架。它不像 perf 那样「猜」,而是直接记录函数的进入和退出时间。精度能达到纳秒级。
我曾经排查过一个诡异的「周期性抖动」问题:飞控每 5 秒卡顿一次,持续 200 微秒。perf 根本看不出什么,因为采样率不够。后来用 ftrace 跟踪了调度器函数,才发现是某个驱动在定时刷写 EEPROM,把调度延迟拉高了。
ftrace 的使用方式:
# 挂载 tracefs
mount -t tracefs tracefs /sys/kernel/tracing
# 设置要跟踪的函数
echo function_graph > /sys/kernel/tracing/current_tracer
echo schedule > /sys/kernel/tracing/set_ftrace_filter
echo 1 > /sys/kernel/tracing/tracing_on
# 等待几秒后关闭
echo 0 > /sys/kernel/tracing/tracing_on
cat /sys/kernel/tracing/trace
输出大概长这样:
2) | schedule() {
2) 0.123 us | __schedule();
2) 0.456 us | pick_next_task_fair();
2) 0.789 us | context_switch();
2) 1.234 us | }
每一行都精确到微秒。你能看到每个子函数花了多少时间,谁调了谁,一目了然。
注意:ftrace 的开销不小。如果跟踪的函数太多,反而会影响系统性能。我建议只跟踪你怀疑的那几个函数,别一股脑全开。
2.3 trace-cmd:ftrace 的友好封装
说实话,直接操作 ftrace 的 sysfs 接口有点麻烦。trace-cmd 就是来解决这个问题的——它把 ftrace 的配置、启动、停止、导出都封装成了命令行。
我个人觉得,trace-cmd 最大的好处是:
- 不用记一堆 sysfs 路径
- 支持图形化查看(用 kernelshark)
- 可以远程录制,本地分析
# 录制 5 秒的调度事件
trace-cmd record -e sched_switch -e sched_wakeup -o trace.dat sleep 5
# 查看结果
trace-cmd report trace.dat
你想想看,在飞控上跑 trace-cmd record,然后把 trace.dat 文件拷贝到电脑上,用 kernelshark 打开——所有任务的切换、唤醒、阻塞时间都可视化呈现。哪个任务抢占了 CPU,哪个任务在等锁,一眼就能看出来。
避坑指南:trace-cmd 生成的 trace.dat 文件可能很大。5 秒的录制,如果事件很多,轻松上百 MB。飞控的存储空间有限,记得控制录制时长,或者用 -b 参数限制缓冲区大小。
2.4 PX4 内置的 perf_counter
前面三个工具都是 Linux 层面的。但 PX4 自己也有个轻量级的性能计数器——perf_counter。它不依赖任何外部工具,直接在代码里埋点。
为什么需要它?因为有些性能问题,只有 PX4 内部才知道。比如:
- uORB 消息的发布/订阅延迟
- 某个模块的执行周期是否稳定
- 任务栈的使用峰值
在 PX4 代码里,perf_counter 的使用非常简单:
// 声明一个计数器
perf_counter_t my_counter = perf_alloc(PC_ELAPSED, "module:my_func");
// 在函数开始/结束处打点
perf_begin(my_counter);
// ... 你的业务逻辑 ...
perf_end(my_counter);
// 打印统计结果
perf_print_counter(my_counter);
输出示例:
module:my_func: 12345 us, 1000 calls, avg 12 us, min 8 us, max 45 us
你看,平均执行时间、最小最大、调用次数,全都有了。而且开销极低——perf_begin/end 本质上只是读了个硬件时间戳,加了个原子操作。
我的习惯:在 PX4 的每个关键模块(如 EKF、导航、姿态控制)的入口和出口都加上 perf_counter。平时不打印,只在调试时通过 perf print 命令查看。这样既不影响性能,又能随时掌握模块的健康状况。
2.5 四把刀怎么选?
嗯,到这里你可能有点晕:四个工具,到底用哪个?
我一般这样决策:
| 场景 | 推荐工具 | 原因 |
|---|---|---|
| CPU 热点分析(哪个函数最耗 CPU) | perf | 采样效率高,开销小 |
| 内核调度延迟、中断延迟 | ftrace / trace-cmd | 精确到纳秒,能看到完整调用链 |
| PX4 模块内部性能 | perf_counter | 轻量、无依赖、可长期开启 |
| 复杂问题交叉分析 | perf + trace-cmd 组合 | 一个看宏观,一个看微观 |
说白了,perf 适合「大海捞针」——先找到可疑的模块;ftrace 适合「顺藤摸瓜」——沿着调用链找到根因;perf_counter 适合「日常体检」——长期监控模块健康度。
我记得有一次,一个客户反馈飞机在悬停时偶尔会「点头」。perf 一看,EKF 模块的 CPU 占用忽高忽低;ftrace 一追,发现是磁力计驱动在 I2C 总线上被高优先级任务抢占了;最后用 perf_counter 确认,EKF 的执行周期从 4ms 抖到了 12ms。三个工具,各司其职,半小时定位问题。
所以,别嫌工具多。每个工具都有它最擅长的战场。你只要记住:先宏观,再微观;先系统,再模块。这个思路,比背一百条命令都管用。
公众号:蓝海资料掘金营,微信deep3321