1.如何导出日志到 anr目录
a.导出anr日志文件:data/anr/traces.txt /anr
由于厂商限制,部分手机使用adb pull的时候,由于权限问题导致无法导出b.无权限时,使用:adb bugreport /anr
可以将一个包含anr日志的zip包导出到 /anr路径定位:查看主线程block信息即可:”main” prio=5 tid=1 Blocked
2.源码:系统如何监听ANR ?
- ActivityManagerService中在产生ANR的时候,会回调到AMS的appNotResponding()方法;
|
|
- dumpStackTraces()这个方法,此处将dump出firstPids与lastPids进程的相关线程堆栈信息至traces.txt,1234// 根据此环境变量获取traces.txt生成路径,默认值为/data/anr/traces.txt// 删除旧的traces.txt文件,并创建新文件// 每个进程是按照先后顺序dump信息至traces.txt文件中的// 通过FileObserver监听完成写入的操作,并通过wait()/notify()机制等待/唤醒,并继续发送Signal给下一个进程,见下面的for循环
如何监听?
第一种:
- 关于reason信息的获取,到目前为止,我们只知道能在logcat中能检索到相关信息,难道要开个线程不断循环去检测logs?这想法看来还是Too Young Too Simple。
- 这里先说下结论,可通过ActivityManagerService.getProcessesInErrorState()方法获取进程的ANR信息,此方法是通过逆向Bugly时发现的,后面会讲到。
- 此方法会遍历mLruProcesses,并根据进程目前的异常状态如crash或者anr类型,返回具体的ProcessErrorStateInfo,具体看代码,比较简单:
|
|
第二种:
这部分内容获取比较简单,直接读取/data/anr/traces.txt里面第一个进程的信息即可,能保证首个进程即是当前ANR的进程,至于为什么上面分析AMS源码中已经说明了,当前进的pid会首先被add进firstPids中被优先输出。此处的难点是如何从traces中过滤出相关的信息,如进程名,进程pid,生成时间,各线程名、tid、优先级、状态、堆栈信息等。这里就要用到强大的正则表达式来进行过滤了,此处忽略一万字…(大家可以反编译Bugly的SDK,参考里面的正则表达式)
什么时候去获取?
在上面AMS的源码分析中,我们可以关注到dumpStackTraces()方法中的FileObserver,系统通过该类监听文件/data/anr/traces.txt来达到顺序依次写入各个进程的traces信息。按照这个思路,当ANR发生的时候,我们也可以通过监听该文件的写入情况来判断是否发生了ANR,看起来这是一个不错的时机。需要注意的一点是,所有应用发生ANR的时候都会进行回调,因此需要做一些过滤与判断,如包名、进程号等。
|
|
上面这段代码比较简单,就是一个启动monitor的方法,监听/data/anr/这个目录,并过滤包含trace的文件名。在FileObserver创建的时候可以传入一个mask参数,8正是代表着CLOSE_WRITE这个常量,当有写入并且close的时候将会回调,跟AMS中的使用基本吻合。
继续翻看b.class的代码,当过滤出trace文件的时候,会执行b.this.a(var3)这个方法。从代码逻辑上可以看出,这是一个对trace文件有效性判断的方法,
然后对进程异常状态和进程号进行判断,过滤掉无效或其他应用的回调,最后对有效的trace进行处理。
如何获取全部线程信息呢
监听
File :pid/thread