2. 性能瓶颈分析工具:perf、ftrace、trace-cmd、PX4内置的perf_counter

做嵌入式开发,尤其是飞控这种实时系统,最怕什么?

怕程序跑着跑着突然卡一下,怕某个任务超时导致飞机抖一下,怕内存泄漏把堆栈撑爆。我见过太多团队,代码写完了,飞起来也能飞,但就是「手感不对」——延迟忽高忽低,日志里偶尔蹦个 watchdog 复位。这时候,你就需要一把趁手的「手术刀」。

这一章,我就带你把这四把刀磨快:perfftracetrace-cmd,还有 PX4 自己带的 perf_counter。它们各有各的脾气,但组合起来,几乎没有查不出的性能问题。

核心观点:性能分析不是玄学,是「测量-定位-优化」的闭环。工具只是手段,关键是知道什么时候用哪个。

性能瓶颈分析工具 perf ftrace trace-cmd PX4 perf_counter CPU采样 · 硬件计数器 函数级热点分析 内核函数跟踪 调度延迟分析 ftrace 的封装 更方便的记录/回放 PX4 内置计数器 任务执行时间/次数 从「系统级」到「任务级」全覆盖

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