一次崩溃事件的调查过程

周六晚,收到反馈称新浪微博上有数十起用户在发表话题声称启动闪退.观察对应机型后,去往对应的应用商店查看评论,发现投诉闪退用户集中在O/V平台.且多为6.0及以下用户.

通过vivo开发者平台进行真机测试,完全无法重现,一方面新版本发布在即,需要确定问题在新版本中是否还存在,另一方面崩溃还在持续产生,问题比较严重.

第二天去公司排查问题,在意见反馈平台也发现大量闪退反馈,分析反馈上来的日志多为卸载重装后产生,多数没有价值,为数不多的有效日志中发现了breakpad的dump操作,且dump size普遍大于1,说明上一次so崩溃后,启动时dump过程中又崩溃了,有dmp文件累积.

  • 崩溃出现在oppo/vivo机型.
  • 崩溃出现在6.0及以下机型.
  • 友盟的java crash率无异常.
  • 反馈上传日志中breakpad dump操作.

根据以上特征可以确定是so崩溃.


既然是so崩溃,则需要去后台的错误收集平台查看dump完成后上传的堆栈信息,可惜的是由于是启动即崩溃,很多日志都没能上传成功.

这里发现一个优化点:

由于目前崩溃日志放在Android/data目录下,卸载时会清空,导致启动即崩溃的日志无法在卸载/清除数据后得到保留,这是一个很关键的点,需要改善.

经过对意见反馈日志的筛选,终于找出少量上传成功的设备,于是根据设备id去错误收集平台查看.分析日志发现导致崩溃的是一个叫libnative_lib.so的文件导致,这个文件不在apk文件中,好像是个系统so?(由于so名字问题,这里浪费了很多时间,后面详细介绍)

由于看起来是系统so的崩溃,日志所能提供的信息就到此为止了.换个思路,从改动点查起.

检查一下最近的版本发布

1.意见反馈的都是线上最新版本,其他版本没有出现意见反馈.(由于卸载后重装一般会使用线上最新版本,所以这里并不能代表旧版本没有出现崩溃,但是当时没有考虑到这一点)

2.崩溃发生在新版本上线一周后.

这里有两个分析点:

  • 新版本和上一版本在so方面有何不同?
  • 上线一周后,有没有什么动态配置的信息发生了变更?

新旧版本比对

经过apk文件的so比对,发现播放器组件,下载组件,广告组件在最近版本发布中有修改.于是一边联系对应开发方确认修改点,一边着手备用方案一:

回滚携带so且有变更的sdk库,保证新版本稳定发布.

很可惜的是,根据收集到的多方sdk修改点,结合崩溃产生场景,并没有关联.再加上回滚过程中产生其他编译问题/运行问题,这条路先放一边.

配置接口变更

目前配置接口主要包含两方面:app自身的配置接口和广告配置接口.

首先找上的是广告配置接口,因为这方面由于接入广告方众多,且已确定存在第三方动态加载dex和so行为,可控性不够,嫌疑最大.首先通知广告部门依次停掉可疑的广告开关,待5分钟生效后,与联系上的用户核查是否继续崩溃,需要一些时间,而且涉及广告缓存问题需要更细致的检查.

同时app自身的配置接口也不能放下,直接联系接口开发人员咨询在那个时间点附近有无修改配置信息,得知当天正好有两个修改点.于是开始检查对应配置修改对app的影响.

可惜的是,通过本地map local未能重现.

到此为止,动态配置的可能性好像被排除了.无法重现意味着无法确认修改方式是否有效,我们甚至一度怀疑是不是某些我们集成的某些不受控sdk针对长沙地区做了特别处理…

难道除了使用自己心里也没底的回滚sdk方案,就没有别的路了么?


就在快要放弃的时候,转机来了.

获得崩溃现场

在与负责自身配置接口的后台开发人员沟通过程中,得知他的手机也是一打开app就崩溃.在再三叮嘱保存现场,千万不要卸载或者清除app之后,同事立马驱车前往取手机…

拿回手机后,确实进入app就崩溃.虽然由于是release版本,无法进入私有目录,但是插上电脑可以看到日志还是非常有价值的.

然而现实很残酷,插上手机后依然没有太多有效信息.只是确认了两个问题:

  • 现在可以完全确认是so崩溃了.
  • 崩溃最后在这个so:libnative_lib.so.到底是哪里来的?(名字带来的误解仍然存在)

此时与用户的沟通的过程中收到一个重要反馈:断网情况下进入app,不崩溃.

毫无疑问和某个配置有关.

具体是哪个配置呢?app启动时调用的接口多达几十个.本来想着连上charles,一个一个去hook,来查找是哪个接口原因.但是接下来的一个事实让我懵了:

手机连接charles代理,将所有接口调用都加入black list,启动app仍然崩溃.

断网不崩溃,但屏蔽所有接口又崩溃,看来是之前某个配置的缓存在”发挥作用”:
拉配置之前会检查网络状态,若无网则不请求也不使用缓存,相当于未开启该功能.但是如果通过黑名单方式使接口请求失败了,就会使用上次下载的缓存配置.

但是目前无法通过清除数据来确认这一点,唯一的现场不能丢.

到底是哪个缓存有问题呢?能否把缓存拉出来分析,或者复制到另一台手机上看是否能重现?

考虑到这是一台未root的oppo手机,中间尝试导出app私有目录下的相关文件未果.

接口breakpoint

既然不能用黑名单,那就在接口请求上面打断点好了.通过对启动接口调用使用二分法排除之后,终于确定了一个有问题的接口.

原来这个libnative_lib.so竟然是接口下发的.

libnative_lib.so并不是什么系统so.仅仅是一些c++官方教程里面喜欢拿这个当做默认名字.

在这个接口打上断点后,可以维持5s左右不崩溃了,5s后接口超时,适用接口错误场景,会加载上次缓存数据,继续崩溃.

原因找到了,接下来就是对这个配置接口做一些修改,看能否通过其开关配置停掉对应的so加载.

改了下载路径后,终于不崩溃了.

现在可以把问题抛给对应sdk提供方,让他们去寻找修复方案了.


总结一下本次事故分析过程.

分析过程改进点:

  1. 不要被so文件名字迷惑,对系统自带so要有了解.
  2. 如果能确定是某个动态下发的文件导致,可以直接在charles中的response中搜索这个名字.

app开发风险点:

  1. 第三方动态加载dex和so过程不可控.
  2. 日志不能放在apk目录,应该在/sdcard中单独使用文件夹存储,反之后续启动即崩溃的场景中,用户安装其他版本后无法保留关键日志.

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!