如何通过 Systrace 图表识别出由 Binder 调用引起的延迟?

解读

国内面试中,Systrace 是验证“真做过性能优化”的试金石。Binder 作为 Android 最高频的 IPC 通道,一旦阻塞会直接拉长应用主线程的“一帧”耗时,导致 16 ms 红线被击穿。面试官想确认两点:

  1. 你能否在一张混杂着 CPU、SurfaceFlinger、system_server 的 Systrace 里一眼定位 Binder;
  2. 你能否把 Binder 延迟与上层卡顿因果链串起来,而不是只报“Binder 很耗时”。
    因此回答要体现“找得到、说得清、改得了”。

知识点

  1. Binder 在 kernel 层的 trace 事件:
    binder_transaction(发起端)、binder_transaction_received(接收端)、binder_command/binder_return(ioctl 进出)。
  2. atrace 用户态标签:
    binder_driver(kernel)、binder_lock(阻塞点)、aidl/hwbinder(Framework 层)。
  3. 主线程被“epoll”或binder_thread_read 挂起时,sched 切片颜色为浅绿(Sleep),唤醒点标注binder_reply
  4. 关键指标:
    ① 端到端 latency = binder_transaction_received 时间戳 − binder_transaction 时间戳;
    ② 排队等待时间 = binder_transaction 到目标进程被唤醒的间隔;
    ③ 单次 ioctl 耗时 > 500 µs 即需警惕,> 2 ms 可判定为异常。
  5. 国内 ROM 常开atrace_categories = "binder_driver,am,view,sched,irq,workq",抓 trace 前先adb shell setprop debug.atrace.tags.enableflags 0x1BF,否则 Binder 事件可能被裁剪。
  6. 与 GC、锁竞争区分:GC 的线程状态为“WaitingForGCToComplete”,锁竞争为“monitor contention”,而 Binder 一定伴随binder_transaction 事件。

答案

步骤化给出面试官想听的“现场破案”过程:

  1. 复现与抓图
    在出现卡顿的用例上执行
    adb shell atrace --async_start -b 32768 binder_driver am view sched gfx
    操作完成后
    atrace --async_stop -o /data/local/tmp/binder_delay.trace
    拖回 PC 用 perfetto.devAndroid Studio CPU Profiler 打开。

  2. 快速定位主线程掉帧
    在“Frames”轨道找到红色掉帧帧号,记其 VSYNC 时间戳区间 [t0, t0+16.6 ms]。

  3. 搜索 Binder 事件
    在 kernel 轨道过滤 binder_transaction,若发现主线程在 [t0, t0+16.6 ms] 内发出 binder_transaction 但对应的 binder_transaction_received 落在 t0+10 ms 之后,即可怀疑 Binder 延迟。

  4. 确认阻塞侧
    查看目标进程(通常是 system_server 或 mediaserver)在同一 transaction ID 上的 binder_transaction_received 是否延迟,若目标进程在排队(binder_work 堆积)或线程池耗尽(binder_thread_read 无空闲线程),则排队时间为“元凶”。

  5. 量化耗时
    用 Perfetto SQL:

    SELECT slice.name, slice.ts, slice.dur
    FROM slice
    WHERE slice.name LIKE 'binder_transaction%'
    AND slice.ts BETWEEN <t0> AND <t0+16600000>;
    

    计算 dur 列,若 > 2 ms 即异常。

  6. 给出调优方向
    ① 业务侧:把阻塞型 Binder 调用(如 PKMS 查权限、SettingsProvider 读配置)挪到异步线程或缓存结果;
    ② 系统侧:若排队严重,可建议厂商调高 ro.config.binder_thread_count(默认 15→31)或把热点 aidl 接口改为 oneway
    ③ 监控侧:线上用 bionic_malloc_dispatch + libbinder 插桩,把 > 1 ms 的 transaction 采样上报,结合 traceId 还原现场。

一句话总结:在 Systrace 里先框定掉帧区间,再按 binder_transactionbinder_transaction_received 的时间差找异常 transaction,最后看对端进程是否排队或线程池耗尽,即可坐实 Binder 引起的延迟。

拓展思考

  1. 国内厂商对 Binder 驱动做过深度定制(如小米“MiBinder”、华为“LiteIPC”),trace 事件名可能带厂商前缀,抓图前需 grep binder /sys/kernel/debug/tracing/available_events 确认实际关键字。
  2. Android 14 引入 libbinder_debug.so,支持把 transaction 调用栈直接打进 trace,可在 adb shell setprop debug.binder.callstack 1 后抓到 Java/Kotlin 层业务方法,直接关联到业务代码,无需再靠人工对照 pid/tid。
  3. 对端进程是 Native 服务时,需同时打开 halgfx 标签,才能把 HIDL::IMapper::getBuffer 这类 hwbinder 延迟与上层掉帧串联,避免“只看到 system_server 很忙却找不到源头”。
  4. 线上若无法抓 Systrace,可用 bpftrace 一行脚本采样 kernel binder_transaction 耗时,回传后离线对照 mapping.txt 还原 aidl 接口,实现“无 trace 也能定性”。