標籤:
一、問題現象
1、System先ANR。
2、ANR之後系統重啟。
測試方法:
Stability test。
Platform:MT6732
Android版本:4.4.4KK
BuildType:user
系統軟體版本:D17+ZX
系統RAM:1GB
問題機率:暫未統計,截止到目前僅此1次
參考機行為:
1、低機率問題,暫無參考機行為。
二、解決方案
通過初步分析、深入分析(具體分析過程、關鍵代碼和log在下面會附上)我們清楚的知道了問題發生的原因:
1、SystemServer進程的主線程訪問media audio的介面進行跨進程調用時被block。
2、Media audio的介面最終會調用到mediaserver進程中。
3、Mediaserver進程中產生了死結。
4、最終導致systemServer先ANR然後被SWT重啟。
在當前代碼的執行狀態下有一定機率(很小,必須滿足sp指標擷取到之後並成為最後一個強引用的條件)出現遞迴調用一個加了預設為NORMAL類型的mutex autolock的方法而產生死結。
考慮mediaserver中複雜的邏輯,所以要以最小風險的改動來修複這個問題,因此這裡給出的方案沒有進行太大的改動。
最終,針對以上問題的根本原因,我們給出以下解決方案:
1、延長sp指標的生命週期,使其生命週期長於mutex autolock
在可能遞迴調用的加了mutex autolock的函數中,使用vector容器延長sp指標的生命週期,使其在mutex autolock生命週期結束並unlock之後再析構sp指標,此時如果再發生遞迴調用則不會死結。
2、方案相關的具體代碼和backtrace
以上是發生死結時鎖對應的backtrace調用棧以及相應的代碼,通過紅線圈住部分我們可以看到發生問題時的關鍵調用關係和狀態。
3、最終方案的代碼修改
三、問題初步分析
以ALTO4.5TF出問題時候的一份典型backtrace和log為例,發現出問題時SystemServer的主線程block在了一個AudioSystem內部的一個native函數上,從而引發ANR和SWT重啟,具體backtrace如下:
為什麼會block?通過查看如上對應代碼,發現setParameter這個方法會通過audioflinger的代理對象跨進程調用到mediaserver中的audioflinger的setParameter方法,因此接下來就要mediaserver進程是否工作的正常。
根據這個線索繼續查看mediaserver中各個thread的調用棧,發現幾乎所有的Binder thread都被block了,這也就解釋了為什麼SystemServer的調用得不到處理而發生ANR以及SWT重啟。其中絕大多數Binder thread被block的backtrace如下:
由於mediaserver是native的C/C++代碼,所以需要用address2line這個工具將對應的PC指標的地址在symbols中找到對應的源碼來分析,具體過程不再贅述,大家可以找一些專門的文章來簡單學習一下,經過這個過程之後得到的對應的源碼如下:
從以上代碼中我們可以看到getplayertype的實現中第一句代碼就是要進行sLock.lock(),如果lock不成功則會等待,因此接下來繼續看其他thread中誰佔用了這個lock。通過查看其他thread的backtrace,發現Binder_5這個thread佔用了sLock,但是它也被block在另外一個mutex lock上,具體的backtrace和代碼如下:
順著這個線索我們繼續看mediaserver進程中的其他thread,看誰佔用了ALooperRoster的mLock,最終找到了rtsp thread在調用ALooperRoster的unregisterStaleHandlers函數時佔用了mLock這個鎖,但同時它自己又間接調用了unregisterStaleHandlers,最終也被block在了這個mLock上,具體的backtrace和代碼如下:
四、深入分析問題
經過初步分析我們定位到了第一個問題點,即rtsp thread block在了它已經lock的一個mLock上,同時也產生了2個問題,接下來我們繼續深入分析以期能到找到答案和問題的根本原因。
1、為什麼會間接遞迴調用ALooperRoster::unregisterStaleHandlers方法?
2、為什麼rtsp thread自己會block在mLock上?
通過進一步分析和查看代碼發現,rstp thread之所以會間接遞迴調用ALooperRoster::unregisterStaleHandlers方法,是因為sp<ALooper> looper = info.mLooper.promote();這句代碼中的looper強指標sp在出現問題的情境下,出了一次for迴圈的範圍時會成為最後一個持有ALooper對象的sp而在析構時發生ALooper對象的釋放,從而引發ALooper對象的析構,在ALooper對象析構時就會再次調用ALooperRoster::unregisterStaleHandlers方法而產生間接遞迴調用。關於Android中native層的C++的智能指標、sp、wp這裡簡單介紹一下,大概原理就是利用sp和wp指標對象的建構函式和解構函式來對所指向的對象進行引用計數的加減,指標所指向的對象必須要繼承自class RefBase,根據對象的生命週期標誌在強引用計數或弱引用計數為0時來進行對象的釋放:
生命週期預設是STRONG,即當對象的強引用計數為0時就釋放對象,關鍵代碼如下:
回答完第一個問題,我們接下來看第二個問題,在回答上面第二個問題之前我們需要先簡單說一下Autolock這個類,這個類是在system/core/include/utils/Mutex.h中Mutex類內部定義的,具體代碼如下:
這個類的原理簡單理解就是在建構函式中lock,解構函式中unlock,這正好利用了C++的特性,所以進入Autolock對象的範圍就會lock,出了範圍就會unlock,從而實現自動的Mutex。而Mutex類是pthread_mutex_t的一個封裝,而pthread_mutex_t預設是NORMAL類型的,即不可遞迴重入的:
而ALooperRoster::unregisterStaleHandlers方法就是使用的Mutex::Autolock autoLock(mLock)進行同步的,在沒有出Autolock的範圍時間接遞迴調用,這時mLock這個Mutex是lock著的,由於Mutex預設的這個不可遞迴重入的屬性最終就導致了當前這個死結的現象,到這這也就回答了為什麼rtsp thread自己為什麼會block在mLock上。
五、解決方案潛在的影響
由於僅僅是稍微延遲了sp指標的生命週期,在出了函數範圍之後就會馬上釋放,所以當前的方案沒有其他已知的影響,最後再次看一下修改的代碼和注釋:
Analyzed by vincent.song from SWD2 Framework team.
[email protected]
201506242052
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
Android Stability test occured SWT restart issue