3. 启动时间测量方法论

在 R-Car 座舱平台上做启动优化,第一步不是改代码,而是建立一套可信、可复现、可对比的测量体系。R-Car H3/M3/E3 这类 SoC 的启动链路很长:BootROM → Bootloader(如 U-Boot/SPL)→ ARM Trusted Firmware → OP-TEE → Linux 内核 → initramfs → systemd → 图形与音频服务。不同阶段运行在不同的特权级和不同的时钟域上,单一工具无法覆盖全链路。因此本节的核心思路是:用分层工具链覆盖不同阶段,用统一的时间基准对齐数据,用可重复的采集流程保证可比性。

一、先建立时间基准:串口打点

串口打点是成本最低、覆盖最广的手段。R-Car 开发板通常把 SCIF/HSCIF 作为调试串口,波特率 115200 或 921600。打点的本质是在关键代码路径上插入带时间戳的打印,例如在 U-Boot 中:

# 在 board_init_f / board_init_r / clk_init 等关键点插入
printf("[TS] %s @ %lu us\n", __func__, timer_get_us());

内核侧则可用 printk 配合 CONFIG_PRINTK_TIME,或使用 initcall_debug 打印每个 initcall 的耗时。串口打点的优势是几乎不依赖额外硬件,缺点是串口本身有传输延迟(115200 下每字节约 87 μs),高频打点会显著扰动被测系统。因此打点应遵循两条原则:只在阶段边界打点,以及打点内容尽量短。解读时要注意,串口时间戳反映的是“打印时刻”,而非“事件发生时刻”,两者之间存在 FIFO 与中断延迟,通常为百微秒量级。

二、需要高精度时:GPIO 翻转

当需要测量微秒级甚至更细的时序,例如 DDR 初始化、时钟切换、电源域上电顺序,串口打点就不够用了。此时用 GPIO 翻转配合示波器或逻辑分析仪是最可靠的方法。在 R-Car 上可选择 PFC 复用为 GPIO 的引脚,在目标代码点执行:

/* 进入关键区 */
writel(GPIO_OUT_HIGH, GPIO_BASE + OUTDT);
/* ... 被测代码 ... */
writel(GPIO_OUT_LOW, GPIO_BASE + OUTDT);

用逻辑分析仪同时抓取多路 GPIO,就能得到各阶段之间的真实时间差,且几乎不引入软件开销。GPIO 翻转的典型用途是:验证 U-Boot 到内核的交接时间、测量内核解压耗时、确认某个外设上电到可访问的延迟。需要注意的是,GPIO 翻转测量的是“代码执行到该点”的时间,若被测代码被中断打断,测得的区间会包含中断时间,解读时要结合中断状态判断。

三、内核内部:ftrace 与 bootgraph

进入内核后,ftrace 是最强大的内建工具。R-Car 内核需开启 CONFIG_FUNCTION_TRACER、CONFIG_FUNCTION_GRAPH_TRACER、CONFIG_TRACER_SNAPSHOT 等选项。常用命令:

# 跟踪 initcall 耗时
echo 1 > /sys/kernel/debug/tracing/events/initcall/initcall_start/enable
echo 1 > /sys/kernel/debug/tracing/events/initcall/initcall_finish/enable
cat /sys/kernel/debug/tracing/trace

# 函数图跟踪,定位调用链耗时
echo function_graph > current_tracer
echo 1 > tracing_on

bootgraph 则是 ftrace 数据的上层可视化工具。它解析 initcall_debug 输出,生成内核启动阶段的时间分布图,能直观看到哪些 initcall 占用了最多时间。在 R-Car 上,常见的耗时大户包括:DDR 校准、eMMC/UFS 探测、PCIe 枚举、显示子系统(DU/VSP)初始化、以及网络 PHY 协商。bootgraph 的价值在于把“内核启动慢”这个模糊问题,拆解为“哪个驱动、哪个 initcall、耗时多少”的具体清单。

四、用户空间:systemd-analyze

