PX4-Autopilot 性能剖析实战:使用 Poor Man‘s Sampling Profiler(PMSP)与火焰图定位飞控瓶颈
2026/9/24 16:50:17 网站建设 项目流程
  • 嵌入式
  • 物联网
  • 机器人
  • 自动驾驶
  • 智能硬件

【免费下载链接】PX4-Autopilot

PX4 Autopilot Software

项目地址:https://gitcode.com/gh_mirrors/px/PX4-Autopilot
点击查看免费下载

本文介绍 PX4-Autopilot 内置的Poor Man's Sampling Profiler(PMSP)采样剖析方案:它通过周期性地中断固件执行并采样当前调用栈,经"折叠(folding)"与 FlameGraph 的采样—折叠—绘图三级流水线原理。

方法概述(Approach)

PMSP 的本质是一个shell 脚本,它实现了一种经典的低成本采样剖析方法(该方法最初由 Mark Callaghan 与 Domas Mituzas 提出,即"poor man's profiler")。其工作流程分为三个阶段:

  1. 采样(Sampling):脚本周期性地通过 GDB 中断(halt)目标固件的执行,抓取当前时刻的调用栈(stack trace),并把每次抓到的栈信息追加写入一个文本文件。由于采样是"随机打点",采样次数越多、采样间隔越均匀,统计结果就越接近真实的 CPU 时间分布。
  2. 折叠(Folding):采样结束后(通常需要一个小时甚至更久),脚本把收集到的所有调用栈"折叠"成另一份文本文件——所有相同的调用栈路径被合并,并记录其出现次数。折叠结果等价于"每个调用路径被采样命中多少次",这正是火焰图绘制的输入数据。
  3. 可视化(Graphing):折叠后的栈数据被送入FlameGraph(Brendan Gregg 开源的调用栈可视化工具)生成交互式 SVG 火焰图。

整个三级流水线的入口是仓库中的 poor-mans-profiler.sh,它位于platforms/nuttx/Debug/目录下,与Nuttx.pygdbinit.in等调试辅助文件同处一个目录。

基本使用(Basic Usage)

前置条件(Prerequisites)

由于剖析依赖 GDB 在嵌入式目标上运行 PX4,开始之前需要准备齐以下三样东西:

  • 待剖析的硬件:例如一块 Pixhawk 4 / Pixhawk 6X 等飞控板;
  • 已编译并烧录的固件:先在目标硬件上编译并上传 PX4 固件(见 boards 下各板卡目录的构建配置);
  • 调试探针(debug probe):用于运行 GDB server 并与板卡交互。官方文档推荐 Zubax BugFace BF1 这类基于 Black Magic Probe(BMP)的调试器。更多探针选择与接线说明可参考 SWD 调试文档。

确定调试器设备号(Determine the Debugger Device)

当使用Zubax BugFace BF1(Black Magic Probe)时,poor-mans-profiler.sh 会自动检测并使用正确的 USB 设备。从脚本源码(第 59 行)可以看到其默认逻辑:

gdbdev=`ls /dev/serial/by-id/*Black_Magic_Probe*-if00`

即脚本自动枚举/dev/serial/by-id/下匹配*Black_Magic_Probe*-if00的设备。如果使用其他类型的探针,则需要通过--gdbdev参数显式指定调试器所在的设备节点。

在 Ubuntu 上,可用如下命令枚举可能的设备:

ls -alh /dev/serial/by-id/

下面示例展示了同时连接 Pixhawk 4 与 Zubax BugFace BF1 时的枚举结果:

user@ubuntu:~/PX4-Autopilot$ ls -alh /dev/serial/by-id/ total 0 drwxr-xr-x 2 root root 100 Apr 23 18:57 . drwxr-xr-x 4 root root 80 Apr 23 18:48 .. lrwxrwxrwx 1 root root 13 Apr 23 18:48 usb-3D_Robotics_PX4_FMU_v5.x_0-if00 -> ../../ttyACM0 lrwxrwxrwx 1 root root 13 Apr 23 18:57 usb-Black_Sphere_Technologies_Black_Magic_Probe_BFCCB401-if00 -> ../../ttyACM1 lrwxrwxrwx 1 root root 13 Apr 23 18:57 usb-Black_Sphere_Technologies_Black_Magic_Probe_BFCCB401-if02 -> ../../ttyACM2

在此例中,脚本会自动选中名为*Black_Magic_Probe*-if00的设备(即ttyACM1)。若你使用的是其他调试器,可从上面的列表中找出对应的 id,并通过--gdbdev传入:

./poor-mans-profiler.sh --elf=build/px4_fmu-v4_default/px4_fmu-v4_default.elf --nsamples=30000 --gdbdev=/dev/ttyACM2

