【NCTS峰会回顾】饿了么邱化峰:人工智能在Bug定位中的应用

开发 前端 人工智能
2019年10月26日,由Testin主办的第二届NCTS中国云测试行业峰会在京召开,此次峰会以“AI+未来”为主题,汇聚来自国内外测试领域的知名专家学者、领先企业决策者、高层技术管理者、媒体从业者等,共同探讨高端云测试技术。

2019年10月26日,由Testin主办的第二届NCTS中国云测试行业峰会在京召开,此次峰会以“AI+未来”为主题,汇聚来自国内外测试领域的知名专家学者、领先企业决策者、高层技术管理者、媒体从业者等,共同探讨高端云测试技术,帮助测试从业者了解最前沿行业趋势,及最新的行业实践。

[[283752]]

会上,饿了么测试开发专家邱化峰做《人工智能在Bug定位中的应用》主题演讲。邱化峰指出,“企业想获得人工智能带来的便利,首先要制订相对应的标准,如果没有这个标准,按照固定的算法,则很难实现。”

以下为邱化峰演讲实录:

今天我讲的是人工智能在Bug应用中的定位,讲这个话题之前,我想先做一下统计,大家用java管理Bug的人员有多少?在修复Bug之后,在代码里面,想关联Bug是什么样的同学请举手。人工智能定位首先需要有一个数据的基础,这个数据的来源是什么样的?测试通过java提交Bug,我们有Bug的描述,Bug的相关信息,以及Bug的重现步骤,会直接提给开发,开发根据这些步骤去执行,看Bug是不是可重现的。如果是可重现的,会要求开发把相关的问题进行关联,关联之后作为Bug分析的一个前提,不需要在人工去标注什么样的样本,因为把Bug描述的相关信息放上之后,我们就可以通过自然语言或者是人工的处理方式,提取含有Bug相关的字段,比如出现的异常是什么,错误是什么,问题是什么样的。有了这个基础之后,不需要人去做标注,就可以拿到一些样本。还有一个是学术界要做的,首先,有些人专门维护了所有关于java的Bug,这些Bug在修复里时会生成一些数据,告诉你哪个方法在第几层,哪个方法哪一行出现了什么错误。如果想使用人工智能带来的便利,首先要制订一些相对应的标准,如果没有这个标准,很难按照固定的算法来得到想要得到的东西。讲完了这些前提,我主要讲三部分,第一是工具介绍,第二是Bug定位的原理和方法,第三,使用这些工具之后应用从哪些方面做具体的落地。

工具分了八种,这个PPT是之前做的,最近一个月又出来一个新的Bug定位的技术,就是基于行为的Bug定位技术。前面这八种定位技术都是以能拿到相关的样本来做对应的处理,而且这些东西是通过提取测试用例,编译测试。这大部分是基于单元测试做的,但是基于行为的已经突破了在单元测试的层面去获取Bug的样本,通过基于链路去分析是什么样的情况。第一个是程序频谱,就是我跑多少测试用例,成功的测试用例覆盖了哪些行,失败的覆盖哪些行。我们计算失败的测试用例占的行,我们做一个排名,就可以拿到这一行代码出现BUG的概率是多少。然后是基于变体,程序频谱的基础上做了一个改良,变体现在应用的,最实用的一个是衡量测试用例的有效性很重要的一个手段,我写了那么多测试用例,这些测试用例是不是真的有效,我可以去变体来做衡量。基于变体会生成两个,有一个变异分数,分数越高的时候,单元的测试用例的质量就比较高。程序切片就是通过程序切片或者场景,生成程序用例的方式,程序切片也可以用于Bug定位,然后是堆栈跟踪,APP端有用监控的,类似于接了监控系统的同学举一下手。安卓系统底下的应用,比较多的只发安卓的,不发iOS的同学有多少?大部分都是既发安卓又发iOS。基于堆栈追踪,堆栈话题要讲到好多,比如日志,log,线上的这些东西怎么去拿。堆栈追踪,不仅仅应用于服务端的Bug的定位,也可以应用于C端的Bug定位,比如界面突然卡掉了,或者突然卡死,我拿到这些对应的堆栈信息,也可以直接来进行Bug的定位,以及做Bug的预测。第五是上下文的切换,这个就不讲了。第六是信息检索,通过一些词频,或者去算出现概率比较多的,大家知道热点覆盖概念的同学请举一下手,你能说一下吗?

 

听众:有个热点图,看哪边用户点击更多,哪边点击更少。

