Android ANR

178 阅读5分钟

前言

ANR(Application No Response),App在特定时间无法响应屏幕触摸或键盘输入时间,或特定事件没有处理完,会出现ANR。分类:

  1. InputDispatching:5秒内无响应input事件,下一个事件到达发现又一个超时事件才会触发ANR。关键字:Input event dispatching timed out
  2. BroadcastQueue:前台广播的onReceive()函数10秒没有处理完,后台为60秒。关键字:Timeout of broadcast BroadcastRecord
  3. Service:前台20秒,后台200秒内没有执行完,前后台根据app的状态判定。关键字:Timeout executing service
  4. 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分析思路

  1. Trace日志
  2. AnrInfo
  3. Kernel 日志
  4. Logcat日志
  5. Meminfo日志
  6. 其他监控工具

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%
  • 关键进程:
    1. kswapd:是linux中用于页面回收的内核线程。
    2. 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写入。