移动端堆栈关键性定位的新思路

一、前言

崩溃堆栈是我们日常应用问题排查中的重要辅助手段,在移动开发上也不例外,为了支持用户在堆栈上的快速定位,我们面临一个看似比较简单问题:高亮崩溃中的关键行, 辅助用户快速定位问题。

崩溃堆栈关键性: 堆栈中是属于用户开发代码中那行直接引起崩溃的代码。

举个例子:

移动端堆栈关键性定位的新思路

二、 业界方案

业界的竞品基本上是通过 Package Name判断的,在没有 Package Name 的情况下,有的竞品会定位到第一行,有的则会定位到非系统库的第一行。

例如: 友商这种情况下就将关键行挂在了第一行 fastjson 的位置。

移动端堆栈关键性定位的新思路

这里容易出现两个问题:

1.Package Name 大多数时候和真正的崩溃包名关系不大。

2.App 组件化,包名不能覆盖一方库,二方库。
为了更好的解决这个问题,我们提出了下面用词频比/词频分的方式来解决问题的新方案。

三、新方案

所以在 Package Name 的基础上,我们还需要一个辅助手段,让我们能够识别这两种情况,从而在关键行定位更精准。

这里我们想到的一个做法就是利用全量的 Crash 崩溃堆栈,计算词频比和相应的词频分,通过概率去优化我们的关键行判断。

实现上分为两个平台。

3.1 对于 iOS

1)主包判断

这个问题,对于 iOS,其实不用考虑用户填写的 Bundle ID, 因为 IOS Crash 天然就自带 Binary Images,我们将用户主包信息预存下来,用于后续判断就行了。
Binary Images

移动端堆栈关键性定位的新思路

2)直接定位

移动端堆栈关键性定位的新思路

对于组件化的包,我们可以通过 Binary Images 里面的信息统计一下每个包名出现的频率,具体的频率分布统计大致如下图所示,纵坐标代表包名出现的次数:

注:横坐标为包名(这里放不下),纵坐标为包名出现次数

移动端堆栈关键性定位的新思路

出现的频率越低,那么我们越认为他是一方库或者二方库。

3.2 对于 Android

对于 Android,情况稍微复杂一点,首先 Android 的 Crash 中其实是不能明确标识包名的,而且 Android 的 Package Name 并不是一个词,而是一长串的以点分隔的包名, 例如

"com.aliyun.emasha.cache"。

如果单纯的还以包名的词频比来做匹配的话,那么就会出现下面的问题
a.历史数据 只出现 com.aliyun.emasha.cache 的包名, 下次出现个 com.aliyun.emasha.login 的就匹配不上了。
b.同样是 com.aliyun.emasha 的前缀,匹配到了 com.aliyun.emasha 和匹配到了 com.aliyun.emasha.cache 包名的词频相差很大,不符合常理。

所以还要解决这两个问题
a.能够尽可能的覆盖未出现的崩溃情况。
b.随着匹配的前缀越长,需要考虑前面的包名匹配带来的影响。

所以这里要引入包名分级和词频分的概念
a.包名分级:将包名 split(".") 得到数组,从前往后为 1级,2级,3级这样的分级。
b.包名词频分:根据包的词频比多级累加算出来的一个评价包名是否是三方库的分数,分数越高,是三方库的几率越大。

但这还不够,如果我们的词频比只是单纯的累加,那么 com 开头的的包名,词频分一定会很高,大于所有的 org 开头的包名,但根据我们的经验,其实不是这样的,我们认为不同级别的匹配,权重应该是不一样的,所以我就拍脑袋想了个权重。

0 5 2 1 1 1

这里举个例子

com.alibaba.aliyun.emas.ha.tlog 这个包名
com 1
com.alibaba 0.3
com.alibaba.aliyun 0.1
com.alibaba.aliyun.emas 0.05
com.alibaba.aliyun.emas.ha 0.02
com.alibaba.aliyun.emas.ha.tlog 0.01

如果匹配到 com 那么词频分为 1 * 0
如果匹配到 com.alibaba 那么词频分为 1 0 + 0.3 5 = 1.5
如果匹配到 com.alibaba.aliyun 那么词频分为 1 0 + 0.3 5 + 0.1 * 2 = 1.7
以此类推

但是在我们的经验中匹配到了 com.alibaba 和匹配到了 com.alibaba.aliyun,后者更有可能是关键行,所以它的词频分按理来说也就更低。所以我们这里做一个符合常理的修正,对于位数过短的匹配,需要后几位的权重做补齐。

最终结果如下:

如果匹配到 com 那么词频分为 1 0 + 1 5 + 1 2 + 1 1 + 1 1 + 1 1 = 10
如果匹配到 com.alibaba 那么词频分为 1 0 + 0.3 5 + 0.3 2 + 0.3 1 + 0.3 1 + 0.3 1 = 3
如果匹配到 com.alibaba.aliyun 那么词频分为 1 0 + 0.3 5 + 0.1 2 + 0.1 1 + 0.1 1 + 0.1 1 = 2

看上去是比较符合我们的经验的。

所以这里词频分的最终定义:根据包的词频多级累加算出来的一个评价包名是否是三方库的分数,分数越高,是三方库的几率越大。如果一个包名分级过短,需要把缺失的后面分级的也算上累加,用于增大短包名的词频分。

我们对所有的包做一个词频分统计,可以得到如下分布图

注:横坐标为包名(这里放不下),纵坐标为包名的词频分

移动端堆栈关键性定位的新思路

根据观察和测试,这里把阈值定在 0.2 左右比较能区分用户的包名和三方、系统库。

3.3 整体架构

在工程实现上我们也做了一些优化:

1.以前业务数据是存储在 OSS 中的,但是 EMR-OSS 目前文件处理较慢,这里换成了更适合并行处理的 HBase。
2.只计算增量 Crash 日志, 对于存量的数据,以 HyperLogLog 的形式存储,增量计算后与存量做 Merge。

移动端堆栈关键性定位的新思路

四、效果评估

常规的利用 Package Name 做判定: F1 Score

移动端堆栈关键性定位的新思路

使用词频分思路的:F1 Score

移动端堆栈关键性定位的新思路

五、真实效果评估

上面的效果评估只考虑到了每一个包名的情况,在生产因素下,考虑到崩溃行出现的位置,包名出现的频率,以及没关键行的情况,准确率可能会有所不同,所以我们在真实环境做了高亮测试,测试方式为:对线上50个 App,每个 App 取前3条崩溃来做统计,总的准确率如下,可以说是比较高的。

安卓准确率:(333-9)/(333)*100%=90.91%
iOS准确率:(173-0)/(173)*100%=100%
总体准确率:(503-9)/(503)*100%=94%

六、思考

小需求可以做出大深度, 后续我们可以考虑更多跨用户数据的脱敏拉通,理解数据,为客户带来更多的数据价值。

七、接下来的方向

1.组内算法的朋友说可以通过打标 + CNN 的方式来做深度学习下的三方包名判断, 这个后续可以试一试。

2.对于凭经验拍脑袋相出来的参数和方程(词频分计算),其实都可以通过打标训练的方式做参数和方程的固定,这也是一个优化方向。

作者:阿里云 云原生应用研发平台EMAS 张月(此间)

 

原文链接

本文为阿里云原创内容,未经允许不得转载。