睡觉的时候,程序能不能自动查 bug?
The following article is from CSDN Author CSDN App
IT服务圈儿
有温度、有态度的IT自媒体平台
责编 | 郭芮曾在 Hacker News 上看到过一个 Oracle 工程师处理 bug 的 日常:
后来这个工程师感慨说:“I don't work for Oracle anymore. Will never work for Oracle again!”Oracle 12.2 有将近 2500 万行 C 代码,复杂系统的测试是一件艰难、艰苦和艰巨的事情,而测试一个分布式数据库的情况就更复杂了。我们永远不知道用户可能写出什么样的 SQL,表结构和索引有多少种组合,此外还要考虑集群在什么时候节点发生宕机,以及受到网络抖动、磁盘性能退化等因素的影响——可能性几乎是无限的。那么有没有一种方法能让程序自动帮我们查 bug?先花两周左右时间来理解 20 个参数如何通过神奇的组合引发 bug。改了几行代码,尝试对 bug 进行修复,提交测试集群开始跑近百万个测试 case,通常要 20~30 小时。运气好的话会有 100 多个 case 没过,有时候上千个也有可能,只好挑选几个来看,发现还有 10 个参数之前没有注意到。又过了两周,终于找到了引起 bug 的真正参数组合,并跑通了所有测试。并增加 100 多个测试 case 确保覆盖他的修改。经过一个多月的代码 review,他的修改终于合并了,开始处理下一个 bug……
01
如何做到「睡觉的时候让程序自动查 bug」?
项目的思路其实很简单,如果在每次跑 case 的时候能用统计学的方法对足够多次实验的代码路径进行分析,就可以找出疑似 bug 的代码,最终结果以代码染色的方式由前端可视化呈现,就得到了如下图展示的效果:
02
背后的原理
项目最初是受到 VLDB 的一篇论文的启发 APOLLO: Automatic Detection and Diagnosis of Performance Regressions in Database Systems,该论文主要围绕如何诊断引发数据库性能回退的代码,其核心思想也同样适用于排查 bug。论文中提到的自动诊断系统由 SQLFuzz,SQLMin 和 SQLDebug 三个模块组成。
SQLFuzz:负责随机生成 SQL,并利用二分查找定位到性能回退的前后两个版本,传递给 SQLMin 模块。
SQLMin:通过剪枝算法将 SQLFuzz 生成的 SQL 进行化简,得出能够复现该问题的最小 SQL ,传递给 SQLDebug 模块。目的是减少无关的代码路径,降低噪音。
SQLDebug:对源码进行插桩,使其在执行 SQL 时能够输出代码的执行路径。然后对两个版本的代码路径进行分析,建立一个统计模型来定位问题的位置。
哪一次的代码 commit 引入了性能回退。
存在问题的代码源文件。
具体的函数位置。
03
聊聊细 (gān) 节 (huò)
如何自动产生测试 case?由于是基于统计的诊断,我们需要先构建足够多的测试用例,这个过程当然最好也由程序自动完成。事实上,grammar-based 的测试在检验编译器正确性方面有相当长的历史,DBMS 社区也采用类似的方法来验证数据库的功能性。比如:微软的 SQL Server 团队开发的 RAGS 系统对数据库进行持续的自动化测试,还有社区比较出名的 SQLSmith 项目等等。今年 TiDB Hackathon 的另一个获奖项目 sql-spider 也是实现类似的目的。这里我们暂时采用 PingCAP 开源的随机测试框架 go-randgen 实现 SQL fuzzing,它需要用户写一些规则文件来帮助生成随机的 SQL 测试用例。规则文件由一些产生式组成。randgen 每次从 query 开始随机游走一遍产生式,生成一条 SQL,产生一条像下图红线这样的路径。
// http://localhost:43222/trace/df6bfbff
{
"sql": "show databases",
"trace": [
{
"file": "executor/batch_checker.go",
"line": null
},
{
"file": "infoschema/infoschema.go",
"line": [
[
113,
113
],
[
261,
261
],
//....
}
],
}
line 字段输出的每个二元组都是一个基本块的起始与结束行号(左闭右闭)。基本块的定义是绝对不会产生分支的一个代码块,也是我们统计的最小粒度。那是如何识别出 Go 代码中基本块的呢?其实工作量还挺大的,幸好 Go 的源码中有这一段,我们又刚好看到过,就把它裁剪出来,成为 go-blockscanner。
04
还能不能做点别的?
看到这里,你可能觉得这个项目不过是针对数据库系统的自动化测试。而实际上借助代码自动调试的思路,可以给我们更多的启发。源码教学阅读和分析复杂系统的源码是个头疼的事情,基于源码的运行时可视化跟踪能否做成一个通用工具呢?这样在程序执行的同时就可以直观地看到代码的运行过程,对快速理解源码一定会大有帮助。更进一步,配合源码在线执行有没有可能做成一个在线 web 应用呢?全链路测试覆盖统计语言本身提供的单测覆盖统计工具已经比较完备了,但一般测试流程中还要通过 e2e 测试、集成测试、稳定性测试等等。能否用本文的方法综合计算出各种测试的覆盖度,并且与 CI 系统和自动化测试平台整合起来。利用代码染色技术,还可以输出代码执行的热力图分析。结合 profiler 工具,是不是还可以辅助来定位代码的性能问题?
05
接下来的工作
以上我们只完成了一个非常简单的原型,距离真正实现睡觉时程序自动查 bug 还有一段路要走,我们计划对项目持续的进行完善。接下来,首先要支持并行执行多个测试用例,这样才能在短时间得到足够多的实验样本,分析结果才能更加准确。另外,要将注入的代码对程序性能的影响降低到最小,从而应用于更加广泛的领域,比如性能压测场景,甚至在生产环境中也能够开启。看到这里可能你已经按耐不住了,附上项目的完整源码:
https://github.com/fuzzdebugplatform/fuzz_debug_platform
黄宝灵,PingCAP 前端开发工程师,喜欢 React 和 TypeScript。
满俊朋, 效率工具工程师, 目前在 PingCAP 从事 Benchmark, Stability 相关工具的研发。
杜沁园,中科大研究生,曾在 PingCAP 实习,从事数据库测试工具的研发。
韩玉博,中科大研究生,在 Tradeshift 实习,从事前端开发。
本文转自公众号“CSDN”,ID:CSDNnews)
*版权声明:转载文章和图片均来自公开网络,版权归作者本人所有,推送文章除非无法确认,我们都会注明作者和来源。如果出处有误或侵犯到原作者权益,请与我们联系删除或授权事宜。