一键运行(Running)

PMSP 最便捷的入口是 PX4 的构建系统。下面这条命令会构建px4_fmu-v4pro目标并执行 10000 次采样(脚本会自动获取FlameGraph并加入PATH,如需自定义采样次数等更细粒度控制,见下文"实现原理(Implementation)"一节):

make px4_fmu-v4pro_default profile

脚本源码头部注释也给出了完整的使用路径(以 Pixhawk 4 / fmu-v5 为例):

# 0) 准备 flamegraph.pl(见下文 PATH 要求) # 1) 连接设备,例如 Pixhawk 4 # 2) 构建 PX4: make px4_fmu-v5_default # 3) 烧录 PX4: make px4_fmu-v5_default upload # 4) 连接 JTAG/SWD 调试器,例如 DroneCode Probe # 5) 执行剖析: make px4_fmu-v5_default profile

理解输出结果(Understanding the Output)

采样与折叠完成后,PMSP 调用flamegraph.pl生成火焰图(FlameGraph)。下图是官方文档给出的输出示例(注意静态图片不具备交互性,实际产物为可交互的 SVG):

火焰图的阅读规则如下:

  • 水平方向:每一层由多个矩形条组成,矩形的宽度与该函数被采样命中的次数成正比。命中次数越多,说明该函数在采样周期内的执行时间(时长 × 频率)占比越高,是潜在的 CPU 热点。
  • 垂直方向:自下而上为调用栈的层次——底部是根函数(如任务入口、px4_main、工作队列调度),顶部是叶子函数(被层层调用的具体实现)。栈越深,代表调用链越长。
  • 颜色:通常用于区分不同函数或不同栈帧,颜色本身不携带数值语义(默认配色下红色系表示高频热区、黄色系为相对低频的深层调用,可结合具体图例判断)。

因此,最宽、最长的"平顶"区域就是你需要重点审视的热点函数——它往往对应某个繁忙的任务循环或高开销的系统调用。

已知问题与注意事项(Possible Issues)

PMSP 定位为"ad-hoc"(临时应急)方案,脚本本身存在一些已知缺陷,使用时应特别留意:

  • GDB 故障可能不被察觉:如果 GDB 工作异常,脚本可能无法检测到并继续空跑,结果自然是毫无可用的栈数据。为避免浪费数小时,应周期性检查/tmp/pmpn-gdberr.log——该文件保存了最近一次 GDB 调用的 stderr 输出。脚本源码(第 103 行)将 GDB 的错误输出重定向到该文件。未来版本应改为以安静模式调用 GDB 并通过退出码判断成败。
  • GDB 偶发永久挂起:采样栈时 GDB 可能卡死,导致目标被无限期 halt。解决办法是手动中止脚本,再带--append选项重新启动(追加而非覆盖旧数据)。未来版本应给每次 GDB 调用强制加上超时。
  • 不支持多线程环境:脚本一次采样只记录单条栈。这对单核嵌入式目标没有影响(始终只有一个执行线程),但限制了它在多线程应用中的适用性。未来版本应让栈折叠器支持每次采样包含多条栈。

实现原理(Implementation)

脚本本体位于 platforms/nuttx/Debug/poor-mans-profiler.sh,其执行流程与全部可调参数都直接体现在源码中。

执行流水线

  1. 启动自检:脚本首先检查flamegraph.pl是否位于PATH中(第 43 行),否则直接报错退出——FlameGraph 脚本必须存在于PATH,否则 PMSP 拒绝启动
  2. 采样:按指定的采样次数与时间间隔执行采样,原始栈数据写入系统临时目录的文本文件。脚本默认的临时文件路径(第 100~103 行)为:
    • /tmp/pmpn-stacks.log:原始采样栈;
    • /tmp/pmpn-folded.txt:折叠后的栈;
    • /tmp/pmpn-flamegraph.svg:最终火焰图 SVG;
    • /tmp/pmpn-gdberr.log:GDB 的 stderr 输出。
  3. 折叠:采样结束后自动调用栈折叠器,输出写入临时目录中相邻的折叠文件。折叠器是脚本内嵌生成的 Python 程序/tmp/pmpn-folder.py(第 157 行起),它专门处理 C++ 类型——包括operator<operator<<operator>operator->等运算符重载符号,以及括号/尖括号/引号内的函数签名解析,避免 C++ 模板与运算符导致栈解析错乱。折叠器还会向 stderr 输出统计信息:总栈帧数(Total stack frames)以及栈顶分布 Top 300 消费函数占比Top consumers),可直接在终端看到热点排行。
  4. 绘图:折叠成功后调用flamegraph.pl --fontsize=$fgfontsize --width=$fgwidth生成交互式 SVG并自动用浏览器打开。注意并非所有图片查看器都支持交互式 SVG,建议用 Web 浏览器打开生成的 SVG(脚本会优先尝试 firefox、google-chrome,其次才是 xdg-open)。

