Crash排查系列第一篇|利用hprof文件排查线上偶现问题

841 阅读3分钟

hprof文件不仅仅只能排查oom,还能帮助我们排查验证一些奇奇怪怪的问题,下面是仅举了两个例子。希望通过这几个例子能拓宽大家排除crash的思路。

案例一

可以先思考一下,遇到以下线上偶现的crash你会如何去排查。

TransactionTooLargeException

image.png

分析

TransactionTooLargeException是什么

发生在 远程程序调用(remote procedure call), 这个过程中,参数和返回值会以 Parcel存储 Binder 事务缓冲(transaction buffer)中,当参数和返回值过大,会发生该异常。

查看源码

image.png 这边我们可以发现可能是mState或者mPersistentState太大了,这边系统也有做相应信息的dumpStats

可以发现logcat里有以下信息

image.png 可以看出是fragment里面保存了太多东西。路径是androidx.lifecycle.BundlableSavedStateRegistry.key 下的android:support:fragments 下的android:support:fragments

查看代码可以知道android:support:fragments 保存的是下面的parcelable

image.png 通过系统的logcat我们只能知道这个FragmentManagerState的Parcelable保存数据太大了,具体是哪里还需要将parcelable转为FragmentManagerState再通过日志打印出来。

日志打印方案的缺点

为了找到具体哪个对象占用过大,我们先需要把Parcelable转为FragmentManagerState 这一步就比较麻烦了,需要嵌套循环的去打印

解决

既然是查看对象内存占用问题,其实我们可以通过查看hprof文件解决,在补获TransActionsTooLargeException异常同时去dump下当时的hporf文件 再通过hprof文件回捞排查问题。

image.png

通过回捞回来的hprof文件发现是这个CommunitySubTabModle创建了300多万个对象。(这里也可以反过来证明打日志还需要控制好嵌套层数和数组大小,否则就是300w个对象的日志打印了)

这边也很容易想到是反解Parcelable的时候出问题了,查看代码变更 ,Parcelable对象确实新增了个两个字段,反解析的时候读了缓存的老内容导致顺序异常了 。

案例二

线上偶现crash 万分之一不到。

image.png

image.png

Restore的时候mActivie中的fragment找不到了,而mActive中fragment通常是在destroy时移除,结合调用处的代码猜想很有可能这个时候activity已经被destroy了。 如何去验证呢。

我们同样通过crash时的hporf的保存和回捞。

观察viewpager2中保存的数据 找到包含crash信息中uuid的viewpager2,找到引用viewpager2的activity,确认确实已经被destroy了 而且这边activity也被泄漏了。

image.png

image.png

image.png

image.png

找到泄漏处代码

image.png

hprof回捞流程

目前仅适用于crash后dump。

  1. 安全中心配置 ,安全中心匹配到规则后进行hprof dump
{

"crashId": "10993293", //crash 平台crashid 

"matchCrashType": "java.lang.IllegalStateException", //crash类型

"matchMessage": "Fragment no longer exists for key", //crash message  

"matchMethod": "restoreState",//crash method  堆栈上一处匹配即可

"matchClass": "androidx.viewpager2.adapter.FragmentStateAdapter", //crash 类 堆栈上一处匹配即可

"dumpHprof": 1,//hprof dump开关

"crashTag": 0 

}
  1. 完成dump后会产生埋点上报,需要根据crashId手动整理一波userid

  2. 使用文件回捞功能。

  3. 等待回捞成功通知。