1

在进行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)。死亡记录展示如下,
Selection_010.png

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信息。
Selection_007.png

线程状态显示了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进程逐个输出统计信息。一个输出示例如下,
Selection_008.png
Selection_009.png

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 10763886080x40286300
BC_REPLY Server向Client发送回复(应答)数据 binder_transaction_data: the sent command 10763886090x40286301
BC_ACQUIRE_RESULT 暂未实现 --- 10740293140x40046302
BC_FREE_BUFFER 释放一块映射的内存。 void *: ptr to transaction data received on a read 10740293150x40046303
BC_INCREFS 增加Binder的弱引用计数 int: descriptor 10740293160x40046304
BC_ACQUIRE 增加Binder的强引用计数 int: descriptor 10740293170x40046305
BC_RELEASE 减少Binder的强引用计数 int: descriptor 10740293180x40046306
BC_DECREFS 减少Binder的弱引用计数 int: descriptor 10740293190x40046307
BC_INCREFS_DONE Binde实体所在的进程处理完BC_INCREFS的反馈 void : ptr to bindervoid : cookie 10742914640x40086308
BC_ACQUIRE_DONE Binde实体所在的进程处理完BC_ACQUIRE的反馈 void : ptr to bindervoid : cookie 10742914650x40086309
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 10740293280x40046310

以BR_开头的命令为Binder驱动向应用层发送的回复命令,定义如下,

Cmd Description Args Code
BR_ERROR 发生内部错误(如内存分配失败) int: error code 21477749760x80047200
BR_OK 操作完成 --- 291850x7201
BR_TRANSACTION 对应发送方的BC_TRANSACTION binder_transaction_data: the received command 21501342740x80287202
BR_REPLY 对应发送方的BC_REPLY binder_transaction_data: the received command 21501342750x80287203
BR_ACQUIRE_RESULT 暂未实现 --- 21477749800x80047204
BR_DEAD_REPLY 交互过程中如果发现对方进程或线程已经死亡则返回该消息 --- 291890x7205
BR_TRANSACTION_COMPLETE 接收方发送该消息作为BC_TRANSACTION或BC_REPLY发送成功的反馈 --- 291900x7206
BR_INCREFS 增加Binder本地对象的弱引用计数 void : ptr to bindervoid : cookie for binder 21480371270x80287207
BR_ACQUIRE 增加Binder本地对象的强引用计数 void : ptr to bindervoid : cookie for binder 21480371280x80287208
BR_RELEASE 减少Binder本地对象的强引用计数 void : ptr to bindervoid : cookie for binder 21480371290x80287209
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 21477749920x80047210
BR_FAILED_REPLY 如果发送非法引用号则返回该消息 --- 292010x7211

transaction_log和failed_transaction_log

这两个文件节点提供了最近32次成功与失败的传输记录,输出信息大致相同。
Selection_011.png

相关的数据结构为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进程的传输状态,一个输出示例如下,

Selection_012.png

这个输出信息中包含许多数据结构里的内容,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 */
}

戈壁老王
143 声望61 粉丝

做为一个不称职的老年码农,一直疏忽整理笔记,开博记录一下,用来丰富老年生活,