前言
ANR(Application No Response),App在特定时间无法响应屏幕触摸或键盘输入时间,或特定事件没有处理完,会出现ANR。分类:
- InputDispatching:5秒内无响应input事件,下一个事件到达发现又一个超时事件才会触发ANR。关键字:Input event dispatching timed out
- BroadcastQueue:前台广播的onReceive()函数10秒没有处理完,后台为60秒。关键字:Timeout of broadcast BroadcastRecord
- Service:前台20秒,后台200秒内没有执行完,前后台根据app的状态判定。关键字:Timeout executing service
- ContentProvider:创建发布超时并不会ANR,可以自己设定anr。关键字:timeout publishing content providers
ANR系统日志获取
发生ANR后,Android 6.0后,应用侧无法访问data/anr/trace文件。app层如何得知anr和获取日志呢?
主线程watchdog机制
类似于Blockcandary机制,检测主线程Message是否在指定时间得到处理。如果规定时间没有处理就认为发生了ANR。弊端:不一定准确。
监听SIGNALaUIT信号
系统system_server进程检测到app出现ANR后,会向出现ANR进程发送SIGQUIT(signal 3)信号。系统的libart.so收到信号,调用Java虚拟机的dump方法生成traces,是APM常用的方法。
app可以拦截SIGQUIT,来生成traces和ANR日志。app处理完信号后,将信号继续传递给系统的libart.so,让系统生成ANR traces.txt。
日志分析
发现ARN in xxx处的代码不会造成anr。anr检测并不是真的监控发送的消息在真实执行过程中是否超时,线程dump时不管当前消息是否执行,或真实执行耗时多久,只要在监控超时来临之前,服务端没有收到通知,就判定超时。
发生ANR问题时,Trace堆栈很多情况下都不是RootCase。系统ANR Info提示某个Service或Receiver导致的ANR很大程度上,并不是这些组件自身问题。例如:之前某个历史消息严重耗时,需要APM具有消息调度监控功能,才能方便排查问题,只是根据系统产生的一些日志很难分析具体原因。
消息调度监控
ANR很多场景是历史消息耗时较长并不断累积导致的,监控每次消息调度耗时记录,有助于清晰知道ANR发生前主线程发生了什么。
耗时消息堆栈采样
- 方案1:对每个函数插桩,统计耗时,进行聚合汇总。优点:可以精确每个函数真实耗时;缺点:影响包体积和性能,不利于产品高效复用。
- 方案2:每个消息执行时,触发子线程超时监控,超时后本次消息还没执行结束,则抓取主线程堆栈。参考BlockCandary实现,通过Looper Printer可以拿到每次消息处理情况。大部分消息耗时都很少,每次都频繁设置和取消将带来性能影响,要对比进行优化。
待调度消息采样
除了监控主线程历史消息调度和耗时之外,要在ANR发生时,获取消息队列中待调度的消息,为分析问题提供更多线索。可通过反射获取消息队列下一个待调度消息,通过Message.next字段遍历链表。
ANR分析思路
- Trace日志
- AnrInfo
- Kernel 日志
- Logcat日志
- Meminfo日志
- 其他监控工具
Trace信息
系统会dump当前进程和关键进程的线程堆栈。
- 线程堆栈:发生ANR时线程正在执行的逻辑
- 线程状态:"state=xxx"当前线程工作状态
- Running当前线程正在被CPU调度
- Runnable 已经Ready等待CPU调度
- Native 由Java环境进入Native环境,可能执行Native逻辑,也可能进入等待状态
- Waiting 空闲等待
- 还有Sleep,Blocked状态等
- 线程耗时:utm=xxx,stm=xxx,线程从创建到当前,被CPU调度的真实运行时长,不包括线程等待或Sleep耗时,CPU耗时分为用户空间耗时(utm)和系统空间耗时(stm),单位是jiffies.当HZ=100时,1utm等于10ms。
- utm:Java层和Native层非Kernel系统调用的逻辑,用户空间耗时
- stm:系统空间耗时,Kernel层API进行空间切换,用户空间切换到Kernel空间,如文件操作:open,write,read等
- core:最后执行这个线程的cpu核的序号
- 线程优先级:nice值越高,进程(线程)优先级越高。对应用线程,nic范围100~139。应用前后台不同场景,系统对进程优先级进行调整。
- 调度态:“schedstat=( 1813617862 14167238 546 )”,CPU执行时长(单位 ns),等待时长,Switch次数。
Anr Info 信息
系统发生ARN前后的系统负载 Top进程和关键进程CPU占用比
- ANR类型:Reason 哪种类型的ANR。如:Input,Receiver,Service等等
- 系统负载:Load: 45.33/27.22/18.32,发生ARN前1分钟,前5分钟,前15分钟各个时间段的系统CPU负载,单位时间内等待系统调度的任务数(可理解为线程)。这个值越高,表示系统面临CPU或IO竞争,普通进程或线程调度将受影响。
- 进程CPU使用率:ANR发生之前(CPU usage from xxx to xxx ago)或发生之后(CPU usage from xxx to xxx later)一段时间内,CPU占用,进程的user,kernel的CPU占比。单进程CPU负载并不是以100%为上限,而是根据几个核,如4核上限为400%
- 关键进程:
- kswapd:是linux中用于页面回收的内核线程。
- mmcqd:内核线程,一般kswapd和mmcqd同步出现
- 系统CPU分布:"66% TOTAL: 20% user + 15% kernel + 28% iowait + 0.7% irq + 0.7% softirq"。如果发生大量文件读写或内存紧张,则iowait占比较高。
Logcat日志
”low memory“,"Slow operation","slow delivery"等。观察当前进程或系统进程是否存在频繁GC
在线上环境,常见因锁(locked)而ANR的场景是SharePreference写入。