参数详解

脚本通过--key=value形式解析参数(第 61~93 行),完整参数如下表:

参数默认值作用
--nsamples=<n>0(跳过采样)采样次数。0表示仅基于已有栈文件重新生成折叠与火焰图,不访问目标
--sleeptime=<sec>0.1两次采样之间的间隔秒数,决定总剖析时长
--taskname=<name>空(全局采样)指定 NuttX 任务名,仅采样该任务(依赖带 Python 支持的 GDB 与Nuttx.py
--elf=<file>无(必填)指向当前运行 ELF 的路径(相对仓库根目录),用于把内存地址映射为符号
--append追加到旧栈文件而非覆盖
--gdbdev=<dev>自动探测 BMPGDB 连接的目标设备节点
--fgfontsize=<px>10火焰图字体大小
--fgwidth=<px>1900火焰图宽度

采样背后的 GDB 调用

PMSP 使用arm-none-eabi-gdb采集栈(当前仅支持该工具链,未来可能扩展)。每次采样执行一次批处理式 GDB 调用(第 120~128 行):

arm-none-eabi-gdb $elf --nx --quiet --batch \ -ex "set print asm-demangle on" \ -ex "target extended $gdbdev" \ -ex "monitor swdp_scan" \ -ex "attach 1" \ -ex bt \ 2> $gdberrfile \ | sed -n 's/\(#.*\)/\1/p' \ >> $stacksfile

关键点解读:

  • --batch保证每次调用非交互、可脚本化;--nx跳过用户.gdbinit以免干扰;
  • target extended $gdbdev+monitor swdp_scan+attach 1完成对目标板的 SWD 连接与挂接;
  • bt(backtrace)抓取当前调用栈,输出经sed过滤出#...帧行后追加到栈文件;
  • 若指定--taskname,则改用source $root/Nuttx.py加载 Nuttx.py,并通过show mybt $taskname获取指定任务的调用栈(此时要求 GDB 带有 Python 支持)。

由于每次采样都要走完整的 GDB attach/backtrace 流程,频繁调用 GDB 会明显干扰目标实时任务的正常运行——因此 PMSP 不适合对强实时性任务做精准剖析;脚本注释还建议:保证 PC 不超载、调试器所连 USB 主机控制器不拥塞,并允许当前用户设置负 nice 值,以获得更均匀的采样节奏。

ELF 与追加模式

为了让 GDB 能把内存地址映射为符号名,必须通过--elf=<file>传入当前运行在目标上的可执行文件路径(相对仓库根目录)。典型用法:

./poor-mans-profiler.sh --elf=build/px4_fmu-v4_default/px4_fmu-v4_default.elf --nsamples=30000

注意:每次启动脚本都会覆盖旧栈数据。若希望保留旧栈并继续追加采样,使用--append

./poor-mans-profiler.sh --elf=build/px4_fmu-v4_default/px4_fmu-v4_default.elf --nsamples=30000 --append

不难推断:--append配合--nsamples=0时,脚本会跳过采样环节,只基于已有的/tmp/pmpn-stacks.log重新执行折叠与绘图——适合调整--fgwidth--fgfontsize反复打磨火焰图而无需再次连线采样。

小结

PMSP 为 PX4 飞控固件提供了一条"零侵入、低成本"的性能剖析路径:只需一块目标板、一个 SWD 调试探针和一份已烧录的固件,即可通过make <target> profile或直接调用 poor-mans-profiler.sh 获得调用栈级别的热点分布。其"采样 → 折叠 → 火焰图"的流水线虽朴素,却与 FlameGraph 生态无缝衔接,特别适合在真实硬件上快速定位任务循环、工作队列与驱动路径中的 CPU 瓶颈;使用时请务必留意其对实时任务的干扰、GDB 偶发故障与多线程不兼容等已知限制,并善用/tmp/pmpn-gdberr.log--append模式保证采样数据的可靠性。

  • 嵌入式
  • 物联网
  • 机器人
  • 自动驾驶
  • 智能硬件

【免费下载链接】PX4-Autopilot

PX4 Autopilot Software

项目地址:https://gitcode.com/gh_mirrors/px/PX4-Autopilot
点击查看免费下载
上一篇:如何免费解锁Grammarly Premium高级版功能?Cookie自动搜索工具详解
下一篇:免费解锁Grammarly Premium高级版:三步实现专业写作助手

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询