Android修炼系列(32),你理解的 ANR 监控可能一直是错的

·  阅读 3246

上一篇文章写了 app卡顿监控的技术原理 ,主要说了几种主流的卡顿监控方式:

  • 通过开一个子线程不断去轮询主线程。原理就是不断向主线程发送Message,每隔一段时间检查一次刚刚发送的消息是否被处理,如果没被处理,说明这段时间主线程被卡住了。

  • 通过使用系统方法 setMessageLogging 替换掉主线程 Looper 的 Printer 对象,通过计算 Printer 打印日志的时间差,来拿到系统 dispatchMessage 方法的执行时间。

  • 通过 Choreographer 类的 FrameCallback 函数。原理当每一帧被渲染时会触发 FrameCallback 回调,FrameCallback 回调 doFrame(long frameTimeNanos) 函数,一次界面渲染会回调 doFrame,如果两次 doFrame 间隔大于16.6ms 则发生了卡顿。

  • 插桩的方式对函数的出入口进行记录。

本篇介绍 ANR 监控,看了一些文章,都是将卡顿阈值设置成 5s 来监控ANR,这其实是很不严谨的,因为 5s 只是 ANR 的一种情况,是 Touch 事件未被及时消费的默认阈值,其他原因的 ANR 阈值并不是 5s。而且就算主线程卡了 5s,input 系统在没有任何事件输入时,也不会 ANR。

那有什么好的方式呢?我们知道当应用 ANR 后,都会 dump 一份 Trace 文件,所以是不是我们只要知道了应用是什么时候生成的这个 Trace 文件,是不是也就知道了ANR 的发生时机呢?

SIGQUIT 信号

当应用发生 ANR 后,system_server 进程会发送 SIGQUIT 信号来通知相关进程来 dump 堆栈,这里就包括发生 ANR 的应用进程(且是第一个收到通知的进程),除此外,还有一些 AMS 维护的 LRU list内的进程(CPU 使用率高的进程)和一些固定的 native 系统进程,也都会收到通知并 dump 堆栈。

微信的 Matrix 库 就是运用的这个原理,使用 sigaction 方法注册 signal handler 来监听 SIGQUIT 信号,来达到监控 ANR 的目的,非常巧妙。

我们下面来看看 Matrix,这是入口:

a.png

通过 pthread_sigmask 将 SIGQUIT 设置为 UNBLOCK,否则我们的 signal handler 监听不到 SIGQUIT 信号,其会直接被系统的 signal catcher 线程捕获:

b.png

创建我们的 signal handler, 并使用 sigaction 方法注册监听:

f.png

我们在 handleSignal 方法中收到 SIGQUIT 信号,并设置 anrCallback:

e.png

注意当我们使用 Signal Handler 抢了系统的 SIGQUIT,Signal Catcher线程的 sigwait 就收不到信号了,就会导致系统原本的 dump 逻辑没法完成了,所以为了保证系统逻辑不变,我们需要再转发一个 SIGQUIT 信号给到 Signal Catcher:

image.png

在 onANRDumped 后,为了防止 SIGQUIT 信号不是由本应用 ANR 导致发出的,需要判断当前进程是否被置了 ProcessErrorStateInfo.NOT_RESPONDING 状态,如果没有,则说明这个信号可能是由其他应用 ANR 导致 system_server 进程发出的。这里会创建了 Check-ANR-State-Thread 线程在 20s 内每隔 500 ms 就轮询查一次:

image.png

还有一种情况,当后台ANR 后,并不会设置 NOT_RESPONDING 状态。这里是通过检查主线程 queue#mMessages 的 when 字段( 在上篇文章中说过),判断当前卡顿的时长,如果发现已卡顿 前台 2s,后台 10s,则认为这是一个 anr,立即上报,防止漏报:

image.png

流程就是这个样子了。

还有一点需要思考,我们都知道进程 dump 堆栈后,会将文件保存在 data/anr/ 目录下,这个文件记录非常全,是分析问题的绝佳选择了,可我们没有权限读取怎么办呢?

Matrix 给了解决方法,我们可以通过 hook Signal Catcher 内写 Trace 的 write 方法,来拿到 Trace 内容,具体自行看源码吧。

本章完

参考资料

  1. cloud.tencent.com/developer/a…
分类:
Android
分类:
Android
收藏成功!
已添加到「」, 点击更改