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 时钟,都应以同一参考点(如内核启动时刻)换算。
六、一个可复用的采集流程
把上述工具串成流程,建议按以下顺序执行。第一步,用串口打点获得全链路粗粒度时间轴,确认各阶段大致占比。第二步,对可疑阶段用 GPIO 翻转做高精度复测,确认边界时间。第三步,在内核阶段开启 ftrace 与 initcall_debug,导出 trace 后用 bootgraph 生成可视化。第四步,在用户空间运行 systemd-analyze time/blame/critical-chain,定位慢 unit 与关键路径。第五步,把四份数据按统一时间基准对齐,形成一张“阶段—耗时—工具—证据”的对照表。
| 阶段 | 推荐工具 | 典型分辨率 | 主要观察对象 |
|---|---|---|---|
| BootROM / Bootloader | 串口打点、GPIO 翻转 | 百微秒 / 微秒 | DDR 初始化、镜像加载 |
| ATF / OP-TEE | GPIO 翻转 | 微秒 | 安全世界切换、电源域 |
| Linux 内核 | ftrace、bootgraph | 微秒 | initcall、驱动探测 |
| initramfs | 串口打点、ftrace | 毫秒 | 根文件系统挂载 |
| systemd 用户空间 | systemd-analyze | 毫秒 | unit 耗时、依赖链 |
最后强调一点:测量本身会扰动系统。串口打点越密,扰动越大;ftrace 的函数图模式会显著增加开销;systemd-analyze 在采集时也会改变服务调度。因此,优化前的基线测量应尽量轻量,优化后的验证测量可以更详细。两者使用不同工具组合是合理的,但必须记录清楚每次采集的工具、参数和环境,否则数据无法对比,优化也就失去了方向。