邱化峰:我们是变向的热点覆盖的方法,映射到修改的方法是最多的。最后是机器学习。如果我们需要知道怎么去定位Bug,通过人工智能的方式,首先要定义各种指标,这些指标就决定了在定位Bug中的准确率有多高。基于java这里列了7个,首先看一下,代码的有效行数,圈复杂度,包括是否符合javadoc的规范,为什么要把这个作为参考指标?阿里巴巴推出了java编写规范,不是说凭空无中生有的,是基于扫描了阿里所有的代码仓库包括历史记录总结出来的,比如空格应该空几行。我们有人工智能定位的工具,通过统计含有Bug的类型做了一个统计,Bug产生因素最多的就是随意的命名变量名,这是作为了定位Bug中得出来的最主要的参考指标。Bug的产生大部分是由人的理解或者认知在一定范围之内造成错误的使用的某个变量名,包括函数。代码编写规范,第一代是函数式的,第二代是面向对象的,面向对象的是不是万能的,我们面向对象去写的时候,是不是易于维护的和易于理解的。最主要的有一个是扇入和👕扇出,我去做Bug定位的时候,如果这个方法被多次引用,也会增加Bug出现的概率。然后是包的设计质量,之前是单体的应用,现在讲微服务,又在讲中台,这些是相当于在包的基础上做了一些变革,之前我们是单体的,把它拆成微服务之后应该怎么做,当把微服务单独抽出来做中台,最主要的改变就是Jar包的结构和这些东西也在随之不断的发生变化。第四个,用来分析Jar包的升级质量,变量的个数和参数的个数,这也是在Bug中影响Bug定位的主要指标,大家见过的,能知道测试过程中发现变量数最多的同学,有没有说发现使用了多少参数,或者一个函数能支持多少个函数,有知道的同学吗?参数的个数,语言多多少少有差异,我在函数传参的时候,函数的参数名和变量最多只允许有256个,超过256个,程序就跑不起来了。变量的个数和参数也是作为Bug定位的指标。再看最后一个,运算符的个数,大家也知道功能测试和做中间件测试的,基本上是不一样的,不一样在哪儿?基于业务的可能是业务场景的,逻辑,或者是业务场景相对来说比较固定,逻辑的单一化比较好一点,但是作为一个中间件,会考虑很多的不确定因素。当我们去考虑这些不决定因素的时候,其实是变向增加了运算符的操作。

第二部分,原理和方法。我们刚才也给大家普及了基础的东西,这部分的training set,都是来自于实际中编写的代码或者是样例,是作为Bug预防的第一代原形,相对来说比较简单,只用了一个圈复杂度,知道圈复杂度的同学举一下手,为什么可以作为预测Bug出现概率的重要指标?就是圈复杂度会根据你写的代码,里面有多少分支,走多少流程,跟圈复杂度是对照的。一个函数里面,如果圈复杂度,工业级别上的,好比军工的软件,一般检测出来不超过7,或者更严格的,代码复杂度不能超过5,当圈复杂度越高的时候,就证明写的函数体越来越长,但是如果在5和7以内,大概40行左右,超过7以上,到20了,代码行数就有可能变成几千行了,几千行的函数,无论是让原来的开发,还是接手的新开发来去做这个事情,都变得很难维护。既然是我们能知道圈复杂度有这样的功能,我们可以用圈复杂度来预测提测代码之后,去扫一下圈复杂度,看是不是在合理的范围内之内,如果不在合理的范围之内,可能会有一定的概率出现Bug。

这个图里面介绍了学术界应用,把加入的代码出现的Bug解决掉,做了一个样本,没有修Bug之前代码是怎么写的,修完Bug之后代码又是如何写的。

这是基于Bug定位出现的第二个原形,用来预测Bug缺陷。从第一个原形到第二个原形,中间已经多了很多步骤,首先需要有一个dataset。第二是前面说的学术界的获取dataset的方法。第三是用人工自然语言的去处理,不用java管理的,或者不跟javaBug关联的,但是有很多开发去直接写,用英文也好或者用中文注释也好,会说某某个问题,这是一般开发都会做的。第三个是获取样本的方法。将来可能出现Bug的因素有哪些,这是这些因素做了排序和处理,做Bug定位或者预测的时候,首先得知道哪个指标应该占什么样的比例,用刚才的方法,就去不断调整各种指标占的比例,然后根据所占的这些比例多少,判断实际得到的和最终现实发生的真实的Bug匹配度,在人工智能中有一个专业名词是召回率,来看最终的结果,这是属于第二代的原形。在这个原形里面,有一个test的样本,大部分是使用变异测试来做的。

