Android ANR分析

ANR(Application Not Responding,即应用程序无响应)。在Android中,当应用程序在规定时间内没有处理完毕相应的事件,系统就会报出ANR。

ANR类型

  • InputDispatchingTimedOut:应用程序主线程在5s内没有完成用户的input事件
  • ServiceTimeout:应用程序没有执行完成service的bind/create/start/destroy/unbind操作,前台服务20s超时,后台服务200s超时
  • BroadcastTimeout:应用程序在规定时间内没有执行完成onReceive操作,前台广播10s超时,后台广播60s超时
  • ContentProviderTimeout:应用程序在20s内没有执行完成ContentProvider相关操作

产生原因

  • 系统原因:由于Kernel/Framework/Driver等存在问题,导致系统不稳定最终表现出ANR
  • 应用原因:主线程死锁、阻塞或者性能低下,需避免将耗时操作放在主线程

Android ANR分析_第1张图片

 PS:一定要把ANR和JE区分开,ANR是XXX无响应,JE是XXX已停止

一、如何分析ANR?

ANR类的问题有些可以直接从日志中排查出原因,例如广播类ANR;有些可能因为系统不稳定(例如系统进程system server进程崩溃,CPU耗时,IO耗时,低内存等原因)导致很难定位。因此我们需要一套思路来分析定位ANR。

无论是MTK平台还是展锐平台,我们都需要如下关键信息的日志:

  • Logcat

logcat属于android原生提供的抓取日志的方式。可以通过logcat来初步定位anr发声时间以及上下文系统或者各个进程状态。

MTK平台的AP日志中主要有main_log和sys_log,如果跟通信相关,还需要去查看radio_log。

展锐平台的AP日志中主要有android.log、android_main.log、android_system.log、android_radio.log。

  • EventLog

envents也属于android原生提供的一种日志。通过这个日志可以很简洁的呈现出系统的事件处理流程,包括时间,状态,便于我们分析定位问题

MTK平台的AP日志中主要是events_log开头的文件;展锐平台AP日志中主要android_events.log。

【Android日志分析】EventLog_android event日志_xqliu2134的博客-CSDN博客

  • MTK DB

MTK通过db的方式来记录系统发生的一切JE/NE/ANR相关的堆栈,如下图,但是需要通过mtk的专用工具才能解析。

  • 展锐 Traces

展锐平台的AP日志中有专门traces文件夹,该文件夹存放了每一次ANR的堆栈调用,系统信息,文件格式为xx_xxx-xx_anr_xx_xx_xx。

2、分析流程

MTK和展锐官网都有详细说明如何分析ANR问题,但这里我更推荐展锐的分析思路。

  • 展锐

Android ANR分析_第2张图片

  • MTK

Android ANR分析_第3张图片

Android ANR分析_第4张图片

相比两种分析思路,大体流程类似,可以总结分为如下几个步骤:

  • 确认ANR类型、时间、进程号
  • 初步查看堆栈信息,分析主线程是否有明显的错误(定位是否应用原因)
  • 如果主线程没有明显异常,在确认是否系统原因,重点关注CPU负债、内存信息、LMDK、IO是否阻塞、重要的系统进程(Zygote/system_server/ActivityManager等)有无明显异常
  • 如果即没有定位应用自身原因,也没有定位到系统原因,那么需要结合上下文日志进行综合分析,如果还是没有什么头绪,这种情况可能是堆栈没有抓取出来,可能其他什么原因,建议可以给平台提case

1)、确认ANR类型和相关信息

  • MTK如何快速定位?

如果测试提供的日志有AEE目录,那么可以先检查db_history快速找到对应的db文件:

Android ANR分析_第5张图片

 Android ANR分析_第6张图片

Android ANR分析_第7张图片

 使用MTK的GAT工具加载db文件并解析:

Android ANR分析_第8张图片

  • 展锐如何快速定位?

直接从YLOG的AP日志中找到对应时间端的traces文件:

Android ANR分析_第9张图片

  • 通过Logcat定位

如果DEBUG或者YLOG里面没有相关的ANR日志,那么只有从logcat中查看ActivityManager进程是否有相关ANR的打印(PS:YLOG通常在android_system.log),例如:

Android ANR分析_第10张图片

 还可以再envent_log日志中搜索am_anr类型的日志,例如:

E012594  06-14 13:54:40.652  1110  4566 I am_anr  : [0,1648,com.android.settings,684310085,Input dispatching timed out (105fb5 com.android.settings/com.android.settings.network.telephony.MobileNetworkActivity (server) is not responding. Waited 8007ms for MotionEvent)]

2、初步查看主线程堆栈信息

