ANR解决思路

From

本文案例主要源于项目上实际遇到的问题,希望通过梳理之后能够对ANR问题能够快速定位,减少排查时间,同时在遇到棘手问题,能够更加从容。

先说下三种常见类型
1:KeyDispatchTimeout(谷歌default 5s(input),MTK平台上是8s) --主要类型
按键或触摸事件在特定时间内无响应
2:BroadcastTimeout(前台:10s  后台:60s)
BroadcastReceiver在特定时间内无法处理完成
3:ServiceTimeout(前台:20s 后台:200s) --小概率类型
Service在特定的时间内无法处理完成

4:ContentProvider:(10s)

一般解决步骤

Step1:日志获取&注意事项

获取日志有一点需要注意,发生ANR后,不要选择结束进程,因为这样AMS会kill掉该进程,有些信息会打印不出来(比如MTK平台上会生成db.XX.ANR,写入到aee_exp文件夹下需要时间),最好是ANR发生后等两三分钟左右,再获取日志

一般需要data/anr下生成的trace文件以及手机系统日志(一般只需要mobile文件夹)

以MTK为例,需要如下日志信息:

1.aee_exp文件夹(MTK日志目录下,发生ANR会生成)
2.MTK目录下的mobilelog文件夹

这里的aee_exp文件夹一般都是需要的, 对DB进行dump解析,得到ANR发生时场景信息,比如主线程callstack,CPU,memory等,在分析问题根因时很关键。

Step2:日志分析一般步骤
Step2.1 确认时间点(非必需)
mobilelog文件夹下的events_log,搜索关键字"am_anr",这一步用于确认ANR时间点,可以搜索到类似如下信息

