BUG实例分析一:死机
目录 |
一、现象
手机睡眠后按power键无法正常唤醒,具体表现如下:
现象一:无法点亮屏幕,按任意键无响应;
现象二:可以点亮屏幕,但触屏、按键无响应,电话也无法呼入,但插入usb偶尔可以更新状态栏时间
二、初步判断
从log上看,有两个案例log是有明显异常,在不停地打印同一条log,由于这些log是在有触屏事件时会出现的(包括但不限于),因此初步判断是input(如按键、触屏)事件出现异常,导致系统的input机制拥挤阻塞,但由于input及powerManager很多log屏蔽,无法作出进一步有效判断,故这里提供一个增加了大量log的image,请协助测试一下,力争抓到log(如能发掘必现规律就更好了)。
三、解决思路
一方面,由于input消息流程中大部分log是关闭的,因此仅从产品上抓取的log消息不足以定位问题点,因此决定发一版包含input所有log并在关键函数加入了不少log的软件给品质部测试(同时也鼓励内部试用机用户烧写这个软件),并且要求品质部一旦发现此问题立马将现场保留,以便我们进行adb分析。
另一方面,之前今天有过一台出现此问题的机器,在不停地打印按键的 "repeatCount=xxx",并且这个数值一直在递增,因此我们也做了第二手准备:即如果短期内无法重现这个问题,那则在打印 "repeatCount"这个地方进行判断,当 "repeatCount"大于某个值时则认为出现了异常,这样则重启手机。如此一来,至少问题会得到恢复,而不至于一直处理“死机”状态而不得不拔电池。这是为了不影响发货不得已的临时解决方案。
四、终得真机分析
几天过去,品质部同事重现了此问题!
adb Go!!!
1、adb logcat,按键,inputDispatcher中的 notifyKey有无log产生?——无
2、adb shell getevent,按键,是否能接收到按键消息?——是
==>驱动ok,问题发生在了framework
3、动态卸载、添加event2 ——无论卸载还是添加,均不成功
==>怀疑 inputReader所在的 Thread阻塞
4、查看anr ——InputReader处于 WAIT状态,栈直指 interceptKeyBeforeQueueing中所调用的某一个函数,而这个函数调用了ContentResolver中的parse操作
==>这就是卡的地方?!
五、验证
又在上述关键的地方加了些log,发个了软件(品质的同事辛苦了!),幸运的是这次不到一天便出现了三台问题机器,其中有一台的log显示:
7824 07-14 15:53:03.688 D/InputDispatcher( 278): before call interceptKeyBeforeQueueing, 2816 7825 07-14 15:53:03.688 W/WindowManager( 278): interceptKeyBeforeQueueing start 7888 07-14 15:53:03.694 W/WindowManager( 278): interceptKeyBeforeQueueing end 7891 07-14 15:53:03.694 D/InputDispatcher( 278): after call interceptKeyBeforeQueueing, 2818 7917 07-14 15:53:03.697 D/InputDispatcher( 278): before call interceptKeyBeforeQueueing, 2816 7918 07-14 15:53:03.697 W/WindowManager( 278): interceptKeyBeforeQueueing start 7923 07-14 15:53:03.699 W/WindowManager( 278): interceptKeyBeforeQueueing end 7926 07-14 15:53:03.699 D/InputDispatcher( 278): after call interceptKeyBeforeQueueing, 2818 22072 07-14 15:57:54.647 D/InputDispatcher( 278): before call interceptKeyBeforeQueueing, 2816 22073 07-14 15:57:54.647 W/WindowManager( 278): interceptKeyBeforeQueueing start 22135 07-14 15:57:54.652 W/WindowManager( 278): interceptKeyBeforeQueueing end 22141 07-14 15:57:54.654 D/InputDispatcher( 278): after call interceptKeyBeforeQueueing, 2818 22186 07-14 15:57:54.887 D/InputDispatcher( 278): before call interceptKeyBeforeQueueing, 2816 22187 07-14 15:57:54.887 W/WindowManager( 278): interceptKeyBeforeQueueing start 22192 07-14 15:57:54.888 W/WindowManager( 278): interceptKeyBeforeQueueing end 22195 07-14 15:57:54.888 D/InputDispatcher( 278): after call interceptKeyBeforeQueueing, 2818 40764 07-14 16:01:19.988 D/InputDispatcher( 278): before call interceptKeyBeforeQueueing, 2816 40765 07-14 16:01:19.988 W/WindowManager( 278): interceptKeyBeforeQueueing start 40767 07-14 16:01:19.989 W/WindowManager( 278): interceptKeyBeforeQueueing end 40770 07-14 16:01:19.989 D/InputDispatcher( 278): after call interceptKeyBeforeQueueing, 2818 40799 07-14 16:01:20.404 D/InputDispatcher( 278): before call interceptKeyBeforeQueueing, 2816 40800 07-14 16:01:20.404 W/WindowManager( 278): interceptKeyBeforeQueueing start
完美地验证了之前的猜测!
六、后续规避
这个问题的原因主要是在关键的路径(所有input事件都要调用的函数)上,增加了比较复杂的、有风险的ContentResolver操作,而一旦操作超时则导致整个系统的input系统瘫痪,用户无法操作,手机也无法自动恢复,只有拔电池了。
所以在后续framework的修改时,一定要考虑到所作更改的影响,在这些地方的更改也尽量要使用简单的逻辑、可靠数据操作方式进行,同时也要兼顾到性能方面的影响,如这里的更改就会导致每个input事件都要去操作ContentResolver,且不论是否可靠,单从性能上讲也不太可取。