引言
每天收到无数的兄弟团队的同事向系统转ANR JIRA,有些一旦遇到App ANR就直接转到系统组,有些简单看一下就转到系统组帮忙看一下。如此浩瀚的JIRA,我们什么事不做也处理不过来,请每个App owner责任各自ANR.
- APP Team要先有较深入分析,真正确定是系统问题,再转JIRA
- 请勿看到binder或者message耗时就直接转到系统,几乎系统所有的通信都离不开这两个基本功能,存在这个不能直接说明问题,要能从逻辑上说得通是系统的问题
- 如果看完本文,分析完一遍之后,还是无法深入分析,可能是信息不足,可考虑降低优先级,等待用户再反馈;如果同时概率非常低,可考虑先关闭
如有不懂,系统组可以提供相关帮助,原则上自家的APP自家负责,谢谢大家的理解。下面就讲一讲分析ANR的常见套路:
一. ANR类型及常见原因
1.1 ANR定义
Application Not Responding:即应用无响应
如果应用程序主线程在超时时间内对输入事件没有处理完毕,或者对特定操作没有执行完毕,就会出现ANR
(主线程在特定的时间内没有做完特定的事情)
1.2.ANR类型
1)KeyDispatchTimeout-主要类型按键或触摸事件,input事件在5S内没有处理完成发生ANR
发生KeyDispatchTimeout类型的ANR日志中出现的关键字:Reason: Input dispatching timed out xxxx
2)ServiceTimeout-bind,create,start,unbind等在主线程处理耗时,前台Service在20s内,后台Service在200s内没有处理完成发生ANR
发生ServiceTimeout类型的ANR日志中出现的关键字:Timeout executing service:/executing service XXX
3)BroadcastTimeout- BroadcastReceiver onReceiver处理事务时前台广播在10S内,后台广播在60s内没有处理完成发生ANR
发生BroadcastTimeout类型的ANR日志中出现的关键字:Timeout of broadcast XXX/Receiver during timeout:XXX/Broadcast of XXX
4)ProcessContentProviderPublishTimedOutLocked-ContentProvider publish在10s内没有处理完成发生ANR
发生ProcessContentProviderPublishTimedOutLocked类型的ANR日志中出现的关键字:timeout publishing content providers
1.3.常见的原因
A.主线程耗时操作,如复杂的layout,庞大的for循环,IO等。
B.主线程被子线程同步锁block
C.主线程被Binder 对端block
D.Binder被占满导致主线程无法和SystemServer通信
E.得不到系统资源(CPU/RAM/IO)
二.ANR分析
ANR问题发生时,对于用户的直观感受是弹框,给用户取消和等待的选择,但是对于分析ANR的开发者来说,发生ANR的过程中,系统会自动的获取并打印日志信息,从这些log中我们可以获取ANR的类型,CPU的情况(CPU使用率过高有可能是CPU饥饿导致了ANR;CPU使用率过低说明主线程被block了),每种ANR类型在规定时间内执行的具体操作等等。log输出以外,你会发现各个应用进程和系统进程的函数堆栈信息都输出到了一个/data/anr/traces.txt的文件中,这个文件是分析ANR原因的关键文件.要获取到该文件可使用adb指令进行赋权后拉出查看调用stack。除此之外,为了更加准确方便的分析ANR问题,我们在系统中还添加了一些额外的信息。以下将通过log、trace.txt,附加的额外信息以及结合代码分析ANR的前因后果。
分析ANR大致分为以下几个步骤:
1.确定ANR发生时间,关键字:am_anr,ANR in
2.查看ANR发生时打印的trace,文件目录:/data/anr/traces.txt,MIUI系统还会有附加信息关键字:MIUI-BLOCK-MONITOR
这里可以在traces.txt文件中查看导致ANR的错误栈,搜索关键字["main" prio=5 tid=1] 其中prio和tid后的值,可以根据文中的情况修改(调用栈一般在文件最后,可以先搜索"main"来确定后面的prio和tid),这样可以找到ANR时main线程的调用栈情况
3.查看系统耗时关键字:binder_sample,dvm_lock_sample,am_lifecycle_sample,binder thread
4.结合源码和以上的信息进行分析
以下将对这几个步骤做进一步的分析.
2.1. 确定发生ANR的时间具体时间点
2.1.1. ANR发生的日志中,为了定位ANR发生的时间点,可以搜索Events log中的关键字:am_anr
eg:
12-17 06:02:14.463 1566 1583 I am_anr : [0,8769,com.android.updater,952680005,Broadcast of Intent { act=android.intent.action.BOOT_COMPLETED flg=0x9000010 cmp=com.android.updater/.BootCompletedReceiver (has extras) }]
根据这行log我们确定的信息
发生anr的时间点:12-17 06:02:14.463,
进程pid:8769,
进程名:com.android.updater,
发生ANR的类型是:BroadcastTimeout,
发生的具体类或者原因:{ act=android.intent.action.BOOT_COMPLETED flg=0x9000010 cmp=com.android.updater/.BootCompletedReceiver (has extras) }
2.1.2. 与之相对应的system log,也就是ANR类型中出现的关键字:
eg:
12-17 06:02:00.370 1566 1583 W BroadcastQueue: Timeout of broadcast BroadcastRecord{21ef8c2 u0 android.intent.action.BOOT_COMPLETED} - receiver=android.os.BinderProxy@2a6c365, started 60006ms ago
12-17 06:02:00.370 1566 1583 W BroadcastQueue: Receiver during timeout: ResolveInfo{5a8283a com.android.updater/.BootCompletedReceiver m=0x108000}
这行日志中很多信息是和Events log是重合的,但是我们我们可以通过" started 60006ms ago"可以知道这信息是在1分多钟之前执行的,现在还没有执行结束,已经达到了发生ANR规定的时间,可以确定这是个后台广播,所以我们可以把信息锁定在时间点:12-17 06:02:00.370往前推算的一分钟内,观察在这段时间内具体执行了哪些操作,导致后台广播没有执行完成.
2.1.3. 关键字:ANR in
大家在分析ANR的时候,也往往会检查这个关键字包含的相关内容,这里面包含了CPU的负载,CPU的统计时间段,使用率等信息
eg:
12-17 06:02:19.286 1566 1583 E ActivityManager: ANR in com.android.updater(进程名)
12-17 06:02:19.286 1566 1583 E ActivityManager: PID: 8769(进程pid)
12-17 06:02:19.286 1566 1583 E ActivityManager: Reason: Broadcast of Intent { act=android.intent.action.BOOT_COMPLETED flg=0x9000010 cmp=com.android.updater/.BootCompletedReceiver (has extras) }(原因)
12-17 06:02:19.286 1566 1583 E ActivityManager: Load: 0.0 / 0.0 / 0.0(Load表明是1分钟,5分钟,15分钟CPU的负载)
12-17 06:02:19.286 1566 1583 E ActivityManager: CPU usage from 0ms to 18846ms later (2017-12-17 06:02:00.379 to 2017-12-17 06:02:19.224):
12-17 06:02:19.286 1566 1583 E ActivityManager: 95%(CPU的使用率)6142/com.immomo.momo: 95% (用户态的使用率)user + 0% (内核态的使用率)kernel
12-17 06:02:19.286 1566 1583 E ActivityManager: 2.3% 8170/com.tencent.mm: 2.3% user + 0% kernel / faults: 448 minor
12-17 06:02:19.286 1566 1583 E ActivityManager: 0.7% 1566/system_server: 0.4% user + 0.3% kernel / faults: 150 minor (高速缓存的缺页次数)1 major(内存的缺页次数)
12-17 06:02:19.286 1566 1583 E ActivityManager: 0.4% 90/kworker/u16:3: 0% user + 0.4% kernel
12-17 06:02:19.286 1566 1583 E ActivityManager: 0.3% 4704/com.tencent.mm:push: 0.1% user + 0.2% kernel / faults: 116 minor
12-17 06:02:19.286 1566 1583 E ActivityManager: 0.3% 8769/com.android.updater: 0.2% user + 0.1% kernel / faults: 1600 minor 2 major
12-17 06:02:19.286 1566 1583 E ActivityManager: 0.2% 4790/com.tencent.mm:patch: 0.2% user + 0% kernel / faults: 748 minor
12-17 06:02:19.286 1566 1583 E ActivityManager: 0.2% 329/mmc-cmdqd/0: 0% user + 0.2% kernel
12-17 06:02:19.286 1566 1583 E ActivityManager: 0.2% 5429/com.tencent.mm:push: 0% user + 0.1% kernel / faults: 17 minor
12-17 06:02:19.286 1566 1583 E ActivityManager: 0.2% 5435/com.tencent.mm:patch: 0.2% user + 0% kernel / faults: 82 minor
12-17 06:02:19.286 1566 1583 E ActivityManager: 0.2% 8712/com.tencent.mm:exdevice: 0.1% user + 0% kernel
注意:1.ANR in对应的ANR的时间点往往是比am_anr的时间点是靠后的,一般不使用这个时间确定ANR发生的具体时间点
2.cpu占用95%有时候并不算高,在多核中每个核最大占用率都是100%(八核占用率是800%)
2.2. ANR发生时打印的堆栈和系统附加信息
2.2.1. trace信息
发生ANR时,各个应用进程和系统进程的函数堆栈信息都输出到了一个/data/anr/traces.txt的文件.我们往往比较关注的是发生ANR的应用进程主线程具体执行堆栈,我们可以知道在发生ANR这个时间点主线程具体在做什么,为什么卡住了,是等锁,binder call调用还是主线程存在耗时的操作等等.
eg:
main(线程名)、prio(线程优先级,默认是5)、tid(线程唯一标识ID)、Sleeping(线程当前状态)
"main" prio=5 tid=1 Sleeping
| group="main" sCount=1 dsCount=0 obj=0x73132d10 self=0x5598a5f5e0
//sysTid是线程号(主线程的线程号和进程号相同)
| sysTid=17027 nice=0 cgrp=default sched=0/0 handle=0x7fb6db6fe8
| state=S schedstat=( 420582038 5862546 143 ) utm=24 stm=18 core=6 HZ=100
| stack=0x7fefba3000-0x7fefba5000 stackSize=8MB
| held mutexes=
// java 堆栈调用信息(这里可查看导致ANR的代码调用流程)(分析ANR最重要的信息)
at java.lang.Thread.sleep!(Native method)
- sleeping on <0x0c60f3c7> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:1031)
- locked <0x0c60f3c7> (a java.lang.Object) // 锁住对象0x0c60f3c7
at java.lang.Thread.sleep(Thread.java:985)
at android.os.SystemClock.sleep(SystemClock.java:120)
at org.code.ipc.MessengerService.onCreate(MessengerService.java:63) //导致ANR的代码
at android.app.ActivityThread.handleCreateService(ActivityThread.java:2877)
at android.app.ActivityThread.access$1900(ActivityThread.java:150)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1427)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke!(Native method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
2.2.2. 附加信息
在前面也说过为更准确更方便分析anr问题,我们在系统中添加了一些额外的信息供分析
如何提升ANR问题分析效率---这篇文章有具体分析,大家可以具体阅读这篇文章,这里就不再详细的讲述
我们在获取ANR日志后需要关注的关键字:MIUI-BLOCK-MONITOR
1.我们对每一个Message都额外记录了它各个状态的时间点,方便我们进行分析
planTime:消息计划被执行的时间点
dispatchTime:消息真正被执行的时间点
finishTime:消息完成时的时间点
2.当主线程某一个消息处理时间大于2s(开发版及稳定版是3秒)时,打印出一个警告日志
MIUI系统可在日志中搜索"MIUI-BLOCK-MONITOR: The msg"关键字
3.当主线程的某一个binder call耗时超过3s时,打印出一个警告日志,并打印出对应的调用栈
MIUI系统可在日志中搜索"MIUI-BLOCK-MONITOR: The binder call"关键字
4.当主线程的某个input事件处理时间大于2s时,打印出一个警告日志
MIUI系统可在日志中搜索"MIUI-BLOCK-MONITOR: The input关键字
5.在发生anr时,会在/data/anr/目录下生成anr_info_processName_time.txt文件,它会记录一些anr发生时的信息
5.1 anr发生前app主线程的耗时消息记录(超过200ms的消息会被记录)
可搜索"History of long time messages"关键字。
需要注意:记录最多保留50条,按完成时间降序排列,如果短时间内处理消息过多,真正耗时的消息可能会被覆盖,这时可在日志中搜索耗时纪录。
5.2 anr发生时正在处理的消息,如果有的话
可搜索"Running message"关键字
5.3 主线程未执行的剩余消息
可搜索"MessageQueue on Looper"关键字
下面是如何提升ANR问题分析效率文章中的一个例子:
eg:
ANR in com.sankuai.meituan (com.sankuai.meituan/.activity.Welcome)
PID: 25544
Reason: 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.)
Dump time : 2016-11-30 22:07:03.756+0800
Running message is { when=-5s363ms what=110 obj=AppBindData{appInfo=ApplicationInfo{e612c25 com.sankuai.meituan}} target=android.app.ActivityThread$H planTime=1480514818393 dispatchTime=1480514818409 finishTime=0 }
先解释一下"Running message"各个重要参数的意义:
when是这个消息需要被执行的时间点,是基于当前dump的时间算出来的一个时间差
注意:history中的消息,when值没有参考意义,它只是消息完成时的一个快照,
还有一点需要特别注意通过Handler.sendMessageAtFrontOfQueue方法设置的消息,它的uptimeMillis设置的是0,
这时候的when值是(SystemClock.uptimeMillis() - 0),对于这种消息不能简单看when值,需要通过以下三个值才能做出判断。
planTime是消息计划被执行的时间点
dispatchTime是消息真正被执行的时间点
finishTime是消息完成时的时间点
上面的例子,消息A应该在22:06:58.393(22:07:03.756-5s363ms)这个时间点被执行,
消息等待执行的时间:dispatchTime-planTime=16ms
这个消息当前已经执行的时间:5s363ms-16ms=5s347ms,
也就是anr发生时what=110这个消息己经执行了5s347ms,这是有问题的
在这种情况下,抓取的trace是准确的,可以根据trace栈,进行分析了。
查看对应主线程的trace栈,刚好是what=110(ActivityThread.BIND_APPLICATION)这个消息,都匹配上了:
88651 at java.lang.Runtime.nativeLoad(Native method)
88652 at java.lang.Runtime.doLoad(Runtime.java:435)
88653 - locked <0x0f7e5bd6> (a java.lang.Runtime)
88654 at java.lang.Runtime.loadLibrary(Runtime.java:370)
88655 at java.lang.System.loadLibrary(System.java:1076)
88656 at com.meituan.android.common.performance.statistics.crash.ExceptionHandler.initNative(ExceptionHandler.java:43)
88657 at com.meituan.android.common.performance.statistics.crash.ExceptionHandler.registerExceptionHandler(ExceptionHandler.java:59)
88658 at com.meituan.android.common.performance.statistics.crash.CrashStatistics.start(CrashStatistics.java:49)
88659 at com.meituan.android.common.performance.statistics.crash.CrashStatisticsManager.start(CrashStatisticsManager.java:44)
88660 at com.meituan.android.common.performance.PerformanceManager.start(PerformanceManager.java:228)
88661 at com.sankuai.meituan.MeituanApplication.onCreate(MeituanApplication.java:2232)
88662 at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1014)
88663 at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4737)
88664 at android.app.ActivityThread.access$1600(ActivityThread.java:154)
88665 at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1423)
88666 at android.os.Handler.dispatchMessage(Handler.java:102)
88667 at android.os.Looper.loop(Looper.java:164)
88668 at android.app.ActivityThread.main(ActivityThread.java:5452)
88669 at java.lang.reflect.Method.invoke!(Native method)
88670 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:738)
88671 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:628)
最终耗时,可以从日志中查看,这个消息最终在07:05的时候才执行完,消息的整个处理耗时7075ms,其中dispatch之后耗时7059ms。
23145 11-30 22:07:05.469 25544 25544 W MIUI-BLOCK-MONITOR: The msg { when=-7s75ms what=110 obj=AppBindData{appInfo=ApplicationInfo{e612c25 com.sankuai.meituan}} target=android.app.ActivityThread$H
planTime=1480514818393 dispatchTime=1480514818409 finishTime=1480514825468 } took 7075ms and took 7059ms after dispatch
注意:1.在查看堆栈的过程中,我们需要和第1)步中的ANR的时间,pid等点结合起来,有时候trace的时间点是和真正发生ANR的时间是对不上,说明抓取的trace已经错过了现场
2.有时候"Running message"和打印的trace信息也未必对的上,这个在如何提升ANR问题分析效率有更加详细的介绍,具体问题还需具体分析
2.3.关于binder call ,关于系统耗时
在日志分析中我们根据上面系统附加的信息,我们往往可以看到以下binder call的信息,
eg:
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: The binder call took 68921ms.
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: java.lang.Throwable
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at android.os.AnrMonitor.checkBinderCallTime(AnrMonitor.java:591)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at android.os.BinderProxy.transact(Binder.java:623)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at android.content.pm.IPackageManager$Stub$Proxy.getApplicationInfo(IPackageManager.java:2658)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at android.app.ApplicationPackageManager.getApplicationInfoAsUser(ApplicationPackageManager.java:340)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at android.app.ApplicationPackageManager.getApplicationInfo(ApplicationPackageManager.java:333)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at miui.core.ManifestParser.create(SourceFile:64)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at miui.core.SdkManager.start(SourceFile:186)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at java.lang.reflect.Method.invoke(Native Method)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at miui.external.Application.c(SourceFile:101)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at miui.external.Application.attachBaseContext(SourceFile:165)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at com.mipay.wallet.App.attachBaseContext(SourceFile:13)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at android.app.Application.attach(Application.java:193)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at android.app.Instrumentation.newApplication(Instrumentation.java:1009)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at android.app.Instrumentation.newApplication(Instrumentation.java:993)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at android.app.LoadedApk.makeApplication(LoadedApk.java:800)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5516)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at android.app.ActivityThread.-wrap2(ActivityThread.java)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1619)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at android.os.Handler.dispatchMessage(Handler.java:102)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at android.os.Looper.loop(Looper.java:163)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at android.app.ActivityThread.main(ActivityThread.java:6379)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at java.lang.reflect.Method.invoke(Native Method)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:904)
12-17 02:08:44.587 5887 5887 W MIUI-BLOCK-MONITOR: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
看到这些日志打印的时候,有时候给人的第一感觉是系统出了问题,跟binder有关,其实我们可以进一步确定问题
首先我们可以确定系统是否有耗时信息,参考文章---系统耗时分析方案
1.binder_sample
1.功能说明: 监控每个进程的主线程的binder transaction的耗时情况, 当超过阈值时,则输出相应的目标调用信息.
2.云控参数: persist.sys.binder.transact_ms (默认值为 1000ms). 当该值小于0则关闭该功能.
3.log格式: 52004 binder_sample (descriptor|3),(method_num|1|5),(time|1|3),(blocking_package|3),(sample_percent|1|6)
4.log实例: 2754 2754 I binder_sample: [android.app.IActivityManager,35,2900,android.process.media,5]
5.log解析:
1.主线程2754;
2.执行android.app.IActivityManager接口
3. 所对应方法code =35(即STOP_SERVICE_TRANSACTION),
4. 所花费时间为2900ms.
5. 该block所在package为 android.process.media
最后一个参数是sample比例(没有太大价值)
2. dvm_lock_sample
1.功能说明: 当某个线程等待lock的时间blocked超过阈值,则输出当前的持锁状态 ;
2.云控参数: persist.vm.lockprof.threshold (默认值为 500ms)
3.log格式: 20003 dvm_lock_sample (process|3),(main|1|5),(thread|3),(time|1|3),(file|3),(line|1|5),(ownerfile|3),(ownerline|1|5),(sample_percent|1|6)
4.log实例: dvm_lock_sample: [system_server,1,Binder_9,1500,ActivityManagerService.java,6403,-,1448,0]
5.log解析: system_server: Binder_9,执行到ActivityManagerService.java的6403行代码,一直在等待AMS锁, "-"代表持锁的是同一个文件,
即该锁被同一文件的1448行代码所持有, 从而导致Binder_9线程被阻塞1500ms.
3.am_lifecycle_sample
1.功能说明: 当app在主线程的生命周期回调方法执行时间超过阈值,则输出相应信息;
2.云控参数: persist.sys.handler.threshold (默认值为3000ms)
3.log格式: am_lifecycle_sample (User|1|5),(Process Name|3),(MessageCode|1|5),(time|1|3)
4.log实例: 02-23 11:02:35.876 8203 8203 I am_lifecycle_sample: [0,com.android.systemui,114,3837]
5.log解析: pid=8203, processName=com.android.systemui, MessageCode=114(CREATE_SERVICE), 耗时3.827s
注意: MessageCode=200 (并行广播onReceive耗时), 其他Code见 ActivityThread.H类
4. binder thread
1.功能说明: 当system_server等进程的线程池使用完, 无空闲线程时, 则binder通信都处于饥饿状态, 则饥饿状态超过一定阈值则输出信息;
2.云控参数: persist.sys.binder.starvation (默认值16ms)
3.log实例: 1232 1232 "binder thread pool (16 threads) starved for 100 ms"
4.log解析: system_server进程的 线程池已满的持续长达100ms
eg:
以上的binder call 信息,我们查找日志中的持锁信息
12-17 02:08:44.559 1999 4899 I dvm_lock_sample: [system_server,1,Binder:1999_16,68916,PackageManagerService.java,3537,UserManagerService.java,3380,0]
12-17 02:08:44.696 1999 4131 I dvm_lock_sample: [system_server,1,Binder:1999_14,61042,PackageManagerService.java,3537,UserManagerService.java,3380,0]
PackageManagerService.java第3537需要的锁被UserManagerService.java中的3380行持有,我们再结合源码查看UserManagerService.java中的3380行是因为什么原因导致持锁耗时
注意:binder call出现耗时,有binder在通信过程中因为繁忙造成,也有可能因为对端持锁或者执行一些耗时的操作耗时,日志中打印binder call信息表明binder call与远程端通信已经结束,出现binder call信息也不不代表framework出现问题,需要根据日志分析准确定位.
2.4. 案例
http://jira.n.xiaomi.com/browse/MIUI-1021360
http://jira.n.xiaomi.com/browse/MIUI-1017959
2.5. ANR分析流程
三.ANR分析工具
3.1. 工具使用介绍
为了减少ANR分析的时间,提高分析效率,开发了一套ANR分析工具LogAnalyse:LogAnalyser使用说明书,这里面有详细的配置和使用说明
LogAnalyse中可以把log进行拆分,对于ANR日志能提取其中一些主要的信息放在不同的文件中
1.日志拆分 LogAnalyser <file_path>
例如:LogAnalyser /home/XXXX/local_log/bugreport-2017-09-18-141601.zip
拆分结果在/home/XXXX/local_log/bugreport-2017-09-18-141601目录下,current_log, last_log, traces, dumpsys, system_info应有尽有.
版本信息和properties信息都在build_info中.
另外,还有惊喜:system log和event log中添加了进程名;kernal log中添加了时间戳对齐;dumpsys service也有拆分,都在dumpsys下.
2.分析ANR LogAnalyser <file_path> <process_name> -anr
例如:LogAnalyser /home/XXXX/local_log/bugreport-2017-09-18-141601.zip com.sohu.inputmethod.sogou.xiaomi -anr
可是我不确定是哪个进程?没关系,LogAnalyser <file_path> -anr也可以,会把日志中所有的anr进程都解析了.
解析结果在/home/XXXX/local_log/bugreport-2017-09-18-141601/analysis下面:
analysis_anr_进程.txt为anr进程的trace解析结果;
analysis_进程.txt为anr进程的system_log, event_log和main_log解析结果
另外,还可以批量解析,LogAnalyser 文件夹即可!
最终在文件夹下会生成一个summary.txt汇总文件,其内容是根据trace将log分类,方便查看
3.trace分析 LogAnalyser -t <trace_file_path> <process_name>
例如:LogAnalyser -t /home/XXXX/local_log/tracefile.txt com.android.setting
解析结果在/home/XXXX/local_log/out/result_trace_进程名.想知道进程的主线程调用情况,block在什么地方,打开文件就知道了.
4.进程启动和被杀 LogAnalyser <file_path> <process_name> -kill
输出结果在./analysis/extract_key_进程名下.
另外,在"功能2<分析ANR>"的结果文件夹下有个./analysis/analysis_process文件,其中记录所有进程的启动和被杀
3.2. bugreport拆分目录:
- build_info : 记录版本以及properties信息
- current_log:
- current_kernel_log; (当前Kernel日志)
- current_system_log; (当前System日志)
- current_event_log; (当前Event日志)
- current_radio_log;
- last_log
- last_kernel_log; (上一次Kernel日志)
- last_system_log; (上一次系统日志)
- last_radio_log;
- traces
- traces_just_now; (当前的调用栈信息)
- traces_last_anr; (最近一次ANR的调用栈信息)
- tombstones; (native crash的详细信息)
- dumpsys (各种dump信息)
- dumpsys_activity (AMS信息)
- dumpsys_package (PKMS信息)
- dumpsys_window (WMS信息)
- dumpsys_input (Input信息)
- dumpsys_power (PMS信息)
- dumpsys_alarm
- dumpsys_else (其他)
- dumpsys_checkin
- system_info
- sys_cpu (CPU)
- sys_memory (内存)
- sys_io (IO)
- sys_procs (进程)
- sys_binder (binder)
- other:
- analysis :
- analysis_anr (ANR的分析结果)
- analysis_process (杀进程的分析结果)
注意:LogAnalyse能进行初步的确定问题,但是并非适应所有的场景
四.建议
为了避免ANR发生的概率:
1:UI线程尽量只做跟UI相关的工作
2:耗时的工作(比如数据库操作,I/O,连接网络或 者别的有可能阻碍UI线程的操作) 把它放入单独的线程处理
3:尽量用Handler来处理UIthread和别的thread之间的交互