在进行 Binder debug 或分析问题时,通常需要看一下当前的 Binder 状态信息。Kernel 通过 SYS 系统提供了一些文件节点供我们读取,它们位于 /sys/kernel/debug/binder/,分别为:
- State:当前 Binder 所有进程的状态。
- Stats:Binder 传输的统计信息。
- Transactions:当前 Binder 所有进程的传输状态。
- transaction_log:最近的 Binder 传输。
- failed_transaction_log:最近失败的 Binder 传输。
要理解这些 Debug 信息,必须 Binder 驱动中相关的数据结构定义。这些信息几乎涉及到驱动中所有的结构体,下面结合二者看一下信息的具体内容。首先看一下 Binder 驱动最基本的数据结构 binder_proc。Binder_proc 是用于描述 Binder 进程的数据结构,一个进程只有一个 binder_proc,其他的结构体都会关联到这个数据结构中。也就是说,通过 binder_proc 可以查找到所有 Binder 进程相关的信息,如 Binder 线程,Binder 引用,Binder 内存等。
struct binder_proc {
struct hlist_node proc_node; /* hlist 节点,连接到全局队列 binder_procs 中 */
struct rb_root threads; /* Binder 线程的红黑树 */
struct rb_root nodes; /* Binder 实体的红黑树 */
struct rb_root refs_by_desc; /* Binder 引用的红黑树,以 handle 为 key */
struct rb_root refs_by_node; /* Binder 引用的红黑树,以 node 地址为 key */
int pid; /* 进程 PID */
struct vm_area_struct *vma; /* 进程虚拟地址空间指针 */
struct mm_struct *vma_vm_mm; /* 进程内存结构 */
struct task_struct *tsk; /* 进程 task 结构 */
struct files_struct *files; /* 进程 file 结构 */
struct hlist_node deferred_work_node; /* hlist 节点,连接到全局队列 binder_deferred_list 中,用于处理 binder 退出任务 */
int deferred_work; /* Binder defer flag,包括 PUT_FILES,FLUSH,RELEASE */
void *buffer; /* Binder 内存内核起始地址 */
ptrdiff_t user_buffer_offset; /* Binder 内存用户与内核间的地址偏移量 */
struct list_head buffers; /* Binder buffer 的队列 */
struct rb_root free_buffers; /* 空闲 Binder buffer 的红黑树 */
struct rb_root allocated_buffers; /* 已分配 Binder buffer 的红黑树 */
size_t free_async_space; /* 异步传输的可用空间 */
struct page **pages; /* 物理内存页 */
size_t buffer_size; /* Binder 内存映射的大小 */
uint32_t buffer_free; /* Binder 内存的可用空间 */
struct list_head todo; /* Binder 进程的 todo 队列 */
wait_queue_head_t wait; /* Binder 进程的等待队列 */
struct binder_stats stats; /* Binder 统计信息 */
struct list_head delivered_death; /* Binder 已分发的死亡通知 */
int max_threads; /* Binder 进程的最大线程数 */
int requested_threads; /* 请求的线程数 */
int requested_threads_started; /* 已启动的请求线程数 */
int ready_threads; /* 准备就绪的线程数 */
long default_priority; /* 缺省优先级 */
struct dentry *debugfs_entry; /* debugfs 入口 */
};
State
State 最开始记录的时死亡的 Binder(dead node)。因为这些 Binder 的进程已经死掉,它们当前不属于任何进程,所以将它们记录在一个全局的队列中(binder_dead_nodes)。死亡记录展示如下,
Dead nodes 相关的数据结构为 binder_node,binder_node 代表着 binder 实体。进程中的 binder_node 由 Proc 的 nodes 红黑树管理,死亡的 binder_node 由全局的 hlist binder_dead_nodes 管理,还有一个全局 binder_context_mgr_node 用来记录 system_server 的 binder_node。其定义如下,
struct binder_node {
int debug_id; /* binder node 在驱动里的全局 id,用于调试 */
struct binder_work work; /* binder 工作队列。用于增加 / 删除 binder,更新 binder 引用计数 */
union {
struct rb_node rb_node; /* Proc nodes 红黑树的 rb_node */
struct hlist_node dead_node; /* 死亡的 binder_node, 当一个进程结束时,它的 binder 也被销毁。但如果其他进程还在引用它的 binder,则 binder node 无法移除,成为 dead node */
};
struct binder_proc *proc; /* binder node 所属的进程 */
struct hlist_head refs; /* binder node 引用的队列,用于遍历 binder node 的所有引用 */
int internal_strong_refs; /* binder node 的外部强引用计数 */
int local_weak_refs; /* 驱动内部的 binder 弱引用计数 */
int local_strong_refs; /* 驱动内部的 binder 强引用计数 */
void __user *ptr; /* 用户空间指向 binder 的指针,用于索引 */
void __user *cookie; /* 用户空间的附加指针 */
unsigned has_strong_ref:1; /* 是否有强引用 */
unsigned pending_strong_ref:1; /* 是否有等待处理的强引用 */
unsigned has_weak_ref:1; /* 是否有弱引用 */
unsigned pending_weak_ref:1; /* 是否有等待处理的弱引用 */
unsigned has_async_transaction:1; /* 在 Todo 队列中是否有未完成的异步传输 */
unsigned accept_fds:1; /* 是否同意接受文件方式的 binder*/
unsigned min_priority:8; /* 设置处理 Binder 请求的线程的最低优先级 */
struct list_head async_todo; /* 异步传输等待队列 */
}
Dead nodes 信息后面跟着是当前系统中所有的 Binder 进程的信息,该信息包括:进程中 Binder 线程的状态,Binder 实体信息,Binder 引用信息,binder buffer 信息。
线程状态显示了 Binder 进程中所有的 Binder 线程的 PID 和线程状态,binder_thread 数据结构和线程状态定义如下。
enum {
BINDER_LOOPER_STATE_REGISTERED = 0x01, /* 注册线程(BC_REGISTER_LOOPER)*/
BINDER_LOOPER_STATE_ENTERED = 0x02, /* 创建主线程(BC_ENTER_LOOPER)*/
BINDER_LOOPER_STATE_EXITED = 0x04, /* 已退出 */
BINDER_LOOPER_STATE_INVALID = 0x08, /* 无效 */
BINDER_LOOPER_STATE_WAITING = 0x10, /* 等待中 */
BINDER_LOOPER_STATE_NEED_RETURN = 0x20 /* 需要返回 */
};
struct binder_thread {
struct binder_proc *proc; /* 线程所属的而进程 */
struct rb_node rb_node; /* 红黑树节点,插入到 binder_proc->threads 中 */
int pid; /* 线程 PID */
int looper; /* 线程 looper 状态,用上面的枚举描述 */
struct binder_transaction *transaction_stack; /* Binder 传输栈 */
struct list_head todo; /* Binder 线程 todo 队列 */
uint32_t return_error; /* 写失败时的返回错误码 */
uint32_t return_error2; /* 写失败时的返回错误码 2 */
wait_queue_head_t wait; /* Binder 线程等待队列 */
struct binder_stats stats; /* Binder 线程统计信息 */
};
然后是 Binder 进程中 Binder 实体的信息,解释参考 Dead nodes。接着打印的是进程中 Binder 引用的信息,相关数据结构为 binder_ref,用做 Binder 实体的代理。
struct binder_ref {
/* Lookups needed: */
/* node + proc => ref (transaction) */
/* desc + proc => ref (transaction, inc/dec ref) */
/* node => refs + procs (proc exit) */
int debug_id; /* 全局 id,用于调试 */
struct rb_node rb_node_desc; /* 红黑树节点,用于 binder_proc->refs_by_desc */
struct rb_node rb_node_node; /* 红黑树节点,用于 binder_proc->refs_by_node */
struct hlist_node node_entry; /* hlist 节点,用于 binder_node->refs 索引 */
struct binder_proc *proc; /* 指向的 Binder 进程 */
struct binder_node *node; /* 指向的 Binder 实体 */
uint32_t desc; /* handle 值 */
int strong; /* 强引用 */
int weak; /* 弱引用 */
struct binder_ref_death *death; /* 已注册的死亡通知地址 */
};
最后时 Binder 进程中 Binder buffer 的信息。相关数据结构为 binder_buffer,用于存储 Binder 传输数据。
struct binder_buffer {
struct list_head entry; /* list 节点,连接到 binder_proc->buffers */
struct rb_node rb_node; /* 红黑树节点,插入到 binder_proc->free_buffers(buffer 空闲时)或 binder_proc->allocated_buffers(buffer 已分配时)*/
unsigned free:1; /* 是否空闲 */
unsigned allow_user_free:1; /* 是否允许用户释放 */
unsigned async_transaction:1; /* 是否为异步传输 */
unsigned debug_id:29; /* 全局 id,用于调试 */
struct binder_transaction *transaction; /* 指向的 Binder 传输事务 */
struct binder_node *target_node; /* 指向的 Binder 实体 */
size_t data_size; /* 数据大小 */
size_t offsets_size; /* 数据偏移量 */
uint8_t data[0]; /* 数据地址 */
}
Stats
Stats 包含的 Binder 的统计信息,包括传输命令的统计,内部对象的统计等。开始输出的是全部 Binder 的统计信息,之后按 Binder 进程逐个输出统计信息。一个输出示例如下,
Binder 统计信息通过 binder_stats 来表示,
struct binder_stats {int br[_IOC_NR(BR_FAILED_REPLY) + 1]; /* Binder 返回命令的计数(Binder Driver Return Protocol)*/
int bc[_IOC_NR(BC_DEAD_BINDER_DONE) + 1]; /* Binder 写命令的计数(Binder Driver Command Protocol)*/
int obj_created[BINDER_STAT_COUNT]; /* Binder 内部对象的计数,创建时加 1 */
int obj_deleted[BINDER_STAT_COUNT]; /* Binder 内部对象的计数,删除时加 1 */
};
以 BC_开头的命令为应用层向 Binder 驱动发送的请求命令,定义如下,
Cmd | Description | Args | Code |
---|---|---|---|
BC_TRANSACTION | Client 向 Server 发送请求数据 | binder_transaction_data: the sent command | 10763886080×40286300 |
BC_REPLY | Server 向 Client 发送回复(应答)数据 | binder_transaction_data: the sent command | 10763886090×40286301 |
BC_ACQUIRE_RESULT | 暂未实现 | — | 10740293140×40046302 |
BC_FREE_BUFFER | 释放一块映射的内存。 | void *: ptr to transaction data received on a read | 10740293150×40046303 |
BC_INCREFS | 增加 Binder 的弱引用计数 | int: descriptor | 10740293160×40046304 |
BC_ACQUIRE | 增加 Binder 的强引用计数 | int: descriptor | 10740293170×40046305 |
BC_RELEASE | 减少 Binder 的强引用计数 | int: descriptor | 10740293180×40046306 |
BC_DECREFS | 减少 Binder 的弱引用计数 | int: descriptor | 10740293190×40046307 |
BC_INCREFS_DONE | Binde 实体所在的进程处理完 BC_INCREFS 的反馈 | void : ptr to bindervoid : cookie | 10742914640×40086308 |
BC_ACQUIRE_DONE | Binde 实体所在的进程处理完 BC_ACQUIRE 的反馈 | void : ptr to bindervoid : cookie | 10742914650×40086309 |
BC_ATTEMPT_ACQUIRE | 暂未实现 | — | 10742914660x4008630a |
BC_REGISTER_LOOPER | 通知驱动线程池中一个线程已经创建 | — | 253550x630b |
BC_ENTER_LOOPER | 通知驱动该线程已经进入主循环,可以接收数据 | — | 253560x630c |
BC_EXIT_LOOPER | 通知驱动该线程退出主循环,不再接收数据 | — | 253570x630d |
BC_REQUEST_DEATH_NOTIFICATION | Binder 引用的进程要求获得 Binder 的实体死亡通知 | void : ptr to bindervoid : cookie | 10742914700x4008630e |
BC_CLEAR_DEATH_NOTIFICATION | 清除获得 Binder 的实体死亡通知 | void : ptr to bindervoid : cookie | 10742914710x4008630f |
BC_DEAD_BINDER_DONE | 收到实体死亡通知书的进程在删除引用后用本命令告知驱动 | void *: cookie | 10740293280×40046310 |
以 BR_开头的命令为 Binder 驱动向应用层发送的回复命令,定义如下,
Cmd | Description | Args | Code |
---|---|---|---|
BR_ERROR | 发生内部错误(如内存分配失败) | int: error code | 21477749760×80047200 |
BR_OK | 操作完成 | — | 291850×7201 |
BR_TRANSACTION | 对应发送方的 BC_TRANSACTION | binder_transaction_data: the received command | 21501342740×80287202 |
BR_REPLY | 对应发送方的 BC_REPLY | binder_transaction_data: the received command | 21501342750×80287203 |
BR_ACQUIRE_RESULT | 暂未实现 | — | 21477749800×80047204 |
BR_DEAD_REPLY | 交互过程中如果发现对方进程或线程已经死亡则返回该消息 | — | 291890×7205 |
BR_TRANSACTION_COMPLETE | 接收方发送该消息作为 BC_TRANSACTION 或 BC_REPLY 发送成功的反馈 | — | 291900×7206 |
BR_INCREFS | 增加 Binder 本地对象的弱引用计数 | void : ptr to bindervoid : cookie for binder | 21480371270×80287207 |
BR_ACQUIRE | 增加 Binder 本地对象的强引用计数 | void : ptr to bindervoid : cookie for binder | 21480371280×80287208 |
BR_RELEASE | 减少 Binder 本地对象的强引用计数 | void : ptr to bindervoid : cookie for binder | 21480371290×80287209 |
BR_DECREFS | 减少 Binder 本地对象的弱引用计数 | void : ptr to bindervoid : cookie for binder | 21480371300x8028720a |
BR_ACQUIRE_RESULT | 暂未实现 | — | 21482992750x800c720b |
BR_NOOP | 不做事情, | — | 291960x720c |
BR_SPAWN_LOOPER | 驱动发现接收方所有线程都处于忙碌状态,向接收方发送该命令要求创建更多线程以备接收数据 | — | 291970x720d |
BR_FINISHED | 暂未实现 | — | 291980x720e |
BR_DEAD_BINDER | 向获得 Binder 引用的进程发送 Binder 实体死亡通知 | void **cookie | 21477749910x8004720f |
BR_CLEAR_DEATH_NOTIFICATION_DONE | 死亡通知清除完成 | void **cookie | 21477749920×80047210 |
BR_FAILED_REPLY | 如果发送非法引用号则返回该消息 | — | 292010×7211 |
transaction_log 和 failed_transaction_log
这两个文件节点提供了最近 32 次成功与失败的传输记录,输出信息大致相同。
相关的数据结构为 binder_transaction_log_entry。这个结构体做为 Binder 传输的描述,每次发起 Binder 传输时记录到全局结构体中,用于 debug。
struct binder_transaction_log_entry {
int debug_id; /* transaction log ID */
int call_type; /* 传输类型,0:call, 1:async, 2:replay */
int from_proc; /* 发起传输的进程 PID*/
int from_thread; /* 发起传输的线程 PID */
int target_handle; /* Binder 实体的引用, 接收时为 -1 */
int to_proc; /* 处理传输的进程 PID*/
int to_thread; /* 处理传输的线程 PID,如果不存在值为 0 */
int to_node; /* 处理传输的 Binder node ID */
int data_size; /* 传输数据的长度 */
int offsets_size; /* 传输数据的偏移量 */
};
struct binder_transaction_log {
int next; /* 传输 log 计数 */
int full; /* 传输 log 是否已满 */
struct binder_transaction_log_entry entry[32]; /* 传输 log 记录 */
};
static struct binder_transaction_log binder_transaction_log;
static struct binder_transaction_log binder_transaction_log_failed;
Transactions
Transactions 中记录了当前系统中所有 Binder 进程的传输状态,一个输出示例如下,
这个输出信息中包含许多数据结构里的内容,buffer 信息与 binder_buffer 相关,thread 信息与 binder_thread 相关,还有一个最重要的传输事件是由 binder_transaction 来描述的。
struct binder_work {
struct list_head entry; /* 工作队列节点 */
enum {
BINDER_WORK_TRANSACTION = 1,
BINDER_WORK_TRANSACTION_COMPLETE,
BINDER_WORK_NODE,
BINDER_WORK_DEAD_BINDER,
BINDER_WORK_DEAD_BINDER_AND_CLEAR,
BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
} type;
};
......
struct binder_transaction {
int debug_id; /* 全局 ID,用于调试 */
struct binder_work work; /* 传输的工作类型 */
struct binder_thread *from; /* 传输发送端线程 */
struct binder_transaction *from_parent; /* 发送端传输事件 */
struct binder_proc *to_proc; /* 传输接收端进程 */
struct binder_thread *to_thread; /* 传输接收端线程 */
struct binder_transaction *to_parent; /* 接收端传输事件 */
unsigned need_reply:1; /* 是否需要回复 */
/* unsigned is_dead:1; */ /* not used at the moment */
struct binder_buffer *buffer; /* 传输数据的 buffer */
unsigned int code; /* 传输的命令码 */
unsigned int flags; /* 传输标识,例如 TF_ONE_WAY */
long priority; /* 传输优先级 */
long saved_priority; /* 传输缺省优先级 */
kuid_t sender_euid; /* 发送端的 uid */
}