ANR提问
官方文档
- 什么是 ANR?
- 系统是如何设计 ANR 的?
- 发生 ANR 时系统都会获取哪些信息以及工作流程?
- 导致 ANR 的原因有哪些?
- 遇到这类问题该如何分析?
- 如何能更加快速准确的定位问题?
- 面对这类问题我们能主动做些什么?
ANR思考
- ANR是系统对Activity,Service,Input,BroadCastReceiver等组件的超时监控,对超时抛出异常。
- 系统有很多服务启动监控,在超时时间内完成任务会关闭监控,没完成则触发超时报警。
- ANR时,系统会捕获当前运行线程的堆栈信息,执行的快照信息,获取和当前线程交互的服务进程的信息,当前活跃进程的信息。
卡顿问题分析指标
1. 线程cpu使用率/线程堆栈信息
1.系统整体cpu使用信息
cat /proc/stat
2.进程cpu使用信息
cat /proc/[pid]/stat
使用率计算
利用信息整理出自定义log,类比trace log(系统进程信息+线程堆栈信息)
案例:解析系统信息打印到log文件
2.获取线程对战堆栈信息:
Thread.getStackTrace()
Thread.getAllStackTraces()
案例
2.trace log 分析
线程状态信息,Java定义的6中线程状态对应Native线程的状态:
线程状态资料
// download-common-49 代表线程名称
// prio = 优先级
// Native = 代表当前线程的状态,Native代表执行JNI代码
"download-common-49" prio=5 tid=62 Waiting
//group是线程组名称 sCount是线程被挂起的次数 dsCount是线程调试状态下挂起的次数
//obj是这个线程的Java对象的地址 self表是这个线程Native的地址。
| group="main" sCount=1 dsCount=0 flags=1 obj=0x133c32b0 self=0x6ead411c00
// sysTid 是此线程在内核中对应的线程ID
// nice 代表着struct_task任务被调度的优先级
// cgrp是调度组 sched分别是线程的调度策略和优先级 handle是线程的处理函数地址。
| sysTid=22035 nice=10 cgrp=default sched=0/0 handle=0x6ec1dacd50
//state = native 线程状态 schedstat值来自于/proc/[pid]/task/[tid]/schedstat,分别是cpu上执行的时间、线程的等待时间和线程执行的时间片
// utm是线程用户态下执行时间 stm是内核态下执行时间(单位是jiffies,执行IO等操作才会切换到内核态) HZ可推出执行时间
| state=S schedstat=( 43132810 109269790 129 ) utm=1 stm=3 core=3 HZ=100
| stack=0x6ec1caa000-0x6ec1cac000 stackSize=1039KB
| held mutexes=
at java.lang.Object.wait(Native method)
- waiting on <0x00b6662f> (a java.lang.Object)
at java.lang.Object.wait(Object.java:442)
at java.lang.Object.wait(Object.java:568)
at com.tencent.qqmusic.qzdownloader.downloader.impl.b.r(SourceFile:142)
- locked <0x00b6662f> (a java.lang.Object)
at com.tencent.qqmusic.qzdownloader.downloader.impl.b.a(SourceFile:652)
at com.tencent.qqmusic.qzdownloader.downloader.impl.b.b(SourceFile:339)
at com.tencent.qqmusic.qzdownloader.downloader.impl.b.a(SourceFile:233)
at com.tencent.qqmusic.qzdownloader.downloader.impl.d.a(SourceFile:35)
at com.tencent.qqmusic.qzdownloader.downloader.impl.DownloadTask.a(SourceFile:263)
at com.tencent.qqmusic.qzdownloader.downloader.impl.DownloadTask.run(SourceFile:54)
at com.tencent.qqmusic.module.common.thread.h.run(SourceFile:55)
at java.util.concurrent.ThreadPoolExecutor.processTask(ThreadPoolExecutor.java:1187)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:929)
at com.tencent.qqmusic.module.common.thread.c$1.run(SourceFile:35)
锁信息案例:
"OkHttp ConnectionPool" daemon prio=5 tid=41 TimedWaiting
| group="main" sCount=1 dsCount=0 flags=1 obj=0x133c04e8 self=0x6f31ff8400
| sysTid=8743 nice=0 cgrp=default sched=0/0 handle=0x6ec2f5bd50
| state=S schedstat=( 69290105 74527077 261 ) utm=2 stm=4 core=3 HZ=100
| stack=0x6ec2e59000-0x6ec2e5b000 stackSize=1039KB
| held mutexes=
at java.lang.Object.wait(Native method)
- waiting on <0x0e321c0e> (a com.android.okhttp.ConnectionPool)
at com.android.okhttp.ConnectionPool$1.run(ConnectionPool.java:106)
- locked <0x0e321c0e> (a com.android.okhttp.ConnectionPool)
at java.util.concurrent.ThreadPoolExecutor.processTask(ThreadPoolExecutor.java:1187)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:929)
3.systrace/Perfetto使用
Android系统跟踪概览
了解systrace
ftrace
atrace
介绍
systrace只能跟踪系统API,如果想确定应用中调用的函数执行时间,需要自定义事件,使用函数插桩在函数执行开始和结束分别调用:
Trace.beginSection("方法名")
...
Trace.endSection()