ANR问题原理介绍与DEBUG分析

一、ANR简介

Android系统中system_server进程的组件ActivityManagerService(简称AMS)InputManagerService(简称IMS)会检测App的响应时间,如果App在特定时间无法相应屏幕触摸或键盘输入时间,或者特定事件没有处理完毕,就会出现Application Not Responding ,即ANR

ANR的机制是为了增强用户体验,避免设备长时间处在无法响应用户操作的状态。应用发生ANR,系统对用户可感知的应用,创建弹框提示用户发生ANR,让用户选择继续运行或者强制关闭。系统对于用户不可感知的应用,直接杀掉其进程。

Android的ANR主要有两种方式触发:

1,通过handler的延迟机制触发ANR。

2,Input事件触发ANR。

Service、BroadcastReceiver、ContentProvider都是通过handler的延时机制触发ANR。

二、ANR发生场景

以下四个条件都可以造成ANR发生:

1,BroadcastQueue Timeout 

    执行前台广播(BroadcastReceiver)的onReceive()函数时10秒没有处理完成,后台为60秒。

2,Service Timeout 

    前台服务20秒内,后台服务在200秒内没有执行完毕。

3,ContentProvider Timeout 

    ContentProviderpublish10s内没进行完。

4,Input Dispatching Timeout

    5秒内无法响应屏幕触摸事件或键盘输入事件。

startService过程根据发起方进程callerApp所属的进程调度组来决定被启动的服务是属于前台还是后台。当发起方进程不等于ProcessList.SCHED_GROUP_BACKGROUND(后台进程组)则认为是前台服务,否则为后台服务,并标记在ServiceRecord的成员变量createdFromFg。简单来说就是Adj>200的进程对用户来说基本是无感知,主要是做一些后台工作,故后台服务拥有更长的超时阈值,同时后台服务属于后台进程调度组,相比前台服务属于前台进程调度组,分配更少的CPU时间片。

三、ANR触发原理

1, APP与AMS之间的Binder通信

ApplicationThread 实现IBinder接口,注册为IApplicationThread的服务端。它是ActivityThread的一个内部类,都运行在UI thread,即main thread。ApplicationThread有自己的handler:mH,通过公用mainlooper消息队列与ActivityThread的mainHander通信。
xxxService.getService()获取xxx服务的一个客户端对象xxxServiceProxy,然后就可以跟AMS通信了。
 

2,Service ANR机制

3,Broadcast超时检测

如果是动态广播,或者静态广播没有正在执行持久化操作的SP任务,则不需要经过“queued-work-looper线程中转。根据发送广播sendBroadcast(Intent intent)中的intentflags是否包含FLAG_RECEIVER_FOREGROUND来决定把该广播是放入前台广播队列或者后台广播队列,前台广播队列的超时为10s,后台广播队列的超时为60s,默认情况下广播是放入后台广播队列,除非指明加上FLAG_RECEIVER_FOREGROUND标识。

4, ContentProvider 超时检测

provider的超时是在provider进程首次启动的时候才会检测,当provider进程已启动的场景,再次请求provider并不会触发provider超时。

5, Input超时机制

input的超时检测机制跟service、broadcast、provider截然不同,为了更好的理解input过程先来介绍两个重要线程的相关工作:

InputReader线程负责通过EventHub(监听目录/dev/input)读取输入事件,一旦监听到输入事件则放入到InputDispatcher的mInBoundQueue队列,并通知其处理该事件;

InputDispatcher线程负责将接收到的输入事件分发给目标应用窗口,分发过程使用到3个事件队列:

a. mInBoundQueue用于记录InputReader发送过来的输入事件;

b. outBoundQueue用于记录即将分发给目标应用窗口的输入事件;

c. waitQueue用于记录已分发给目标应用,且应用尚未处理完成的输入事件;

input的超时机制并非时间到了一定就会爆炸,而是处理后续上报事件的过程才会去检测是否该爆炸。表现就是我们在UI界面操作即使某个时间超过阈值也不会提示,而是要到下次事件才会提示ANR。

EventHub模块直接访问所有的设备节点/dev/input/event1/dev/input/event2…/dev/input/eventNInputReader运行于独立线程,负责管理输入设备的列表和配置,以及进行输入事件的加工处理。通过线程循环不断地通过getEvent()函数从EventHub中将事件取出并进行处理。对于设备节点的增删事件,他会更新输入设备列表于配置。对于原始输入事件,InputReader对其进行翻译,组装封装包含更多的信息,然后交给InputDispatcher进行派发。InputDispatcherGlobal System 键默认是不会发给 App 的,Dispatcher 线程处理完后直接把它给丢掉。对于要发给 App 的事件也要放到目标 App 的队列中。传给 App 的时候向 WMS 查询当前窗口,得到目标 App connectionWM 创建的),从 outBoundQueue 取出数据通过 connection 发给 App