04-29 10:00:57.240 1267 1341 I am_anr : [0,6073,com.android.dialer,952745541,Input dispatching timed out

比如上面这行表示ANR类型为Input dispatching timed out, 这种anr的原因的是在viewrootimpl分发事件时,并没有找到focuswindow导致的

这一步确认时间点在:10:00:57,进程号:6073

Step2: 查看ANR时的CPU以及IO率(可选)

这一步一般来说能基本定位是什么造成了ANR,是IO高还是CPU高,如两者都不是,需进入第三步trace日志分析环节 查看mobilelog文件夹下的main_log,搜索关键字"ANR in",可以看到当时的CPU以及IO率,这一个环节一般来讲主要是看发生ANR时的CPU使用情况,CPU是否吃紧,

还有需要注意iowait的占有率,如果占比比较高,则排查的方向要倾向与读取文件操作有关的信息,可以看trace日志中有没有一些读取文件或者操作SD卡的动作

Step3:分析ANR时的堆栈(必需,最重要的环节)
ANR发生后会在data/anr下生成trace.txt,这份trace务必要与moile日志匹配,一般来讲直接先看tid=1的堆栈即对应主线程,因为ANR都是主线程执行超时导致

关于trace日志的分析是ANR问题分析最重要的一个环节,下面将列出平时项目中遇到的ANR案例类型

ANR常见类型归纳

1.主线程Binder调用等待超时

比如下面的这段

ANR解决思路_第1张图片

1_binder等待超时.png

很明显当时在做Binder通信,并没有waiting to lock等代表死锁的字样,那么说明这个案例即有可能是在等Binder对端响应,我们知道Binder通信对于发起方来说是默认是阻塞等待响应,只有有了返回结果后才会继续执行下去,当然了可以给接口设置oneway声明,这样的话binder请求就是异步请求,这里不多说

所以,如上这个案例中需要找到对端是哪个进程,这个进程当时在做什么,这时候就需要找到anr文件夹下另外一个文件binderinfo,这里需要找到与我们发起方进程1461通信的是哪个进程

ANR解决思路_第2张图片

2_binder等待超时.png

可以看到是1666号这个进程,再回到trace中看下,这个进程当时在做什么

ANR解决思路_第3张图片

3_binder等待超时.png

可以看到当时对端在做消息的读取,也就是说这里出了问题,很明显这里我们无法修改,我们这个问题在于主线程执行了Binder请求,对端迟迟未返回便很容易出现这个问题,当前做法异步中执行

案例二:主线程等待锁

比如下面这个主线程的CallStack例子,可以看到如下信息:

 

ANR解决思路_第4张图片

4_主线程等待锁.png

 

这个案例中gallery的main thread在执行UploaderChimeraService的onDestroy方法时,需要lock 0x23f65d8b,但这个lock有被upload_periodic GCM Task 拿住,这个thread当前是在做连接网络的动作。从这段信息来看,很有可能与测试时手机连接的网络有关,当时连接的事google的网络,由于墙的原因,无法连接gms的相关server有关

还有一种情况就是死锁,即形成了头尾相连,互相等待的情况,对于这种问题以及上面案例的解决,一般会尝试将锁改为超时锁,比如lock的trylock,超时会自动释放锁,从而避免一直持有锁的情况发生

案例三:卡在IO上
这种情况一般是和文件操作相关,判断是否是这种情况,可以看mainlog中搜索关键字"ANR in",看这段信息的最下边,比如下面的信息
ANRManager: 100% TOTAL: 2% user + 2.1% kernel + 95% iowait + 0.1% softirq
很明显,IO占比很高,这个时候就需要查看trace日志看当时的callstack,或者在这段ANR点往前看0~4s,看看当时做的什么文件操作,这种场景有遇到过,常见解决方法是对耗时文件操作采取异步操作

案例四:主线程有耗时的动作
这种情况是ANR类型问题里遇到最多的,比如网络访问,访问数据库之类的,都很容易造成主线程堵塞,

这里以访问数据库来说,这类型引起的ANR,一般来讲看当时的CPU使用情况会发现user占比较高,看trace中主线程当时的信息会发现会有一些比如query像ContentProvider这种数据库的动作。这种情况下,还可以去看eventlog或者mainlog,在ANR发生前后打印出来的信息,比如访问数据库这种,在eventlog中搜索"am_anr",然后看前后片段,会发现发生ANR的这个进程有很多数据库相关的信息,说明在发生ANR前后主线程一直在忙于访问数据库,这类型的问题常见于图库,联系人,彩短信应用。

所以这种问题的解决,一般考虑的是异步解决,异步解决并不是简单的new一个线程,要根据业务场景以及频率来决定,Android常见的异步AsyncTask, IntentService, 线程池(官方四种或自定义), new thread等,一般来说不建议直接new thread

案例五:binder线程池被占满
系统对每个process最多分配15个binder线程,这个是谷歌的设计(/frameworks/native/libs/binder/ProcessState.cpp)
如果另一个process发送太多重复binder请求,那么就会导致接收端binder线程被占满,从而处理不了其它的binder请求
,这时候请求端发起的请求就会阻塞等待了(未设置异步请求的前提下),这本身就是系统的一个限制,如果应用未按照系统的要求来实现对应逻辑,那么就会造成问题。
而系统端是不会(也不建议)通过修改系统行为来兼容应用逻辑,否则更容易造成其它根据系统需求正常编写的应用反而出现不可预料的问题。
判断Binder是否用完,可以在trace中搜索关键字"binder_f",如果搜索到则表示已经用完,然后就要找log其他地方看是谁一直在消耗binder或者是有死锁发生

之前有遇到过压力测试手电筒应用,出现BInder线程池被占满情况,解决的思路就是降低极短时间内大量Binder请求的发生,修复的手法是发送BInder请求的函数中做时间差过滤,限定在500ms内最多执行一次

案例六:JE或者NE导致ANR

这种场景有遇到过,ANR前出现频繁NE,NE所在的进程与ANR的进程有交互,在解决了NE后,ANR也不复存在,对于这类在ANR前有JE或者NE,一般思路是先解决JE或NE,因为JE/NE发生时会去dump一大堆异常信息,本身也会加重CPU loading,修改完异常后再来看ANR是否还存在,如果还存在,那么就看trace 堆栈,如果不存在,则可以基本判定是JE或NE导致

案例七:只存在于Monkey测试下
有些问题是只有在Monkey环境下才能跑出来,平时的user版本用户使用是不会出现的,这种问题的话就没有改动的意义。
比如下面这个例子:
ActivityManager: Not finishing activity because controller resumed
03-18 07:25:50.901 810 870 I am_anr : [0,25443,android.process.media,1086897733,Input dispatching timed out (Waiting because no window has focus but there is a focused application that may eventually add a window when it finishes starting up.)]
发生这个ANR的原因是Contoller将resume的操作给拦截了, 导致Focus不过去, 从而导致ANR,User版本不会有Contoller, 所以不会出现这个 ANR. 所以这个 ANR 可以忽略.

你可能感兴趣的:(Android,framework之积累,Android系统调试方法)