Binder驅動之debug信息與數據結構

在進行Binder debug或分析問題時,一般須要看一下當前的Binder狀態信息。Kernel經過SYS系統提供了一些文件節點供咱們讀取,它們位於/sys/kernel/debug/binder/,分別爲:node

  • 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內存等。cookie

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數據結構和線程狀態定義以下。async

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實體的代理。oop

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傳輸數據。ui

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.pngspa

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 */
}
相關文章
相關標籤/搜索