Skip to content

binder_transaction_log

解析 Android 17 Binder 事务环形日志的写入、字段、失败副本、并发可见性和覆盖边界。

基于android-17.0.0_r1
AndroidBinder调试transaction

binder_transaction_log ​

transaction log 是有限容量的环形历史,不是当前状态表。writer 先占用 entry、填写字段,最后通过完成标记发布;reader 使用 barrier 避免读到半写入条目。下图同时显示正常、失败副本和覆盖。

本文承接 debugfs Binder节点。debugfs transaction_log 不是完整历史数据库,而是 Binder 驱动中的两个 32 项环形缓冲区:正常事务和失败事务。本文追踪 entry 的创建、完成标记、show 读取顺序和字段如何与 proc state/extended error 对齐。

1. 日志结构 ​

源码文件:kernel/common/drivers/android/binder.c

binder_transaction_log_entry 保存 debug_id、call type、from/to proc/thread、target handle/node、context、data/offset size 和错误字段。binder_transaction_log 保存原子游标 cur、full 标志和 32 个 entry;binder_transaction_log_failed 是独立失败环形缓冲区。

2. 写入流程 ​

源码文件:kernel/common/drivers/android/binder.c

c
unsigned int cur = atomic_inc_return(&log->cur);
if (cur >= ARRAY_SIZE(log->entry)) log->full = true;
e = &log->entry[cur % ARRAY_SIZE(log->entry)];
WRITE_ONCE(e->debug_id_done, 0);
smp_wmb();
memset(e, 0, sizeof(*e));

binder_transaction_log_add 用原子游标分配槽位,超过 32 项后覆盖旧 entry。先把 debug_id_done 清零再清空字段,读取端据此判断 entry 是否完整,避免并发写入时打印半条记录。

3. 正常入口 ​

事务创建早期,驱动把 debug id、call type、source、target handle、数据大小和 Binder context 写入正常 log;call type 为 0、async 为 1、reply 为 2。之后通过 binder_set_extended_error 把同一 debug id 绑定到发送线程,便于失败时查询额外 errno。

4. 完成标记 ​

成功或失败路径结束时,驱动把 return_error、参数和代码行写回 entry,最后 WRITE_ONCE(e->debug_id_done, t_debug_id)。只有完成标记发布后,读者才应把 entry 当作稳定记录;show 函数的 memory barrier 正是围绕这个字段建立的。

5. 失败副本 ​

失败事务会复制整条 entry 到 binder_transaction_log_failed,再分别发布两个 debug_id_done。因此失败 log 能保留失败现场,即使正常 log 槽位之后被新事务覆盖;但两个环形区各自只有 32 项,仍可能被高频失败覆盖。

6. 读取顺序 ​

源码文件:kernel/common/drivers/android/binder.c

transaction_log_show 读取游标,若未满从 0 开始,否则从 count % 32 开始,循环最多打印 32 项。输出按环形逻辑顺序重排,不是数组物理顺序;同一采样期间新事务仍可能覆盖槽位。

7. 字段解释 ​

输出格式:debug_id: type from pid:tid to pid:tid context ... node ... handle ... size data:offset ret error/param line。target_handle 是发送方视角的 handle,to_node 是目标 node debug id;两者不能互换。to_thread=0 可能表示尚未选定具体线程,需结合 state/proc todo。

8. 失败定位 ​

ret 是 Binder return command,return_error_param 通常是 errno,return_error_line 是驱动源码行。看到 BR_FAILED_REPLY/-ENOSPC 时联读 allocator/debugfs;看到 -EPERM 联读 SELinux;看到 -EPROTO 联读 transaction stack/对象格式。日志字段本身不替代 AVC 或线程栈。

9. 与state对齐 ​

用 debug id 把 transaction_log 的最近事件与 transactions 当前未完成事务对齐:log 记录“发生过什么”,state 记录“现在还剩什么”。若 log 有 call 但 state 没有对应 pending,可能事务已完成;若 state 有 transaction 但 log 已被覆盖,需要 tracepoint 或下一次采样补齐时间线。

10. 读取限制 ​

日志没有 wall-clock 时间戳,只有 debug id 和字段;游标原子但 entry 内容通过发布标记保护,整份文件仍不是全局原子快照。跨进程/跨设备比较 debug id 没有意义,重启后也会重新开始。

11. 阅读检查 ​

给定正常 log、failed log 和 proc state 三份输出,按 debug id 判断一次事务是调用、异步还是 reply,定位失败 errno,并解释为何日志中出现但 state 中已经不存在。再说明环形覆盖后应补采集哪些 trace/debugfs 状态。