根据Android 13 进行测验
这个anr 发生的原因以及例子 , 之前的文章有写过一个非耗时操作Input ANR引发的思考, 能够查看之前写的文章 , 此文只做xcrash 和 matrix 关于此类anr的监控和排查.
anr 发生 xCrash 部分日志文件如下
` Tombstone maker: ‘xCrash 3.1.0’
Crash type: ‘anr’
Start time: ‘2023-07-04T10:05:50.997+0800’
Crash time: ‘2023-07-04T10:06:12.226+0800’
App ID: ‘xcrash.sample’
App version: ‘1.2.3-beta456-patch789’
Rooted: ‘No’
API level: ’33’
OS version: ’13’
Kernel version: ‘Linux version 5.10.149-android12-9-00001-gda3d81545d1d-ab9545656 #1 SMP PREEMPT Tue Jan 31 05:44:42 UTC 2023 (aarch64)’
ABI list: ‘arm64-v8a,armeabi-v7a,armeabi’
Manufacturer: ‘Xiaomi’
Brand: ‘Redmi’
Model: ‘Redmi Note 12 Turbo’
Build fingerprint: ‘Redmi/marble/marble:13/TKQ1.221114.001/V14.0.20.0.TMRCNXM:user/release-keys’
ABI: ‘arm64’
Heap: 86% free, 3706KB/26MB; 175828 objects
pid: 17680 >>> xcrash.sample <<<
“main” prio=5 tid=1 Native
| group=”main” sCount=1 ucsCount=0 flags=1 obj=0x723064d8 self=0xb40000721fe42c00
| sysTid=17680 nice=-10 cgrp=top-app sched=0/0 handle=0x72beebb4f8
| state=S schedstat=( 737089013 150968431 989 ) utm=62 stm=11 core=7 HZ=100
| stack=0x7fc9984000-0x7fc9986000 stackSize=8188KB
| held mutexes=
native: #00 pc 00000000000e1b9c /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+12) (BuildId: 449f781894033dce6346794a1ee593e0)
native: #01 pc 0000000000017e40 /system/lib64/libutils.so (android::Looper::pollInner(int)+192) (BuildId: 104125701f0f8a41374b9998e94209e9)
native: #02 pc 0000000000017d1c /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+116) (BuildId: 104125701f0f8a41374b9998e94209e9)
native: #03 pc 00000000001688bc /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+48) (BuildId: 5c9da975dbb315da13b16b1bdb77d79c)
at android.os.MessageQueue.nativePollOnce(Native method)
at android.os.MessageQueue.next(MessageQueue.java:341)
at android.os.Looper.loopOnce(Looper.java:169)
at android.os.Looper.loop(Looper.java:300)
at android.app.ActivityThread.main(ActivityThread.java:8395)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:559)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:954)`
…省略大部分不相关日志
经过主线程仓库 , 能够看到当时主线程是闲暇的 , 而且当时主线程回到native层去调用 nativePollOnce -> epoll_wait 进入等候堵塞状态, 如果仅凭xrash 这些日志 , 虽然知道有ANR了 , 但仍是很难定位到问题去解决这类ANR问题.
anr 发生 matrix 部分日志文件如下
[at] visibleScene[xcrash.sample.SecondActivity] has detach focus!
[realRelease] timestamp:1688438955353
getTotalMemory cost:1, total_mem:11719024640, LowMemoryThresold:226492416, Memory Class:256
[getLevel] totalMemory:11719024640 coresNum:8
getLevel, cost:3, level:BEST
RandomAccessFile(Process Stat) reader fail, error: java.io.FileNotFoundException: /proc/stat: open failed: EACCES (Permission denied)
getAppCpuRate cost:3,rate:0.0
report issue content: tag[Trace_EvilMethod]type[0];key[null];content[{“machine”:”BEST”,”cpu_app”:0,”mem”:11719024640,”mem_free”:4767520,”detail”:”SIGNAL_ANR”,
“threadStack”:”android.os.MessageQueue.nativePollOnce(Native Method)\nandroid.os.MessageQueue.next(MessageQueue.java:341)\nandroid.os.Looper.loopOnce(Looper.java:169)\nandroid.os.Looper.loop(Looper.java:300)\nandroid.app.ActivityThread.main(ActivityThread.java:8395)\njava.lang.reflect.Method.invoke(Native Method)\ncom.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:559)\ncom.android.internal.os.ZygoteInit.main(ZygoteInit.java:954)\n”,”scene”:”xcrash.sample.SecondActivity”,”isProcessForeground”:true,”tag”:”Trace_EvilMethod”,”process”:”xcrash.sample”,”time”:1688438970396}]
matrix Hook socket 生成的 trace.txt 文件部分如下 ,这部分内容和xcrash 共同

