Android ANR分析(trace文件的产生流程)

简介: Android ANR分析(trace文件的产生流程)

ANR信息获取(trace文件的产生流程)

首先收集需要dump trace的进程并给对应进程发送dump trace的信号

1.当一些带有超时机制的系统消息(如:Service的创建)判定超时后,会调用系统服务AMS接口,收集ANR相关信息并存档(data/anr/trace, data/system/dropbox)

2.进入到AMS中,AppError会先进行筛选(1.当前进程正在进行dump流程 2.已经发生crash 3. 已经被系统kill 4.系统是否正在关机等情况),如果都不符合,则认为当前进程发生了anr。

3.接下来系统在判断当前ANR进程对用户是否可感知,然后开始统计与该进程由关联的进程,或者一些系统核心服务进程的信息(例如与应用交互的SurfaceFligner,System Server等系统进程),如果这些系统服务进程在响应时被阻塞,那么将导致应用进程IPC通信过程被卡死。接着获取其他系统核心进程,因为这些服务进程是init进程直接创建的,并不在SystemServer或Zygote进程管理范围。

firstPids队列:第一个是ANR进程,第二个是system_server,剩余是所有persistent进程;

Native队列:是指/system/bin/目录的mediaserver,sdcard 以及surfaceflinger进程;

lastPids队列: 是指mLruProcesses中的不属于firstPids的所有进程。

4.在收集完第一步信息后,接下来便开始统计各进程本地的更多信息,如虚拟机信息,java线程状态及堆栈。首先会弹出一个ANR的对话框,然后向UI线程发送SHOW_NOT_RESPONDING_MSG消息

5.当UI线程收到该消息后,会调用dumpStackTraces函数:

最重要的一点:向目标进程发送SINAL_QUIT(进程中的Signal Catcher会进行阻塞检测收集信息后面讲),firstPids列表中的进程, 两个进程之间会休眠200ms, 可见persistent进程越多,则时间越长. top 5进程的traces过程中, 同样是间隔200ms, 另外进程使用情况的收集也是比较耗时.

总结;

将am_anr信息输出到EventLog(分析anr问题时先看该log)

获取重要进程的信息,java进程的,和native的进程

将ANR的Reason和CPU使用的情况输出到main_log

在将CPU使用情况和进程的trace文件信息,在保存到drpobox文件下

向收集到的进程发送SINAL_QUIT信号。


接着分析最后一步向收集到的进程发送信号

(Android5.0之前是dump用的SuspendAll线程,收集信息之后用ResumeAll恢复。在5.0之后采用的是checkPoint进行dump信息)

发生ANR时,systemServer进程会执行dumpStackTraces函数,在该函数中发SIGQUIT信号给对应的进程(上面有分析到)

处于安全考虑,进程之间是相互隔离的,即使系统进程也无法获取其他进程的信息,所以要借助于IPC通信,将指令发送到目标进程,目标进程接收到消息后,协助完成自身进程Dump信息并发送给系统进程。Android P 流程:

1.一个进程接收到了SIGQIUT信号的时候,SingaCatcher线程的WaitForSignal函数会返回接着会调用到HandlerSigQuit()函数。

2.hindleSigQuit()函数为:

3.DumpForSigQuit()函数:

这是读取的信息,但是什么时候去读取呢(什么时候才能保证获取到的却是是需要的东西,例如GC信息,当前分配了多少对象,这些打印一般都需要在suspend当前进程里面的所有的线程),接下来先分析这个suspend过程:

这个挂起SupendAll实在Thread_list.cc中实现的,他的作用就是用来suspend当前进程里面所有其他的线程(一般发生在GC,DumpForSigQuit等过程中)。SuspendAll过程实现最重要的就是ModifySupendCount(self,+1,false)这段语句他会修改对应Thread对象的suspend引用计数:

因为传入的delta值是+1所以会先执行AtmoicSetFlag()利用原子操作设置了KSuspendRequest标志位,代表当前这个线程有挂起请求。什么时候会进行检测这个标志位呢?这里涉及到了checkPoint的知识点最后讲解(在线程运行中进行上下文切换(例如java线程转换为Native线程)时就会运行CheckSuspend函数,这个函数才是真正的把当前线程suspend:

可以看到检测到了KSuspendRequest标记就会执行FullSuspend函数,KSuspendRequest标志位是用来dump线程的堆栈的,分析完了SuspendAll之后,再继续分析FullSuspendCheck函数:

调用TransitionFromRunnableToSuspend()这个函数后,线程就进入了KSuspended状态,然后在调用TransitionFromSuspendedToRunnablecpm函数从Suspend状态切换到Runnable状态的时候会阻塞在一个条件变量上,除非调用SuspendAll的线程接着又调用了ResumeAll()函数,要不然这些线程就会一直被阻塞住。

4.现在就把SuspendAll的流程分析完了,但是dump线程堆栈的时候并不是在设置了挂起标志位(KSuspendRequest)后执行的,与他相关的是另外一个标志位KCheckpointRequest,接下来看一下Thread_list的Dump函数,这个函数会在Thread_list的DumpForSigQuit中会被调用到,也就是在Signal Cathcer线程处理SIGQUIT信号的过程中。

这个函数先创建了一个叫DumpCheckPoint对象checkpoint,然后调用了RunCheckpoint将这个对象传入,这个函数会返回现在处于Runnable状态的线程个数,接着

调用了WaitForThreadsToRunThroughCheckpoint()等待这些处于Runnable的线程都执行完DumpCheckpoint的Run函数,如果等待超时就会报错。

接着分析RunCheckPoint函数,先看前一部分:

对于处于Runnable状态的线程执行它的RequestCheckpoint函数会返回true,其他状态的线程则会返回false。对于这些非Runnable状态的线程就会像SuspendAll一样会设置KSuspendRequest标志位,后面状态切换的时候就会检查这个标志位挂起。同事RunCheckPoint函数会把这些线程统计到suspend_count_modified_threads这个Vector变量中,在这个变量中的线程,Singal Catcher线程会主动触发他们的dump堆栈过程。接下来再看看这个RequestCheckpoint函数

最后一行设置kCheckpointRequest标志位,在刚才线程切换运行状态时会执行CheckSuspend函数在检测kCheckpointRequest标志位的时候会执行RunCheckpointFunction函数,接着会执行这个checkpoints里面元素的run函数:

(这个存储的其实就是Thread中的RequestCheckpoint在这里不仅设置了标志位还把参数设置为元素的值,这个参数就是Dump里面调用RunCheckpoint传过来的,其实就是DumpCheckpoint)。

,所以也就是执行DumpCheckpoint的run函数:

其实就是调用了Thread的Dump函数,线程的java堆栈,Native堆栈和Kernel堆栈就是在这里打印的,刚才说对于处于Runnable状态的线程是通过调用他们的RequestCkeckPoint函数,然后它们自己去dump当前堆栈的,而那些不处于Runnable状态的线程则是添加到了一个Vector的变量中,接着就分析RunCheckPoint函数的第二部分:

对于这些不是Runnable状态的线程,他们可能不会主动去调用Run函数,所以只能有Signal Catcher线程去帮他们Dump,至于DumpCheckpoint的Run函数的功能和Runnable状态的线程是一样的,都是打印线程堆栈,并且最后修改引用计数让这些线程在切换状态时继续运行。

总结

1.SingalCatcher线程接收到信号后,首先Dump当前虚拟机有关信息(内存状态。对象,加载class,GC等相关信息)

2.接下来会设置每个线程的标记为(check_point),和请求线程状态(suspend)。当线程运行过程中进行上下文切换时,会检查该标记。如果发现有挂起请求,会将自己主动挂起。等到所有线程都挂起之后,SingalCatcher线程开始遍历Dump各个线程的堆栈和线程数据后再唤醒线程。如果某个线程一直无法挂起导致超时,那么本次Dump流程失败抛出异常.

大致流程(Android5.0之前)

checkPoint:

先讲解safePoint,对于ART编译的代码,可以定期轮询当前Runtime来确认是否需要执行某些特定代码;可以认为这些轮询时的点,就是safepoint;safepoint可以用来实现暂定一个java线程,也可以用来实现Checkpoint机制;

例:

当正在执行java代码的线程A执行到safepoint时,会执行CheckSuspend函数,在发现当前线程有 checkpoint request时,

会在这个点执行线程的CheckPoint函数;如果发现当前线程有suspend request时,会进行SuspendCheck,使得线程进入Suspend状态(暂停);

所以说,ART CheckPoint应该是safepoint的一个功能实现;

safePoint讲解链接:

作者:RednaxelaFX

链接:https://www.zhihu.com/question/48996839/answer/113801448

来源:知乎

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

从编译器和解释器的角度看,ART的safepoint有两种:

主动safepoint:编译生成的代码里或者解释代码里有主动检查safepoint的动作,并在发现需要进入safepoint时跳转到相应的处理程序里。

ART的解释器安插主动safepoint的位置在循环的回跳处(backedge,具体来说是在跳转前的源头处)以及方法返回处(return / throw exception)。

ART Optimizing Compiler安插主动safepoint的位置在循环回跳处(backedge,具体来说是在跳转前的源头处)以及方法入口处(entry)。

被动safepoint:所有未内联的方法调用点(call site)都是被动safepoint。这里并没有任何需要主动执行的代码,而就是个普通的方法调用。

之所以要作为safepoint,是因为执行到方法调用点之后,控制就交给了被调用的方法,而被调用的方法可能会进入safepoint,safepoint中可能需要遍历栈帧,因此caller也必须处于safepoint。

安插safepoint的位置的思路是:程序要能够在runtime发出需要safepoint的请求后,及时地执行到最近的safepoint然后把控制权交给runtime。

怎样算“及时”?只要执行时间是有上限(bounded)就可以了,实时性要求并不是很高。

于是进一步假设,向前执行(直线型、带条件分支都算)的代码都会在有限时间内执行完,所以可以不用管;而可能导致长时间执行的代码,要么是循环,要么是方法调用,所以只要在这两种地方插入safepoint就可以保证及时性了。

至于具体在方法入口还是出口、循环回边的源头还是目标处插入safepoint,这是个具体实现的细节,只要选择一边插入就可以了。


相关文章
|
4月前
|
Android开发 开发者
Android自定义View之不得不知道的文件attrs.xml(自定义属性)
本文详细介绍了如何通过自定义 `attrs.xml` 文件实现 Android 自定义 View 的属性配置。以一个包含 TextView 和 ImageView 的 DemoView 为例,讲解了如何使用自定义属性动态改变文字内容和控制图片显示隐藏。同时,通过设置布尔值和点击事件,实现了图片状态的切换功能。代码中展示了如何在构造函数中解析自定义属性,并通过方法 `setSetting0n` 和 `setbackeguang` 实现功能逻辑的优化与封装。此示例帮助开发者更好地理解自定义 View 的开发流程与 attrs.xml 的实际应用。
Android自定义View之不得不知道的文件attrs.xml(自定义属性)
|
4月前
|
Java Android开发
Android studio中build.gradle文件简单介绍
本文解析了Android项目中build.gradle文件的作用,包括jcenter仓库配置、模块类型定义、包名设置及依赖管理,涵盖本地、库和远程依赖的区别。
383 19
|
4月前
|
存储 XML Java
Android 文件数据储存之内部储存 + 外部储存
简介:本文详细介绍了Android内部存储与外部存储的使用方法及核心原理。内部存储位于手机内存中,默认私有,适合存储SharedPreferences、SQLite数据库等重要数据,应用卸载后数据会被清除。外部存储包括公共文件和私有文件,支持SD卡或内部不可移除存储,需申请权限访问。文章通过代码示例展示了如何保存、读取、追加、删除文件以及将图片保存到系统相册的操作,帮助开发者理解存储机制并实现相关功能。
1059 2
|
7月前
|
移动开发 安全 Java
Android历史版本与APK文件结构
通过以上内容,您可以全面了解Android的历史版本及其主要特性,同时掌握APK文件的结构和各部分的作用。这些知识对于理解Android应用的开发和发布过程非常重要,也有助于在实际开发中进行高效的应用管理和优化。希望这些内容对您的学习和工作有所帮助。
669 83
|
6月前
|
安全 算法 小程序
【03】微信支付商户申请下户到配置完整流程-微信开放平台创建APP应用-填写上传基础资料-生成安卓证书-获取Apk签名-申请+配置完整流程-优雅草卓伊凡
【03】微信支付商户申请下户到配置完整流程-微信开放平台创建APP应用-填写上传基础资料-生成安卓证书-获取Apk签名-申请+配置完整流程-优雅草卓伊凡
352 28
【03】微信支付商户申请下户到配置完整流程-微信开放平台创建APP应用-填写上传基础资料-生成安卓证书-获取Apk签名-申请+配置完整流程-优雅草卓伊凡
|
5月前
|
NoSQL 应用服务中间件 PHP
布谷一对一直播源码android版环境配置流程及功能明细
部署需基于 CentOS 7.9 系统,硬盘不低于 40G,使用宝塔面板安装环境,包括 PHP 7.3(含 Redis、Fileinfo 扩展)、Nginx、MySQL 5.6、Redis 和最新 Composer。Swoole 扩展需按步骤配置。2021.08.05 后部署需将站点目录设为 public 并用 ThinkPHP 伪静态。开发环境建议 Windows 操作系统与最新 Android Studio,基础配置涉及 APP 名称修改、接口域名更换、包名调整及第三方登录分享(如 QQ、微信)的配置,同时需完成阿里云与腾讯云相关设置。
|
6月前
|
监控 Shell Linux
Android调试终极指南:ADB安装+多设备连接+ANR日志抓取全流程解析,覆盖环境变量配置/多设备调试/ANR日志分析全流程,附Win/Mac/Linux三平台解决方案
ADB(Android Debug Bridge)是安卓开发中的重要工具,用于连接电脑与安卓设备,实现文件传输、应用管理、日志抓取等功能。本文介绍了 ADB 的基本概念、安装配置及常用命令。包括:1) 基本命令如 `adb version` 和 `adb devices`;2) 权限操作如 `adb root` 和 `adb shell`;3) APK 操作如安装、卸载应用;4) 文件传输如 `adb push` 和 `adb pull`;5) 日志记录如 `adb logcat`;6) 系统信息获取如屏幕截图和录屏。通过这些功能,用户可高效调试和管理安卓设备。
|
7月前
|
Dart 前端开发 Android开发
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
166 4
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
算法 Java API
Android性能优化面试题经典之ANR的分析和优化
Android ANR发生于应用无法在限定时间内响应用户输入或完成操作。主要条件包括:输入超时(5秒)、广播超时(前台10秒/后台60秒)、服务超时及ContentProvider超时。常见原因有网络、数据库、文件操作、计算任务、UI渲染、锁等待、ContentProvider和BroadcastReceiver的不当使用。分析ANR可借助logcat和traces.txt。主线程执行生命周期回调、Service、BroadcastReceiver等,避免主线程耗时操作
267 3
|
Java 数据库 Android开发