这是基于java的,参考各种因素和指标,能得到一些样例结果的展示。每种定位技术,得到的能定位的结果差异也是有很多的,如果大家想去看具体的实现,在这个底下有一个连接,把种种技术,包括方法,源码,结果,都生成一个统计结果,可以到里面看具体的执行情况是什么样的。

这是堆栈,现在的堆栈抛出,一般情况下,无论是前端也好,还是后端也好,都要求封装。Exception,堆栈有多个方法,异常的会提示说属于什么样的异常,既有基于系统级别的,也有中间件定义的,也有用户行为定义出来的,这个时候我们不能单纯的说用它做缺陷预防,我们首先要把三个分层,不能直接简单拿过来用,基于堆栈去预测这种情况,这样就会更加准确。这个里面会告诉你某一个类,某一个方法,底下具体是哪一行到哪一行,我们基于热点覆盖的时候经常会提一个概念,拿到修改的行,通过修改的行去匹配,这个行的代码就会明确的告诉你我在这行抛了,开发去解决问题的时候,就有经验的开发,会做一件事情,永远去看0或者1的,不去看后面的,后面再抛出来的跟现在是完全不一样的。既然开发都能够通过这个来做,我们可以转化为一个缺陷预防,或者是判断的技术手段,这个时候我们就可以去针对这些抛出来的Exception,再做一个排名,需要收集线上所有的日志里面,把所有日志里面无论这类也好,还是这个方法也好,只要在里面出现一次,我将排名就加“1”,会首先看全局的哪类哪个方法,即使行数不一样,我要统计到这个方法底下服务端的排名情况。

我刚才所说的都是为如何建立我的缺陷预防的指标的参考依据,大家可以看到这些指标,比如有多少个链路的调用,总共有多少函数处在这个破抛出来的这些堆栈的异常相关信息,十前面的ST作为基础数据,我们发现只是用基础数据去预测Bug出现Stack Trace概率并不是很准确,到底出现Bug的概率是什么样的,我们相当于做了一个类似于加权平均,或者统计学里面应该叫取一个均值,把参数做一个综合,举个简单的例子,如果这个项目中只有两个文件,如果抛出来一个堆栈异常,两个类里面有多少个,和我有10个文件,但是每个类里面的代码行数对应的行数不同,如果想全面的了解到Bug出现的概率,就不能简单的以其中一个指标作为参考。做了一个组合之后,就相当于把出现Bug的概率和情况做了一个更加详细的指标定义。我们有了这个指标的定义,就更容易预测Bug出现的概率。

这是堆栈信息人工智能预测的模型,把Stack Trace和错误的代码进行了关联,做了一个提取和模型的建立,并且自动的把它打标,第二个阶段,我已经有了线上这么多出现的异常,我们新来一个,能不能直接定位到某一类某一行的方法上。为什么要做这个事情?现在测试不是左移就是右移,对于我们来说,跟开发合作的时候,他认为比较理想的测试是说不用给那么多步骤,不用那么多方法,能不能写一个单元测试,能不能直接告诉我问题出在哪儿。有很多的方法是希望测试做到这样的,并不是说测试做不到,测试也是可以做到的,但是需要很多的工具,包括历史数据的积累才能做到。而且有很多的情况,测试对于场景,包括业务了解得更多,这里只写了模块的一小部分,如果把测试的这些反馈到开发身上,就可以做到这一点。

这是第三代的技术,会把静态分析和动态分析做整合,把代码的覆盖率,方法的覆盖率,包括分支的覆盖率,统一放到矩阵里面,在这个矩阵相对应的,可以认为原生的第一代是一维的,只有一个东西帮我们做判定,这个是一个二维的,到了第三代其实是做了更多维度的,但是这个维度里面既包含了静态,又把动态的也放进来了,同时也把覆盖的行这些东西也都做到统一的PageRank Matrix里,从多个角度去看能不能预测这个Bug出现的概率。

最终,我们会得到一个Rank List,我们统一做各种排序,再去看预测Bug出现的概率就会比之前更加准确。

还有一个是基于行为的,也是最近刚刚出现的用来定位Bug的,就不展开描述了。

