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
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 状态。