四,ANR信息搜集

对于四大组件发生ANR后,AMS会马上去抓取现场的信息,用于调试分析。收集的信息包括如下:

1,将am_anr信息输出到EventLog,也就是说ANR触发的时间点最接近的就是EventLog中输出的am_anr信息。

2,收集以下重要进程的各个线程调用栈trace信息,保存在data/anr/traces.txt文件:

      a, 当前发生ANR的进程,system_server进程以及所有persistent进程

      b, audioserver, cameraserver, mediaserver, surfaceflinger等重要的native进程

      c, CPU使用率排名前5的进程

3,将发生ANR的reason以及CPU使用情况信息输出到main log,/data/anr/目录。

4,将traces文件和CPU使用情况信息保存到dropbox,即data/system/dropbox目录。

5,对用户可感知的进程则弹出ANR对话框告知用户,对用户不可感知的进程发生ANR则直接杀掉。

五,导致ANR的原因

应用原因

1,主线程阻塞:如死循环、主线程处理IO时间太长、处理大数据 。

2,主线程锁住:主线程等待子线程的锁 。

3,服务端无法及时响应:服务的线程池有限,如果短时间内client过多可能导致ANR问题。

系统原因

1,CPU被抢占:比如前台在玩游戏,可能会导致你的后台广播出现ANR。

2,内存紧张:系统长期处于内存紧张,会导致频繁内存交换、回收,进而导致应用的一些操作超时。

3,CPU调度不合理:需要检查CPU调度。

4,IO紧张:系统整体IO紧张,造成平时可以很快完成的IO操作出现较长耗时。

六,ANR问题分类

以上归类只是初步判断,实际情况可能与初判结果大相径庭。应用Idle mode出现在支持电源模式DozeApp Standby的设备。

七,应用问题分析步骤

Other issue 可能是I/O,内存,CPU,GPU或启动其他模块的问题。

八,ANR LOG分析

1,ANR LOG筛选

KeyDispatch Timeout : “dispatching timed out”

Broadcast Timeout: “Timeout of broadcast BroadcastRecord”

Service Timeout: “Timeout executing service”

Content Provider Timeout: “timeout publishing content providers”

2,ANR trace关键字解析

sCount 此线程被挂起的次数

dsCount 线程被调试器挂起的次数

prio=5 priority 优先级

nice 线程调度的优先级

utm 线程用户态下使用的时间值(单位是jiffies)

stm 内核态下的调度时间值

core  最后执行这个线程 cpu核的序号

cgrp=default 线程调度组

sched=0/0 线程调度策略和优先级

线程处理函数地址:handle=0x7f85742548

3,其他日志关键字

关键字

说明

CPU usage from

筛选CPU信息确认CPU是否被占用

 Free RAM:

cached pss 与  cached kernel 任一较低预示内存不足

am_meminfo: [1175105536,。。。]

四个值分别是:Cached, Free, Zram, Kernel, Native

lowmemorykiller:

内存不足时触发Low Memory Killer

FAILED BINDER TRANSACTION

可能是资源不足导致binder通信出错

IPCThreadState: binder thread pool

(4 threads) starved for 10018 ms

可能是资源不足导致binder通信缓慢

 Davey Displayed Choreographer

这些是性能相关的关键字,也可能预示着资源紧张

九,示例

1,GPU性能造成ANR

查看main log,发现在绘制的时候出现性能打印:

Line 180095: 09-01 10:44:46.254  2764  3064 I OpenGLRenderer: Davey! duration=32868ms; Flags=1, IntendedVsync=23678824135,

则考虑几种情况:

  1. GPU存在异常
  2. 界面绘制太复杂
  3. 兼顾检查CPU与memory

2,lock up导致ANR

以上是典型的abba锁导致的死锁问题。具体修复方案要根据实际场景一般来说,处理死锁问题有三种方法:

    a.通过协议来预防或避免死锁,确保系统不会进入死锁状态。

    b.可以允许系统进入死锁状态,然后检测它,并加以恢复。

    c.可以忽视这个问题,认为死锁不可能在系统内发生。

3,binder对端返回结果慢

对于关键字:

libbinder.so (android::IPCThreadState::talkWithDriver(bool)

通常是由于binder对端返回超时,需要对端检查原因。也有可能是对端binder线程池耗尽。

如果对端不能保证结果返回时限,本应用最好把该消息处理函数的binder调用改为异步模式。

如果要确认对端是谁,则可以通过如下命令打开binder的transaction调试后重新复现:

echo 512 > /sys/module/binder/parameters/debug_mask

也可在现场打印:

cat /d/binder/transaction_log

4,CPU不足,I/O太高 IPC复杂导致对端返回慢

5,内存不足,kswapd很高

参考资料:
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值