然后我们说一下怎么应用,程序频谱是一种有效的Bug定位的方法,作为卡壳的时候,首先不能单纯的说以指标来衡量,需要把它作为综合的指标去看整体的提测质量之后,就会发现之间是有正相关的,或者呈线性的数据的指标,代码写得越烂,出现的Bug概率就会越高,这是毋庸置疑的。第二,Stack Trace对于Crash的定位是很有效的,现在手机经常运行着的时候就不动的,或者响应比较慢,我们去拿到堆栈的错误异常,上报上来,这个时候对于去定位这些异常是很有效的,用它来提高安卓端的响应,包括出现什么样的错误,这是十分有效的手段。第三,错误的样本集目前仍然不够大,刚才说的三种方式,还漏掉很多没有按照规范和固定操作制度,中间有很多开发没有按照这个去做,有一个叫全样本,这也是息息相关的因素。还有一个,算法的多样性,我们用静态和动态的调用,里面的算法也是多样性的,目前有20多个算法,算法也决定了预测Bug出现概率的重要参考因素。很显然,第一代的不如第二代的,第二代的不如第三代的,但是这些迭代的版本、方法多了之后,原来简单的算法也不适应了,每增加一个因素,或者每增加一个参考的因子,模型也要做相对应的处理,这样才能预测Bug出现的情况。

现场视频(下)

通过这些Bug定位的技术和方法,我们拿到最直接、最有效的方式。把它作为实地落地的情况,通过这些能够对我的企业产生直接收益,一般情况下,前面分析了那么多,但是这些东西都不能落地,或者都不能提高协同的效率,推广和应用是很困难的,我们通过它可以做到的有这四个场景,能够提高大家的效率,辅助开发来定位Bug。阿里内部有一个可视化的质量分,分了四个维度,第一是圈复杂度,第二是面向对象的指标,第三个我忘记了,每个指标里面都有因子,每个方面给一个权重,提测完代码之后进行开发,跟代码的质量分是不是有直接关联。 指标定义好,就可以发现问题,指标定义不好就会出笑话。

Bug的重复检测,如果我们做交叉,或者是比较大的,比如我们使用外包,免费的从网上公开共测的东西,他们提过的Bug之后,我们也可以去做去重,来决定将来判定一个重要的指标。Bug的自动assign,每个开发提交的,根据这个场景涉及到的case,或者提交分支版本,可以将Bug和这个产生者直接做一个关联,出现了Bug就可以不需要判定是哪个开发要做的事情,可以告诉是这个东西出了问题。然后是case的等级分类,相信大家在做第四个的时候,都应该会做,如果不做case的等级分类,就是测试连入门都没有做到。为什么要把它提出来?因为我们发现现在有很多的工具也可以帮我们生成单元测试的代码,但是这些单元测试的代码和这个之间并没有作为强关联。大家都知道“8020”法则,80%的Bug来自于20%的代码,我们所有case也不应该在同一个级别,可以针对这个理论,对这些20%的代码做case等级的分类。迭代的版本越多,开发和测试不断在维护自己自动化的脚本,包括单元测试的脚本,这些东西会慢慢的越积越多,这个时候之前第一个版本跑了10分钟就能跑完,但是运行了半年,或者是一年以上,这个项目没有死,你会发现测试用例一直往上堆,之前跑10分钟,半个小时就能跑完,现在是用空间换时间看case执行的情况。我们除了用空间换时间之外,还可以用等级分类的方法把这些case标志出来,可以用少一些的空间,或者是少一些的时间。

这是四篇论文,同学可以自己去网上找,研究一下哪些对你们来说是可以直接落地的。

今天的分享就到这儿。

 

责任编辑:张燕妮 来源: 51CTO
相关推荐

2017-12-05 15:03:45

人工智能饿了么大数据

2022-08-18 14:37:39

人工智能社交媒体数字化

2022-10-08 23:54:26

人工智能垃圾分拣机器人

2017-04-18 09:12:06

人工智能SEO技术

2021-07-21 10:24:28

人工智能AI深度学习

2021-06-21 14:08:28

人工智能AI航运物流

2022-07-13 15:05:27

人工智能农业

2021-04-27 11:20:41

人工智能制造业AI

2023-08-08 18:08:17

2022-03-09 10:17:34

人工智能审计碳信用

2023-10-20 11:12:43

人工智能供应链管理

2020-08-17 09:26:09

人工智能股票训练

2022-03-08 13:59:35

人工智能空战技术

2022-08-25 14:06:33

人工智能游戏

2022-07-26 16:54:14

人工智能物流

2022-07-21 12:59:39

人工智能交通领域

2023-10-07 07:48:41

人工智能质量技术

2022-08-09 13:48:30

人工智能时尚行业聊天机器人

2023-07-27 18:03:51

人工智能AI

2018-02-25 13:09:42

人工智能视频场景应用现状
点赞
收藏

51CTO技术栈公众号