为什么要优先去定位主线程信息?

根据ANR产生原因来看,即应用主线程做了耗时操作,根据分类可以是广播接受者、内容提供者、服务(四大组件都运行在主线程)中做了耗时操作;

当然Acitcity也有可能存在耗时产生InputDispatchingTimedOut类型的ANR,例如activity没有即使显示出来(分析焦点),activity view没有及时响应触摸点击等事件,handler中消息处理超时 等。

1)、堆栈信息结构

MTK通过db文件中的__exp_main.txt文件来定位,展锐通过traces来定位。traces显示的有如下几部分:

  • ANR基本信息:参考《确认ANR类型和相关信息》

Android ANR分析_第11张图片

  • ANR发生前后系统(CPU和MEM)情况

Android ANR分析_第12张图片Android ANR分析_第13张图片

  • ANR发生进程Signal Catcher线程堆栈信息

Signal Catcher线程后文会详细介绍,这里只需要知道他是抓取Java虚拟机堆栈的线程

Android ANR分析_第14张图片

  • ANR发生进程主线程堆栈信息

线程堆栈中,如下截图表明为主线程,关键特征:线程组为main,tid为1

Android ANR分析_第15张图片

  •  ANR发生进程其他线程堆栈信息:主线程堆栈信息之后紧跟着其他子线程的堆栈信息,例如binder线程、守护线程等。通常我们会根据主线程的相关信息,选择性是否有必要去查看其他子线程的信息,例如主线程block到binder子线程中,或者与某个子线程存在死锁

2)、线程堆栈参数解读

线程名: main(如有daemon则代表守护线程)
prio: 线程优先级
tid: 线程内部id
线程状态: NATIVE
"main" prio=5 tid=1 Native
  | group="main" sCount=1 ucsCount=0 flags=1 obj=0x735086e8 self=0xb4000071e8c707b0
  | sysTid=1329 nice=0 cgrp=default sched=1073741824/0 handle=0x732dad44f8 
  | state=S schedstat=( 136542970431 292470333243 553823 ) utm=8774 stm=4879 core=3 HZ=100
  | stack=0x7febede000-0x7febee0000 stackSize=8188KB
  | held mutexes=
第1行group: 线程所属的线程组
第1行sCount: 线程挂起次数
第1行dsCount: 用于调试的线程挂起次数
第1行obj: 当前线程关联的java线程对象
第1行self: 当前线程地址
第2行sysTid:线程真正意义上的tid
第2行nice: 调度有优先级
第2行cgrp: 进程所属的进程调度组
第2行sched: 调度策略
第2行handle: 函数处理地址
第3行state: 线程状态
第3行schedstat: CPU调度时间统计, 见proc/[pid]/task/[tid]/schedstat
第3行utm/stm: 用户态/内核态的CPU时间(单位是jiffies), 见proc/[pid]/task/[tid]/stat
第3行core: 该线程的最后运行所在核
第3行HZ: 时钟频率
第4行stack:线程栈的地址区间
第4行stackSize:栈的大小
第5行mutex: 所持有mutex类型,有独占锁exclusive和共享锁shared两类

schedstat含义说明:
nice值越小则优先级越高。此处nice=0, 可见优先级还是比较高的;
schedstat括号中的3个数字依次是Running、Runable、Switch,紧接着的是utm和stm
Running时间:CPU运行的时间,单位ns
Runable时间:RQ队列的等待时间,单位ns
Switch次数:CPU调度切换次数
utm: 该线程在用户态所执行的时间,单位是jiffies,jiffies定义为sysconf(_SC_CLK_TCK),默认等于10ms
stm: 该线程在内核态所执行的时间,单位是jiffies,默认等于10ms

在分析各个线程的时候,首先还需要关注的是他们线程名称和线程状态:

线程名"main":通常为主线程,这个跟group不一样,有些子线程的group也是main,但他们不是主线程

线程名"Binder":通常为binder子线程

线程状态Native:表明该线程当前运行在native层,通常会有一些c/c++代码堆栈打印

线程状态Blocked:表明该线程被阻塞,通常为java某个方法被卡住,或者等待某个锁的释放

3)、常见主线程堆栈能够定位的案例

  • Native发生异常
  • Binder通信出现异常

Android ANR分析_第16张图片

  • 死锁阻塞

Android ANR分析_第17张图片

Android ANR分析_第18张图片

Android ANR分析_第19张图片

 

 

 

二、常用Android相关机制

1、Signal Catcher线程

Android Runtime | Trace文件的生成机制_waitholdinglocks_小陈乱敲代码的博客-CSDN博客

你可能感兴趣的:(android)