第4章 通过Systrace/Perfetto捕获合成帧时序:trace标签、SurfaceFlinger主线程、HWC线程、BufferQueue、vsync信号

4.1 异常现象描述

当合成帧率出现异常时,典型的外部表现包括:界面滑动卡顿、动画掉帧、帧率低于预期(如60 fps降至30 fps以下)、或帧率不规则抖动。在Systrace/Perfetto中,这些现象会体现为:

  • SurfaceFlinger主线程的“onFrameAvailable”或“doComposition”持续时间过长。
  • HWC线程的“hwc_set”或“present”阶段明显滞后于vsync。
  • BufferQueue的“dequeueBuffer”/“queueBuffer”出现等待或阻塞。
  • vsync信号偏移、丢失或频率异常(如SF vsync与HW vsync不同步)。
⚠️ 常见误判: 仅看帧率平均值不足以定位问题,必须通过trace查看每个合成周期内的微观时序,才能区分瓶颈在SurfaceFlinger主线程、HWC还是BufferQueue。

4.2 排查思路

使用Systrace/Perfetto抓取一段包含问题的trace(建议持续时间2~5秒),沿着vsync→SurfaceFlinger→HWC→BufferQueue的数据流逐级分析。推荐按以下故障树进行:

故障树(Fault Tree)
┌─ 帧率异常 ──────────────────────────────┐
│                                          │
├─ vsync信号是否稳定?                      │
│   └─ 否 → 检查HW vsync中断、SF vsync偏移  │
│   └─ 是 → 继续                            │
├─ SurfaceFlinger主线程耗时是否超限?       │
│   └─ 是 → 检查doComposition内部耗时       │
│   └─ 否 → 继续                            │
├─ HWC present耗时是否 >1帧时间?           │
│   └─ 是 → 检查HWC驱动或合成策略           │
│   └─ 否 → 继续                            │
├─ BufferQueue是否有dequeue阻塞?           │
│   └─ 是 → 检查BufferQueue容量或生产者速度  │
│   └─ 否 → 排查其他组件或驱动层问题        │
└──────────────────────────────────────────┘
  

4.3 关键判断规则

观测指标 正常参考值 异常阈值 判断规则
vsync period 16.67 ms (60Hz) 变异 >2ms 或出现缺失 检查硬件vsync频率和SF vsync偏移
SF主线程“doComposition” < 8 ms > 14 ms 合成耗时接近或超过vsync周期,导致掉帧
HWC“present” < 5 ms > 12 ms HWC驱动或硬件带宽不足
BufferQueue“dequeueBuffer” < 2 ms > 10 ms 或阻塞 消费者(SF/HWC)速度慢或队列满
💡 技巧: 在Perfetto UI中,选中事件后可在底部“Details”面板直接看到持续时间和线程状态。利用“R”标签区分Running、Runnable、Sleeping,可快速发现等待原因。

4.4 采集方法

使用Systrace或Perfetto抓取合成帧时序,按以下步骤操作:

  1. 连接设备并重置进程: adb shell "kill -9 $(pidof surfaceflinger)" 后立即启动抓取(确保未缓存旧状态)。
  2. 命令行采集(推荐Perfetto):
    # 快速抓取5秒,包含surfaceflinger和hwc相关类别
    adb shell perfetto \
      -c - --txt \
      -o /data/misc/perfetto-traces/trace_issue.pb \
      <<< '{
        "buffers": { "size_kb": 102400 },
        "duration_ms": 5000,
        "data_sources": [
          { "config": { "name": "linux.sys_events" } },
          { "config": { "name": "linux.ftrace", "target_events": "power/vsync*" } },
          { "config": { "name": "android.surfaceflinger.frametimeline" } },
          { "config": { "name": "android.hwcomposer" } }
        ]
      }'
    adb pull /data/misc/perfetto-traces/trace_issue.pb .
  3. Systrace替代命令:
    python systrace.py \
      -t 5 \
      -o trace.html \
      -a surfaceflinger \
      -e "gfx input view sched freq idle res"
  4. UI抓取: 使用Perfetto UI(https://ui.perfetto.dev)的“Record new trace”功能,勾选“SurfaceFlinger”、“HWComposer”、“BufferQueue”、“vsync”标签,点击“Start recording”后操作重现问题。
⚠️ 注意: 抓取时确保设备CPU频率不因温控而降低,否则trace中会出现大量CPU空闲导致误判。建议在抓取前锁定CPU频率或置于散热环境。

4.5 修复步骤

根据trace分析发现的具体瓶颈,采取对应修复:

定位问题 典型修复动作
vsync偏移/不稳定 检查硬件vsync源(如MIPI panel配置);调整 vendor.display.vsync_offset;升级显示驱动。
SF主线程doComposition过慢 减少Layer数量;禁用不必要的Blend;使用GPU合成(GPU composition)代替CPU;优化Client Composition。
HWC present阻塞 降低HWC合成层数;检查writeback带宽;升级HWC HAL版本或调整帧率调度策略。
BufferQueue频繁阻塞 增大BufferQueue容量(maxDequeuedBufferCount);优化生产者的渲染速度;减少缓冲区分配次数。
SF vsync与HW vsync不同步 配置 sf_primary_display_vsync_level;关闭SF自带的偏移补偿或使用硬件vsync直通。
案例修复示例: 某设备出现每帧doComposition耗时20ms,trace显示大量Layer且使用CPU合成。修复:在 surfaceflinger.rc 中添加 setprop debug.sf.gpucomposition 1 强制GPU合成,并合并非必要Layer,合成耗时降至3ms,帧率恢复60fps。

4.6 复测要点

  • 使用同样的抓取脚本再次采集trace,对比修复前后的关键耗时:SF主线程、HWC present、BufferQueue dequeue。
  • 观察vsync信号是否恢复稳定周期(变异<1ms)。
  • 使用 dumpsys gfxinfo 或 dumpsys SurfaceFlinger 查看帧率统计确认掉帧率为0。
  • 在高压场景(如多窗口、高帧率游戏)下重复测试至少3次,确保修复没有引入新问题。

4.7 预防建议

  1. 建立持续trace基线: 在每个版本发布前自动抓取一段标准场景trace,提取关键时序指标存入数据库,当指标超出基线时自动告警。
  2. 控制Layer数量: App层和SystemUI应限制悬浮层、动画层数量,SurfaceFlinger中设置 maxVirtualDisplayLayers 等参数。
  3. 使用TraceProcessor分析: 编写SQL脚本定期扫描Perfetto trace,检查是否出现长时间doComposition或HWC present超时。
  4. vsync健康检测: 在SurfaceFlinger启动日志中记录vsync源频率和偏移,若检测到异常自动切换至备用vsync源。
Systrace/Perfetto 捕获合成帧时序关键节点 vsync 信号 (HW vsync / SF vsync) SurfaceFlinger 主线程 (doComposition) HWC 线程 (present/set) BufferQueue (dequeue/queue/latch) App 生产者 latchBuffer queueBuffer trace 关键标签 • SurfaceFlinger::onFrameAvailable • hwc_set / present • BufferQueue::dequeueBuffer • vsync_event / vsync_offset
🎯 使用提示: 在上方SVG中,虚线表示vsync驱动同步,实线表示数据/命令流。实际分析trace时重点关注每个节点之间的延时差异,尤其检查vsync信号到SF开始处理的时间间隔(若大于2ms则可能是调度延迟)。