可是matrix 把 error sate anr 原因打印了出来

总结一下: 如果看到主线程仓库调用nativePollOnce 阐明主线程是闲暇的, 并不是形成anr 的原因 , 关于这类anr , xcrash 和 matrix 都能监控到 , 而且都能把仓库\内存\cpu信息打印出来 , 可是xcrash 并没有把anr 发生的原因打印出来 , 导致这类问题很难排查 , 所以此类anr 处理上 matrix更胜一筹 .
功能上matrix经过PLT Hook socket write()函数获取该日志 , 而xcrash 经过二次调用Runtime::DumpForSigQuit 办法 , 重定向fd 获取日志 , 每次调用Runtime::DumpForSigQuit 去dump仓库也是比 Hook socket write() 比较更损耗功能.
至于主线程卡顿 \ 死锁发生的anr , 经过两个框架输出的日志都能很好的进行剖析 , 这里就不多说了 .
经过ANR再谈Handler机制和事情分发机制
记得刚学android 的时分 事情分发机制和handler机制 无疑是听得最多的 , 看网上的文章剖析 , 许多基本都是对事情分发机制的那三大办法(dispatchTouchEvent ,onInterceptTouchEvent , onTouchEvent)一顿剖析 , 至于handler机制 , 许多都是对(MessageQueue ,Looper , Handler)源码的一顿剖析. 深入学习了两年半 , 总结下这两个的相关以及ANR .
首先说下Handler机制 ,大伙都知道 android 根据linux 内核 , 其实无论什么内核 , 只需创建出进程 , 当进程内的代码跑完了进程就退出了 , 所以要想进程不退出就只有一个办法 , 一向死循环下去 。 可是一向死循环有个问题 , 那就是cpu一向在空转 , cpu是硬件会一向消耗电量啊 , 所以得想个办法让cpu只在这个线程有活干的时分才调度 , 没活干得时分就堵塞,让出cpu的运用权 。让出cpu的运用权啊 , 这个简略,线程不是给咱们供给了运用Thread.sleep(long millis) , 我直接运用Thread.sleep(一年) 不就行了 , 运用不可能存活一年吧 , 可是这个不支持主动唤醒 ,所以Thread.sleep肯定是不可的。那有人会问不是还有Object.wait() 和 notify ,以及其他的办法吗 , 用这些办法不可吗 ,没活干的时分我就wait() 堵塞,有活的时分我就调用notify 唤醒。
如果是那种翻盖非智能手机,只运行在java虚拟机上的话这样做肯定是没问题,可是android 是根据linux 内核的 ,每一个app运用都是zygote fork出来的 ,都有自己art 虚拟机,art虚拟机也只是一个so库罢了,这个so库唯一作用就是用来执行为java文件转化而来的这些dex文件。事情从硬件到 linux 内核 再由内核分发给进程,内核唤醒主进程,终究在native主线程中经过 jni 调用Java 办法。能够看到这种做法适当费事,有什么什么比线程的堵塞系列办法更高效的?
android 根据linux ,linux中万物皆文件 ,比方dlopen 动态库会回来一个fd , 打开binder驱动会回来一个fd ,读写文件会回来一个fd ,运用socket 会回来sockfd ,等等 。一切事情都和fd挂钩。有没有能够一起监控这些的fd的?当然是有的linux 供给了多路复用的epoll 就能够。handler 机制中运用epoll_wait 堵塞当时线程让出cpu运用权 , 当监听到fd有变化时 epoll_wait会完毕等候堵塞,康复执行。
handler机制和事情分发有什么联络? 那当然是有联络的,事情从硬件发生终究到运用层的主线程的looper.loop死循环中被处理 。运用进程经过binder 和 wms、ims 通讯时,wms创建一对socket pair,用InputChannel封装,一个给app,一个给InputFlinger , 之后InputFlinger经过这个 socket fd把touch数据发给InputChannel 运用进程端。运用进程端 native 层经过jni调用到java层,事情分发到java层后 , 大致流程ViewRootImpl——>DecorView——>Activity——>PhoneWindow——>DecorView——>ViewGroup /view -> ViewRootImpl $WindowInputEventReceiver#finishInputEvent -> native 层经过 socket 通讯去把事情从 waitQueue 移除,从waitQueue移除表明已经处理完此次事情。
若窗口 waitQueue 非空 且 头事情分发超时 500ms, 则无法分发事情 , 然后安放定时器, 默认 5s 之后重试,重试的时分, 若依然分发失败, 则调用 onANRLocked 弹出 ANR 弹窗 。能够看到当handler 机制 中消息处理卡顿的时分,会导致事情不会及时从 waitQueue 中移除,然后无法分发新事情重试的时分仍是无法分发便会弹ANR 。