Binder驱动之debug信息与数据结构

36次阅读

共计 9607 个字符,预计需要花费 25 分钟才能阅读完成。

在进行 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 */
}

正文完
 0