编译器如何做测试
Posted 光荣之路
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编译器如何做测试相关的知识,希望对你有一定的参考价值。
新书
速递
吴老的java版《selenium webdriver 实战宝典》和python版《selenium Webdriver 3.0 自动化测试框架实战指南》出版了,代码拿来就能用。
文 | Emark
不知不觉,做编译器工具链测试已经有8年了,随着国家对芯片的重视,“中国芯”迎来了前所未有的机遇,作为芯片的核心配套软件,编译器也越来越多进入了人们的视野。编译器主要是将高级软件翻译成底层芯片能够认识二进制码,主要包括编译器、汇编器、链接器。如果编译器有问题,可能导致生成可执行程序的行为和源程序语义不一致,使得程序出现意想不到的错误,这种错误对应用程序开发人员来讲,不容易被检测和发现,容易导致重大的网上事故,因此编译器可靠性十分关键,对编译器的系统化测试和质量保证也变得至关重要。本文主要介绍下编译器是如何测试的。
1 功能测试
任何对象的功能测试一般都是从分析输入输出开始的,编译器的输入就是某一种语言,每种语言都有自己的语法语义规范(specification),以C/C++语言为例,就有十分明确的规范,比如C89/C99标准,现行标准是C11。因此编译器测试主要一部分工作是做语法意义规范的测试,我们称之为标准测试。
标准测试主要包括类型定义,类型转换,运算符操作等,由一系列小程序组成(几十行),目前通用语言的编译器能够买到一些商业标准测试套,以C/C++语言为例,主要包括,supertest,cvsa,plamhall等,用例规模一般几万个左右。也比较容易能够找到一些开源社区的测试套,比如gcc社区的dejagnu,社区用例主要是编译器开发过程中出错小程序的积累,因此也称之为regressiontest,回归测试用例,dejagnu有将近8万个用例。
标准的商业测试套和开源regressiontest测试套构成了编译器的基本功能用例。
对于通用芯片的c语言编译器(比如面向x86和arm),有了这些基本的用例,就能保证编译器基本功能的正确性。
但是对于一些dsp芯片的编译器,语言在C语言的基础上会有比较多的语法扩展,就需要测试工程师来生成这些用例,这些用例规模比较庞大,因此自动化用例设计是编译器测试工程师的必备技能。根据语法的复杂程度,用例规模从几千到几万不等,多的会有10多万。
要实现和消化这些用例,编译器功能测试工程师必须对语法特别精通。
2 优化测试
1)优化功能测试
编译器为了实现最佳性能,会做比较多的优化,比如死代码消除,循环展开等,编译器测试工程师需要测试这些优化功能是否生效,比如一行死代码真的消除了;由于编译器的优化非常复杂,尤其一些指令调度的优化,通过常规的语法覆盖非常困难,因此这部分工作通常情况下会有开发工程师完成,测试工程师主要承担黑盒角度测试的部分。
2 )优选选项组合测试
针对前面提到的各种优化,编译器会有相应的优化选项,以GCC为例,常规的选项有O0选项,不做任何优化,主要用于调试和定位,正确性高;O2/O3选项,在执行性能上有明显提升;OS选项,对代码体积上有明显优化。此外GCC还提供几百种优化选项,这些选项都可以开关控制,因此组合是一个天文数字。
编译器测试工程师会对这些选项进行随机组合,然后测试一些特定的用户代码,从而发现选项组合情况下的编译器的bug。这个过程比较耗时,且无法完成穷举,需要和开发工程师一起讨论,从而在某个局部的范围内进行搜索。
3 专项测试
性能测试
性能测试主要是为了检测编译器性能是否达成既定目标,主要包括执行性能,代码体积,在开发过程中,主要方法是对一些通用和专用的benchmark进行对比测试,通用的BM如spec2006和nullstone等,专用的BM主要通过提取一些用户代码中的热点函数,性能数据直接统计cycle值,方便开发获取一些性能反馈,从而不断改进提升。临近版本交付的时候,对特定客户,则通过真实代码下的实际性能对比来进行评估。
环境测试
这块主要在各类操作系统下完成编译测试,在各类芯片上完成运行测试。
一致性测试
一致性测试是编译器测试一个十分重要的方向,分两部分:
a)在编译器不变的情况下,确保编译器前后两次编译器出来的二进制代码行为一致,不同操作系统下编译器出来的二进制代码行为一致,主要方法是编译生成的汇编代码进行对比,需要严格一致。
b)优化效果的评估,这个时候是期望汇编代码产生变化,从而观察优化是否生效。
兼容性测试
主要包括前后编译器兼容,O0/O2等混编场景下的兼容测试,可以选一些大型的代码库完成验证。
代码库
前面介绍编译器基本功能用例主要是由一系列小程序组成,但是为了确保编译器身经百战而不败,需要不断积累各种类型的代码,越多也好,各种应用场景都可以包含,比如数据库类代码,通信类代码,图形图像类代码等等,这些代码最好自身有验证用例,从而间接的测试编译器的正确性。
自举测试(bootstrap)
自举测试主要是编译器自己编译自己,对于新生成的编译器,能够跑通全量用例,bootstrap是编译器开发过程中的重要里程碑,不过个人认为,这块其实可以归结为代码库的测试。
4 专业的工具
语言作为编译器的输入,是一个无穷尽的组合,靠手写产生的case比较有限,为了实现更多复杂场景的覆盖,专业的工具是编译器测试的另外一个核心部分,这块最近几年也是学术研究的热点,核心技术是程序分析技术。
随机测试(csmith)
论文:Finding and Understanding Bugs in C Compilers工具网站:http://embed.cs.utah.edu/csmith/
其主要原理:对语言的各类语法进行随机组合,包括变量,表达式,运算符等,加上语言合法检测,生成一个代码长度可控,代码嵌套深度可控,传参数量等等可控的测试代码,且保证语法准确,通过O0和O2等不同优化对比,实现运行测试,该方法生成的程序非常复杂,能比较有效的发现一些藏得较深的bug,尤其是优化引起的。目前csmith已经发现了1700+的社区编译器的bug,包括gcc,llvm。
等效模型测试
论文:Compiler Validation Via Equivalence Modulo Inputs
其主要原理,对于给定程序,通过覆盖率分析等技术,分析程序中的死代码,通过删除死代码,得到一个功能一致的新程序,通过对比两个程序的输出,实现自动化预期和测试。该工具可以结合随机生成的用例,实现测试用例源源不断的获取。
变异测试
Skeletal Program Enumeration for Rigorous Compiler Testing
其主要原理,对于给定骨架的小程序(小程序最好之前发现过bug),通过一些代码变换技术,包括变量替换,实现程序的变异,从而发现相临的bug,可以用于regressiontest。
智能化测试
Learning to Prioritize Test Programs for Compiler Testing
智能化测试主要研究方向是如何提升编译器测试效率,如何提升测试用例的有效性等。
5 自动化框架
编译器测试用例庞大,每天需要执行百万规模以上的用例,才能确保当天合入代码符合一定的质量要求,因此自动化测试是编译器测试的标配。编译器用例自动化率非常高,接近100%。一个友好的自动化测试框架是高效工作的基础,现在开源社区的自动化框架做得比较好,完全能够满足用户的需求,主要由gcc dejagun和llvm lit。
为了提升测试效率,分布式自动化工厂也是一个重要的配置,因为执行越快,质量反馈越快,从而提升团队的整体效率。目前基于容器的自动化框架,即能实现分布式测试,也能按需的分配资源,是一个不错的选择。
6 总结
综观各类语言的编译器,包括gcc,llvm,javac,go等,一般都由标准的语法测试+功能测试+优化测试+随机测试等方法组成,编译优化技术是编程语言、语言虚拟机的核心,因此编译器的测试方法也可以大部分复用到编程语言和语言虚拟机的测试。
综上所述,作为一名专业的编译器测试人员,需要精通各种语言的语法规则,擅长程序分析技术,掌握自动化测试技术和各类自动化框架。想必看完这篇文章的应该对编译器、编程语言和虚拟机等开发和测试感兴趣,现在随着团队的壮大,十分欢迎对编译器、编程语言、语言虚拟机等基础软件感兴趣的开发和测试有志之士加盟,一起投身基础软件事业,为芯片做软件,为中国芯加油。
有意向的兄弟欢迎投递简历:masaijun@huawei.com
点此链接了解
感谢认真阅读的你!⬇
以上是关于编译器如何做测试的主要内容,如果未能解决你的问题,请参考以下文章