干货-ANR问题处理套路

anr问题的处理技巧

hi, 大家好,我是爱吃香蕉的猴子,今天记录一下ANR问题的处理套路吧,这也是自己分析ANR问题一般的思路;


  • 根据测试的描述,区分是Monkey测试偶现的 ,还是可以手动复现,一般情况前者多一些,我也是根据前者写这个记录;
  • 首先,要会区分ANR类型,是什么类型的ANR ??
    • 用户输入事件处理超时: KeyDispatchTimeout-主要类型按键或触摸事件,input事件在5S内没有处理完成发生ANR
      • 关键字:Reason: Input dispatching timed out xxxx
    • Broadcast超时: BroadcastReceiver onReceiver处理事务时前台广播在10S内,后台广播在60s内没有处理完成发生ANR
    • 关键字:Timeout of broadcast XXX/Receiver during timeout:XXX/Broadcast of XXX
    • Service超时:ServiceTimeout-bind,create,start,unbind等在主线程处理耗时,前台Service在20s内,后台Service在200s内没有处理完成发生ANR
    • 关键字:Timeout executing service:/executing service XXX
    • ContentProvider超时: publish在10s内没有处理完成发生ANR
      • 关键字:timeout publishing content providers
  • 补充一种类型 窗口获取焦点超时(input 类型的一种子类型)
    • 关键字: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.)需要注意区分同为Input dispatching timed out大类的用户输入事件处理超时,这两类超时括号内的提示语是不同的。
    • 这个问题,我可以简单理解为窗口切换时,A --> B, 开始焦点在A 窗口在A ,后面 焦点在A 这时窗口时null,如果这个时候超过了5s,那就会发生anr.

查看现场
- ag “am_anr” / ag ag “ANR in” # 这个时候,你会发现很多或者几个anr的地方, 我先看时间最早的,我这里就不贴log了
- 确定最早发生的anr的时间,进程,(如果还没有确定anr类型) 这里基本也会看到特定类型anr的信息
- 然后既然确定了一个anr的位置,那我们就要分析,这个是怎么造成的?? 猜测思路如下
- 1. 主线程耗时操作,如复杂的layout,庞大的for循环,IO等
- 2. 主线程被子线程同步锁block
- 3. 主线程被Binder 对端block
- 4. Binder被占满导致主线程无法和SystemServer通信
- 5. 得不到系统资源(CPU/RAM/IO)(有一个关键搜索:CPU usage
- 如果,并没有有效的收获,那就检索 上面的anr类型的关键字grep -v ‘mmm|nnn’ abc.txt


看anr的trace文件 我一般是大概有了了解,最后看trace文件

  • 匹配一下,上面看到的时间 进程 线程(如果不匹配,也可以反向去找)
  • 补充一下:
    • main tid=1 //主线程
    • group: 线程组名称
    • sCount: suspendCount个数
    • dsCount: debugSuspendCount个数
    • obj: 0x4025b1b8 线程java对象地址
    • self: 线程native的对象地址
    • Binder Thread #2: Binder线程是进程的线程中用来处理binder请求的线程.
    • SUSPENDED:线程暂停,可能是由于输出Trace、GC或debug被暂停
    • NATIVE: 正在执行JNI本地函数
    • MONITOR: 线程阻塞,等待获取对象锁
    • WAIT: 执行了无限等待的wait函数
    • TIMED_WAIT: 执行了带有超时参数的wait、sleep或join函数
    • VMWAIT: 正在等待VM资源
    • RUNNING/RUNNABLE: 线程可运行或正在运行
    • INITALIZING: 新建,正在初始化,为其分配资源
    • STARTING: 新建,正在启动
    • ZOMBIE: 线程死亡,终止运行,等待父线程回收它
  • 一般我们要首先查看main线程,是否出现 block wait suspend ,有没有线程间的死锁,有没有出现binder调用服务端没有响应,有没有阻塞在system server.
    • 死锁:线程在获得一个锁L1的情况下再去申请另外一个锁L2,也就是锁L1想要包含了锁L2,也就是说在获得了锁L1,并且没有释放锁L1的情况下,又去申请获得锁L2,这个是产生死锁的最根本原因。看一个android中的简单Log
Cmd line: com.android.inputmethod.latin
"main" prio=5 tid=1 NATIVE
......
  at android.os.BinderProxy.transact(Native Method)
  at com.android.internal.view.IInputMethodManager$Stub$Proxy.getCurrentInputMethodSubtype(IInputMethodManager.java:908) ......
Cmd line: system_server
"main" prio=5 tid=1 MONITOR
  - waiting to lock <0x41c5a650> (a java.lang.Object) held by tid=12 (WindowManager)......
"WindowManager" prio=5 tid=12 MONITOR
  - waiting to lock <0x41c0c118> (a android.view.inputmethod.InputMethodManager$H) held by tid=61 (Binder_F)......
"Binder_F" prio=5 tid=61 MONITOR
  at com.android.server.InputMethodManagerService.getCurrentInputMethodSubtype(InputMethodManagerService.java:~3078)
  • 解析一下:main等待WindowManager ,WindowManager 又等待Binder_F。因此inputmethod主线程的远程调用无法返回,导致ANR。
  • 还有一点,这里查看这个cpu占用信息
11 CPU usage from 9818ms to 0ms ago (2020-12-09 06:03:39.534 to 2020-12-09 06:03:49.352):
12   109% 10198/system_server: 85% user + 24% kernel / faults: 12620 minor 11 major
13   35% 2724/pulseaudio: 34% user + 1.7% kernel
14   13% 10545/com.android.car: 10% user + 3.1% kernel / faults: 3518 minor
............
110 74% TOTAL: 49% user + 24% kernel + 0.1% iowait + 0.1% softirq
  • binder调用服务端没有响应: 基本通过clent和server共同的方法去grep,看服务端在做什么?
  • system server出现block
  • 有时候,也可以根据:id threadid 进行grep看执行了什么程序,例如:1089 1089
  • 其实,74%这个是负载,这个负载是算是高,在这个负载的情况出现anr也是有可能是系统性能问题,但不能明确,起码我们还要找到什么原因造成的负载高。cpu其他进程占用信息,基本都相对容易判断。

  • 其实还有一些分析策略,但最常用的还是这些套路吧,有了好的且可以分享的案例,我会补充上来。

                                                 Code的搬运工V1.0

你可能感兴趣的:(干货,android)