标签:
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.
vincent.song@tcl.com
201506242052
版权声明:本文为博主原创文章,未经博主允许不得转载。
Android Stability test occured SWT restart issue
标签:
原文地址:http://blog.csdn.net/songjinshi/article/details/46685389