Binder子系统之调试分析(一)
一. 概述
在博客以前有寫過關于binder系列,大概寫了10篇關于binder的文章,從binder驅動,到native層,再到framework,一路寫到app層的使用。有興趣的可以看看?Binder系列—開篇。
二.Binder驅動調試
看過Binder系列文章的同學,會發現Binder IPC過程最終都交給Binder Driver來完成,這是真正干跨進程通信活的地方,那么意味著這里會有各種核心的通信log,比如binder open, mmap, ioctl等操作都可以通過某種方式來打開相應調試信息來分析。對于binder driver存在16類調試log開關,如下:
2.1 debug_mask
| BINDER_DEBUG_USER_ERROR | 1 | 用戶使用錯誤 |
| BINDER_DEBUG_FAILED_TRANSACTION | 2 | transaction失敗 |
| BINDER_DEBUG_DEAD_TRANSACTION | 4 | transaction死亡 |
| BINDER_DEBUG_OPEN_CLOSE | 8 | binder的open/close/mmap信息 |
| BINDER_DEBUG_DEAD_BINDER | 16 | binder/node死亡信息 |
| BINDER_DEBUG_DEATH_NOTIFICATION | 32 | binder死亡通知信息 |
| BINDER_DEBUG_READ_WRITE | 64 | binder的read/write信息 |
| BINDER_DEBUG_USER_REFS | 128 | binder引用計數 |
| BINDER_DEBUG_THREADS | 256 | binder_thread信息 |
| BINDER_DEBUG_TRANSACTION | 512 | transaction信息 |
| BINDER_DEBUG_TRANSACTION_COMPLETE | 1024 | transaction完成信息 |
| BINDER_DEBUG_FREE_BUFFER | 2048 | 可用buffer信息 |
| BINDER_DEBUG_INTERNAL_REFS | 4096 | binder內部引用計數 |
| BINDER_DEBUG_BUFFER_ALLOC | 8192 | 同步內存分配信息 |
| BINDER_DEBUG_PRIORITY_CAP | 16384 | 調整binder線程的nice值 |
| BINDER_DEBUG_BUFFER_ALLOC_ASYNC | 32768 | 異步內存分配信息 |
每一項mask值通過將1左移N位,也就是等于2的倍數
2.2 調試開關
通過節點/sys/module/binder/parameters/debug_mask來動態控制選擇開啟上表中的debug log.
(1)例如打開BINDER_DEBUG_OPEN_CLOSE調試開關,則通過adb shell執行如下命令:
echo 8 > /sys/module/binder/parameters/debug_mask(2)再例如同時打開BINDER_DEBUG_FAILED_TRANSACTION和BINDER_DEBUG_DEAD_BINDER,將各個mask值相加即可,16+2 =18.
echo 18 > /sys/module/binder/parameters/debug_mask(3)要打開多個開關,只需將各個開關的mask值相加寫入debug_mask即可。打開調試開關后,可通過adb shell,執行cat /proc/kmsg | grep binder,即可查看相應的binder log信息。
2.3 原理
mask相加,其實現其實是利用或運算,通過一個變量控制16個開關,而不是采用16個變量,這是比較經典的設計方案。在binder Driver中通過下面語句完成節點控制debug的功能:
module_param_named(debug_mask, binder_debug_mask, uint, S_IWUSR | S_IRUGO);module_param_named的功能:
- 首先會生成/sys/module/binder/parameters/目錄;
- module_param_named的第一個參數為debug_mask,則會在parameters目錄下創建debug_mask文件節點;
- 當通過echo NUM > debug_mask命令,會觸發動態修改module_param_named的第二個參數binder_debug_mask值,這是個靜態uint32_t類型數據;
- 驅動中輸出debug log都是通過調用binder_debug()方法,該方法通過與binder_debug_mask變量做或運算來判斷相應類型的log信息是否需要輸出。
binder_debug宏定義,如下:
do { \if (binder_debug_mask & mask) \pr_info(x); \} while (0)當然,也可以通過代碼直接修改binder_debug_mask值來控制調試開關,默認值為:
binder_debug_mask = BINDER_DEBUG_USER_ERROR |BINDER_DEBUG_FAILED_TRANSACTION | BINDER_DEBUG_DEAD_TRANSACTION;另外,在/sys/module/binder/parameters/目錄還有另外兩個節點,分別為proc_no_lock, stop_on_user_error,其實現原理也基本差不多。
- proc_no_lock節點:與之對應binder驅動的binder_debug_no_lock變量,這是bool類型變量,該開關含義為在輸出某些統計調試方法中是否加鎖;默認為N;
- stop_on_user_error節點:與之對應binder驅動的binder_stop_on_user_error變量,這是int類型變量,另外,修改該節點還會觸發調用binder_set_stop_on_user_error()方法;該開關含義是指當觸發BINDER_DEBUG_USER_ERROR類型錯誤時是否讓整個binder系統進入休眠等待狀態,默認值為0,代表不會即便發生該類型錯誤系統不會被掛住,而是繼續執行。
三 實戰
3.1 BINDER_DEBUG_OPEN_CLOSE
當打開調試開關BINDER_DEBUG_OPEN_CLOSE時,主要輸出binder的open, mmap, close, flush, release方法中的log信息
具體kernel log,如下:
3.2 解析
上面各行log所對應的信息項:
進一步說明其中部分關鍵詞的含義:
- vm_page_prot:是指當前進程的VMA訪問權限;
- wake_count:是指該進程喚醒了處于BINDER_LOOPER_STATE_WAITING休眠等待狀態的線程個數;
- threads是指該進程中的線程個數;
- nodes代表該進程中創建binder_node個數;
- incoming_refs指向當前node的refs個數;
- outgoing_refs指向其他進程的refs個數;
- active_transactions是指當前進程中所有binder線程的transactions總和;
- buffers是指當前進程已分配的buffer個數;
- page_count是指當前進程已分配的物理page個數。
3.3 對應函數
上述log每一行相對應的函數:
binder_flush調用棧:
binder_flush binder_defer_work(proc, BINDER_DEFERRED_FLUSH);queue_work(binder_deferred_workqueue, &binder_deferred_work);binder_deferred_func //通過 DECLARE_WORK(binder_deferred_work, binder_deferred_func);binder_deferred_flushbinder_release調用棧:
binder_release binder_defer_work(proc, BINDER_DEFERRED_RELEASE);queue_work(binder_deferred_workqueue, &binder_deferred_work);binder_deferred_func //通過 DECLARE_WORK(binder_deferred_work, binder_deferred_func);binder_deferred_release當binder所在進程結束時會調用binder_release。 binder_open打開binder驅動/dev/binder,這是字符設備,獲取文件描述符。在進程結束的時候會有一個關閉文件系統的過程中會調用驅動close方法,該方法相對應的是release()方法。
但并不是每個close系統調用都會觸發調用release()方法. 只有真正釋放設備數據結構才調用release(),內核維持一個文件結構被使用多少次的計數,即便是應用程序沒有明顯地關閉它打開的文件也適用: 內核在進程exit()時會釋放所有內存和關閉相應的文件資源, 通過使用close系統調用最終也會release binder.
四. 其他實例
4.1 BINDER_DEBUG_DEAD_BINDER
//debug_id, node的引用次數,死亡通知個數?
binder: node?1078337?now dead, refs?1, death?0
//ref->proc->pid, ref->debug_id, ref->desc(handle)?
binder:?13839?delete ref?1078335?desc?1?has death notification
//proc->pid, thread->pid, (u64)cookie, death?
binder:?1788:1805?BC_DEAD_BINDER_DONE?9ce308c0?found?f10a5400
4.2 BINDER_DEBUG_FREE_BUFFER
查詢可用buffer:
//proc->pid, thread->pid, (u64)data_ptr, buffer->debug_id, buffer->transaction?
binder:?463:5919?BC_FREE_BUFFER ub4641028?found buffer?1183795?for?finishedtransaction?
binder:?277:2771?BC_FREE_BUFFER ub6c58028?found buffer?1183806?for?activetransaction
另外,buffer->transaction ? “active” : “finished”
位于方法binder_thread_write()
4.3 BINDER_DEBUG_BUFFER_ALLOC_ASYNC(異步)
申請和釋放異步buffer:
//proc->pid, size, proc->free_async_space
binder:?1788: binder_alloc_buf size?148?async free?520004
binder:?1788: binder_free_buf size?148?async free?520192
解析:
- binder_alloc_buf:進程1788,申請148 Bytes,則該進程的可用異步空間大小520004 Bytes;
- binder_free_buf: 進程1788,釋放148 Bytes,則該進程的可用異步空間大小520192 Bytes;
內存大小計算:
free_async_space = 520004 Bytes,再釋放148 Bytes后,則可用大小應該是 520152 Bytes,這里卻為520192 Bytes,這里多出來的40 Bytes是哪來得呢?這是因為binder_free_buf還會同時釋放struct binder_buffer,該結構體大小則為40 Bytes.
另外:buffer申請內存binder_alloc_buf和釋放內存binder_free_buf,除了本身內存申請和釋放,會同時伴隨著binder_buffer結構體的創建和釋放,這便是每次操作40 Bytes差距所在。另外, 對于64位系統,binder_buffer大小為80 Bytes.
初始化值
proc->free_async_space = proc->buffer_size / 2 = (1M-8K)/2 = 520192 Bytes。當進程剛打開binder驅動,執行完binder_mmap方法后,異步可用空間總大小為 520192 Bytes.
4.4 BINDER_DEBUG_BUFFER_ALLOC(同步)
// 參數:proc->pid, size, buffer, buffer_size?
binder:?1788: binder_alloc_buf size?76?got buffer?c7800128?size?208?
binder:?1788:?allocate?pages?c7801000-c7800000?
// 參數:proc->pid, new_buffer_size, new_buffer?
binder:?1788: add free buffer, size?92, at?c780019c?
binder:?1788:?free?pages?c7801000-c7800000?
//參數:proc->pid, buffer, prev?
binder:?1788: merge free, buffer?c780019c?share page with?c7800128
解析:
- binder_alloc_buf: 從proc->free_buffers這棵紅黑色樹,找到一塊大小大于并最接近76Bytes的buffer,該buffer大小為208Bytes;
- binder_update_page_range:申請一個page大小的物理內存,地址為c7801000-c7800000。
- binder_insert_free_buffer: 將空閑buffer添加到proc->free_buffers;
- binder_update_page_range:釋放一個page大小的物理內存,地址為c7801000-c7800000。
- binder_delete_free_buffer:在執行binder_free_buf()過程,合并釋放的buffer,由于該buffer跟上一個buffer共享同一page,則無需釋放。
五. 小結
本文主要介紹控制調試開關和各個開關的含義及原理,最后再通過一個實例來進一步來說明其中一項開關打開后的log信息該如何分析。后續會介紹更多的調試含義和調試工具,以及從上至下binder是如何通信。
原文地址:http://gityuan.com/2016/08/27/binder-debug/
總結
以上是生活随笔為你收集整理的Binder子系统之调试分析(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android5.1.1源码 - 让某个
- 下一篇: Binder子系统之调试分析(二)