当内核交给 init 之后,启动时间的主要部分往往在用户空间。systemd-analyze 提供三个层次的视图:

  • systemd-analyze time:给出 firmware、loader、kernel、userspace 四段总时间。
  • systemd-analyze blame:按耗时排序各 unit,快速定位慢服务。
  • systemd-analyze critical-chain:展示关键路径,说明“为什么总时间下不来”。

在 R-Car 座舱上,critical-chain 常暴露出图形服务、音频服务、网络服务之间的依赖串行问题。例如显示服务等待 GPU 驱动,GPU 驱动又等待电源域就绪,形成一条长链。此时优化方向不是“让某个服务更快”,而是“打破不必要的依赖,让可并行的服务真正并行”。

五、工具链协同与数据解读

单一工具只能看到局部。正确的做法是把串口打点作为时间轴骨架,用 GPIO 翻转校准关键边界,用 ftrace/bootgraph 填充内核内部细节,用 systemd-analyze 覆盖用户空间。四者时间基准需要对齐:串口时间戳、GPIO 边沿、ftrace 的 local_clock、systemd 的 monotonic 时钟,都应以同一参考点(如内核启动时刻)换算。

建议在每次采集前固定 CPU 频率、关闭非必要后台任务、保持相同环境温度,否则 DDR 校准和热降频会引入数十毫秒级波动,导致数据不可比。
R-Car 启动时间测量工具链与阶段覆盖 时间 BootROM + Bootloader ATF / OP-TEE 安全世界 Linux 内核 initcall / 驱动 initramfs 挂载根文件系统 systemd 用户空间服务 串口打点 阶段边界 · 低成本 · 全链路 GPIO 翻转 + 逻辑分析仪 微秒级 · 硬件边界 · 低扰动 ftrace / bootgraph 内核内部 · initcall 耗时 systemd-analyze blame · critical-chain 数据解读要点 串口时间戳 = 打印时刻,含 FIFO/中断延迟(约百微秒),不宜用于微秒级判断 GPIO 翻转测的是代码执行点,若被中断打断,区间会包含中断时间 ftrace 的 local_clock 与 systemd monotonic 时钟需换算到同一参考点 critical-chain 揭示依赖串行,优化重点是打破依赖而非单纯加速单个服务 采集前固定 CPU 频率、关闭后台任务,避免 DDR 校准与热降频引入波动

六、一个可复用的采集流程

把上述工具串成流程,建议按以下顺序执行。第一步,用串口打点获得全链路粗粒度时间轴,确认各阶段大致占比。第二步,对可疑阶段用 GPIO 翻转做高精度复测,确认边界时间。第三步,在内核阶段开启 ftrace 与 initcall_debug,导出 trace 后用 bootgraph 生成可视化。第四步,在用户空间运行 systemd-analyze time/blame/critical-chain,定位慢 unit 与关键路径。第五步,把四份数据按统一时间基准对齐,形成一张“阶段—耗时—工具—证据”的对照表。

阶段推荐工具典型分辨率主要观察对象
BootROM / Bootloader串口打点、GPIO 翻转百微秒 / 微秒DDR 初始化、镜像加载
ATF / OP-TEEGPIO 翻转微秒安全世界切换、电源域
Linux 内核ftrace、bootgraph微秒initcall、驱动探测
initramfs串口打点、ftrace毫秒根文件系统挂载
systemd 用户空间systemd-analyze毫秒unit 耗时、依赖链
不要用单一工具的数据下结论。串口打点看到“内核启动 2 秒”,可能其中 1.5 秒是串口打印本身的开销;systemd-analyze 看到“userspace 5 秒”,可能其中大部分是内核已就绪但服务在等待硬件。交叉验证是测量方法论的底线。

最后强调一点:测量本身会扰动系统。串口打点越密,扰动越大;ftrace 的函数图模式会显著增加开销;systemd-analyze 在采集时也会改变服务调度。因此,优化前的基线测量应尽量轻量,优化后的验证测量可以更详细。两者使用不同工具组合是合理的,但必须记录清楚每次采集的工具、参数和环境,否则数据无法对比,优化也就失去了方向。