Binder子系统之调试分析(一)
一. 概述
在博客以前有寫過關(guān)于binder系列,大概寫了10篇關(guān)于binder的文章,從binder驅(qū)動,到native層,再到framework,一路寫到app層的使用。有興趣的可以看看?Binder系列—開篇。
二.Binder驅(qū)動調(diào)試
看過Binder系列文章的同學,會發(fā)現(xiàn)Binder IPC過程最終都交給Binder Driver來完成,這是真正干跨進程通信活的地方,那么意味著這里會有各種核心的通信log,比如binder open, mmap, ioctl等操作都可以通過某種方式來打開相應(yīng)調(diào)試信息來分析。對于binder driver存在16類調(diào)試log開關(guān),如下:
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引用計數(shù) |
| 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內(nèi)部引用計數(shù) |
| BINDER_DEBUG_BUFFER_ALLOC | 8192 | 同步內(nèi)存分配信息 |
| BINDER_DEBUG_PRIORITY_CAP | 16384 | 調(diào)整binder線程的nice值 |
| BINDER_DEBUG_BUFFER_ALLOC_ASYNC | 32768 | 異步內(nèi)存分配信息 |
每一項mask值通過將1左移N位,也就是等于2的倍數(shù)
2.2 調(diào)試開關(guān)
通過節(jié)點/sys/module/binder/parameters/debug_mask來動態(tài)控制選擇開啟上表中的debug log.
(1)例如打開BINDER_DEBUG_OPEN_CLOSE調(diào)試開關(guān),則通過adb shell執(zhí)行如下命令:
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)要打開多個開關(guān),只需將各個開關(guān)的mask值相加寫入debug_mask即可。打開調(diào)試開關(guān)后,可通過adb shell,執(zhí)行cat /proc/kmsg | grep binder,即可查看相應(yīng)的binder log信息。
2.3 原理
mask相加,其實現(xiàn)其實是利用或運算,通過一個變量控制16個開關(guān),而不是采用16個變量,這是比較經(jīng)典的設(shè)計方案。在binder Driver中通過下面語句完成節(jié)點控制debug的功能:
module_param_named(debug_mask, binder_debug_mask, uint, S_IWUSR | S_IRUGO);module_param_named的功能:
- 首先會生成/sys/module/binder/parameters/目錄;
- module_param_named的第一個參數(shù)為debug_mask,則會在parameters目錄下創(chuàng)建debug_mask文件節(jié)點;
- 當通過echo NUM > debug_mask命令,會觸發(fā)動態(tài)修改module_param_named的第二個參數(shù)binder_debug_mask值,這是個靜態(tài)uint32_t類型數(shù)據(jù);
- 驅(qū)動中輸出debug log都是通過調(diào)用binder_debug()方法,該方法通過與binder_debug_mask變量做或運算來判斷相應(yīng)類型的log信息是否需要輸出。
binder_debug宏定義,如下:
do { \if (binder_debug_mask & mask) \pr_info(x); \} while (0)當然,也可以通過代碼直接修改binder_debug_mask值來控制調(diào)試開關(guān),默認值為:
binder_debug_mask = BINDER_DEBUG_USER_ERROR |BINDER_DEBUG_FAILED_TRANSACTION | BINDER_DEBUG_DEAD_TRANSACTION;另外,在/sys/module/binder/parameters/目錄還有另外兩個節(jié)點,分別為proc_no_lock, stop_on_user_error,其實現(xiàn)原理也基本差不多。
- proc_no_lock節(jié)點:與之對應(yīng)binder驅(qū)動的binder_debug_no_lock變量,這是bool類型變量,該開關(guān)含義為在輸出某些統(tǒng)計調(diào)試方法中是否加鎖;默認為N;
- stop_on_user_error節(jié)點:與之對應(yīng)binder驅(qū)動的binder_stop_on_user_error變量,這是int類型變量,另外,修改該節(jié)點還會觸發(fā)調(diào)用binder_set_stop_on_user_error()方法;該開關(guān)含義是指當觸發(fā)BINDER_DEBUG_USER_ERROR類型錯誤時是否讓整個binder系統(tǒng)進入休眠等待狀態(tài),默認值為0,代表不會即便發(fā)生該類型錯誤系統(tǒng)不會被掛住,而是繼續(xù)執(zhí)行。
三 實戰(zhàn)
3.1 BINDER_DEBUG_OPEN_CLOSE
當打開調(diào)試開關(guān)BINDER_DEBUG_OPEN_CLOSE時,主要輸出binder的open, mmap, close, flush, release方法中的log信息
具體kernel log,如下:
3.2 解析
上面各行l(wèi)og所對應(yīng)的信息項:
進一步說明其中部分關(guān)鍵詞的含義:
- vm_page_prot:是指當前進程的VMA訪問權(quán)限;
- wake_count:是指該進程喚醒了處于BINDER_LOOPER_STATE_WAITING休眠等待狀態(tài)的線程個數(shù);
- threads是指該進程中的線程個數(shù);
- nodes代表該進程中創(chuàng)建binder_node個數(shù);
- incoming_refs指向當前node的refs個數(shù);
- outgoing_refs指向其他進程的refs個數(shù);
- active_transactions是指當前進程中所有binder線程的transactions總和;
- buffers是指當前進程已分配的buffer個數(shù);
- page_count是指當前進程已分配的物理page個數(shù)。
3.3 對應(yīng)函數(shù)
上述log每一行相對應(yīng)的函數(shù):
binder_flush調(diào)用棧:
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調(diào)用棧:
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所在進程結(jié)束時會調(diào)用binder_release。 binder_open打開binder驅(qū)動/dev/binder,這是字符設(shè)備,獲取文件描述符。在進程結(jié)束的時候會有一個關(guān)閉文件系統(tǒng)的過程中會調(diào)用驅(qū)動close方法,該方法相對應(yīng)的是release()方法。
但并不是每個close系統(tǒng)調(diào)用都會觸發(fā)調(diào)用release()方法. 只有真正釋放設(shè)備數(shù)據(jù)結(jié)構(gòu)才調(diào)用release(),內(nèi)核維持一個文件結(jié)構(gòu)被使用多少次的計數(shù),即便是應(yīng)用程序沒有明顯地關(guān)閉它打開的文件也適用: 內(nèi)核在進程exit()時會釋放所有內(nèi)存和關(guān)閉相應(yīng)的文件資源, 通過使用close系統(tǒng)調(diào)用最終也會release binder.
四. 其他實例
4.1 BINDER_DEBUG_DEAD_BINDER
//debug_id, node的引用次數(shù),死亡通知個數(shù)?
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;
內(nèi)存大小計算:
free_async_space = 520004 Bytes,再釋放148 Bytes后,則可用大小應(yīng)該是 520152 Bytes,這里卻為520192 Bytes,這里多出來的40 Bytes是哪來得呢?這是因為binder_free_buf還會同時釋放struct binder_buffer,該結(jié)構(gòu)體大小則為40 Bytes.
另外:buffer申請內(nèi)存binder_alloc_buf和釋放內(nèi)存binder_free_buf,除了本身內(nèi)存申請和釋放,會同時伴隨著binder_buffer結(jié)構(gòu)體的創(chuàng)建和釋放,這便是每次操作40 Bytes差距所在。另外, 對于64位系統(tǒng),binder_buffer大小為80 Bytes.
初始化值
proc->free_async_space = proc->buffer_size / 2 = (1M-8K)/2 = 520192 Bytes。當進程剛打開binder驅(qū)動,執(zhí)行完binder_mmap方法后,異步可用空間總大小為 520192 Bytes.
4.4 BINDER_DEBUG_BUFFER_ALLOC(同步)
// 參數(shù):proc->pid, size, buffer, buffer_size?
binder:?1788: binder_alloc_buf size?76?got buffer?c7800128?size?208?
binder:?1788:?allocate?pages?c7801000-c7800000?
// 參數(shù):proc->pid, new_buffer_size, new_buffer?
binder:?1788: add free buffer, size?92, at?c780019c?
binder:?1788:?free?pages?c7801000-c7800000?
//參數(shù):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大小的物理內(nèi)存,地址為c7801000-c7800000。
- binder_insert_free_buffer: 將空閑buffer添加到proc->free_buffers;
- binder_update_page_range:釋放一個page大小的物理內(nèi)存,地址為c7801000-c7800000。
- binder_delete_free_buffer:在執(zhí)行binder_free_buf()過程,合并釋放的buffer,由于該buffer跟上一個buffer共享同一page,則無需釋放。
五. 小結(jié)
本文主要介紹控制調(diào)試開關(guān)和各個開關(guān)的含義及原理,最后再通過一個實例來進一步來說明其中一項開關(guān)打開后的log信息該如何分析。后續(xù)會介紹更多的調(diào)試含義和調(diào)試工具,以及從上至下binder是如何通信。
原文地址:http://gityuan.com/2016/08/27/binder-debug/
總結(jié)
以上是生活随笔為你收集整理的Binder子系统之调试分析(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android5.1.1源码 - 让某个
- 下一篇: Binder子系统之调试分析(二)