最近基于海思3716c方案的智能机顶盒批量出货了,但出现了意想不到的问题。有少数机顶盒在开机动画停留很长时间有5分钟之多,之后黑屏,不进入Launcher,就像死机了一样。此问题出现概率达到百分之二,概率很高。
根据此现象,联想到开发阶段出现的一个类似情形,机顶盒开机后一直进不了主界面。当时没有大规模出现,认为可能是某人的误操作。当时重新格式化了data分区后,机器就又正常运行了。现在回想起来,感觉开发人员神经太大条了,这么严重的问题都放过。开发阶段遇到的偶然问题,在真正用户使用阶段就可能大规模出现,成为致命问题。
将问题机顶盒连上串口,发现还能进入命令行,还能输入命令,敲logcat还能打印出日志,还算好办。日志的最后部分如下:
I/sysproc ( 1545): Entered system_init() I/sysproc ( 1545): ServiceManager: 0x1219320 D/SensorService( 1545): nuSensorService starting...I/sysproc ( 1545): System server: starting Android runtime. I/sysproc ( 1545): System server: starting Android services. I/SystemServer( 1545): Entered the Android system server! I/sysproc ( 1545): System server: entering thread pool. D/SensorService( 1545): nuSensorService thread starting... I/SystemServer( 1545): Samba Service I/SystemServer( 1545): NFS Service I/SystemServer( 1545): Entropy Service I/SystemServer( 1545): Power Manager I/SystemServer( 1545): Activity Manager E/ ( 1424): [86400205 ERROR-tuner]:HI_UNF_TUNER_GetSNR[1748]:SIGNAL DROP E/ ( 1424): [86400205 ERROR-tuner]:HI_UNF_TUNER_GetSignalStrength[2012]:SIGNAL DROP I/ActivityManager( 1545): Memory class: 64 W/UsageStats( 1545): Usage stats version changed; dropping
根据日志分析,可能停在system_server的初始化阶段。ps一下看到最后的进程就是system_server。打印如下:
root 1030 1 3808 344 ffffffff 400fac74 S /system/bin/xcmidware root 1035 1 780 408 c007bf30 400a0e54 S /system/bin/sh root 1036 1 1380 148 ffffffff 0000825c S /sbin/adbd root 1316 2 0 0 bf0a1ca4 00000000 S MixerEngineTask root 1347 2 0 0 bf98bb28 00000000 S x5hdqam_sop root 1367 2 0 0 c00a1e98 00000000 S galcore workque root 1368 2 0 0 c00aba1c 00000000 S galcore daemon system 1421 1 137036 10728 ffffffff 40059670 S /system/bin/surfaceflinger root 1422 1 14820 3712 ffffffff 40105670 S /system/bin/updserver root 1423 1 14712 3676 ffffffff 4008d670 S /system/bin/fpserver root 1424 1 29380 6596 ffffffff 400fdf78 S /system/bin/cy_dvb_daemon root 1430 2 0 0 c0099a68 00000000 D HDMI_kthread root 1431 2 0 0 c0099a68 00000000 D HDMI_kCEC system 1545 1022 455764 33108 ffffffff 4003e670 S system_server root 1661 1035 960 336 00000000 400df438 R ps
找了一个正常的机顶盒,抓了个日志,看到Activity Manager下一个初始化项为Telephony Registry。
I/sysproc ( 1545): System server: starting Android runtime. I/sysproc ( 1545): System server: starting Android services. I/sysproc ( 1545): System server: entering thread pool. D/SensorService( 1545): nuSensorService thread starting... I/SystemServer( 1545): Entered the Android system server! I/SystemServer( 1545): Samba Service I/SystemServer( 1545): NFS Service I/SystemServer( 1545): Entropy Service I/SystemServer( 1545): Power Manager I/SystemServer( 1545): Activity Manager I/ActivityManager( 1545): Memory class: 64 I/ActivityManager( 1545): Enabled StrictMode logging for AThread's Looper I/SystemServer( 1545): Telephony Registry I/SystemServer( 1545): Package Manager D/dalvikvm( 1545): GC_CONCURRENT freed 265K, 4% free 9345K/9671K, paused 2ms+3ms D/dalvikvm( 1545): GC_CONCURRENT freed 285K, 5% free 9515K/9927K, paused 2ms+3ms E/StrictMode( 1545): A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
继续回到问题机顶盒,敲入busybox top查看,system_server的CPU使用率竟然占到了90%
PID PPID USER STAT VSZ %MEM CPU %CPU COMMAND 2441 1042 0 R 1120 0.1 0 9.0 busybox top 1565 1030 1000 S 631m 89.2 0 90.0 system_server
怀疑是system_server在初始化 Activity Manager过程中陷入死循环了。再多次仔细对比日志,发现有个蹊跷打印UsageStats,正常和问题状态不一样。去读frameworks/base/services/java/com/android/server/am/UsageStatsService.java的代码,发现这个文件会读取文件,怀疑操作文件的时候出问题了。
W/UsageStats( 1545): Usage stats version changed; dropping
运行lsof查看程序打开文件情况,与data分区相关的就是如下几项。与system_server相关的就是usage-history.xml
keystore 1031 keystore cwd ??? ??? ??? ??? /data/misc/keystore nfsinit 1032 root 7 ??? ??? ??? ??? /data/exports system_se 1548 system 52 ??? ??? ??? ??? /data/system/usagestats/usage-history.xml
为了加快速度找到错误准确地方,想到了用eclipse中adt插件的ddms功能。因system_server是java程序,应该可以看到堆栈情况。
通过ddms很快看到堆栈地方,停在了readHistoryStatsFLOCK(), while (eventType != XmlPullParser.START_TAG) 陷入了死循环,当usage-history.xml文件内容为空时,处理不对。从代码得知没有判断内容为空parser.next()返回的是XmlPullParser.END_DOCUMENT的情况。
修改为:
//Slog.w(TAG,"windsome eventType=" + eventType + ", START_TAG="+XmlPullParser.START_TAG+", TEXT="+XmlPullParser.TEXT+", END_TAG="+XmlPullParser.END_TAG+", START_DOCUMENT="+XmlPullParser.START_DOCUMENT+", END_DOCUMENT="+XmlPullParser.END_DOCUMENT); while ((eventType != XmlPullParser.START_TAG) && (eventType != XmlPullParser.END_DOCUMENT)) { eventType = parser.next(); //Slog.w(TAG,"windsome eventType="+eventType); } //Slog.w(TAG,"windsome 2");
奇怪的是,手机上很少出现此问题,可能跟手机是用电池供电有关吧,很少遇到写usage-history.xml时断电的情况。
最近发现之前发的一批盒子陆续出现这个进不了Launcher的问题,有必要去查一下到底什么地方什么时候去写usage-history.xml文件了。writeStatsToFile()函数写了此文件。函数介绍中写了“Conditionally start up a disk write if it's been awhile, or the day has rolled over.”,看来这个函数会经常被调用。shutdown(),notePauseComponent(),noteLaunchTime(),dump()都调用了这个函数。
./services/java/com/android/server/am/ActivityManagerService.java中的updateUsageStats()调用了notePauseComponent()。然后reportResumedActivityLocked(),ActivityStack.java中的startPausingLocked()调用了updateUsageStats()。
./services/java/com/android/server/am/ActivityRecord.java中windowsDrawn()调用了noteLaunchTime()。
shutdown()在正常关机时调用。
从调用流程上看很多地方都去写usage-history.xml了,特别是activity的各个活动阶段,此文件读写还是很频繁的。突然断电导致此文件出问题,也不足为奇了。