当前位置:XML > XML历史

代码质量提升之道代码覆盖率原理与移动

白癜风专家李从悠 https://jbk.39.net/yiyuanfengcai/ys_bjzkbdfyy/792/

一、背景介绍

随着项目迭代的不断深入,工程逻辑与用户场景日益复杂,传统的白盒测试体系已经无法适应苛刻的工程质量要求,因此有必要针对工程质量进行精细化管理。质量评估不再单纯依赖bug率和性能指标,而是通过精准的数据来量化代码质量,代码覆盖率就是其中的一项重要标准。

简单来说,代码覆盖率就是单元测试或者UI测试过程中对于被测代码的覆盖程度,可分为以下三种度量方式:

StatementCoverage:最基础的一种覆盖方式,用以度量被测代码中每个可执行语句是否都被执行。可执行语句不包括头文件声明、宏定义、代码注释等BranchCoverage:分支覆盖,度量程序中每一个判定的分支是否都被测试LoopCoverage:度量对循环体进行了多少次覆盖在实践中,通常采用StatementCoverage方式来分析覆盖率数据。拿到覆盖率数据之后,分析未覆盖部分的代码,可以反推出测试是否充分,前期的测试用例设计是否合理,没有覆盖到的代码是否是设计的盲点。同时其对于冗余代码的检测具有重要的参考意义,可以逆向反推出代码设计中存在的问题,提醒开发人员理清代码逻辑关系,提升代码质量。

代码覆盖率高不能说明代码质量高,但是代码覆盖率低,意味着代码质量很可能存在一些问题,因此代码覆盖率可以作为代码质量的一个重要衡量标准。

二、原理与演示

获取代码覆盖率的前提是插入计数器,也就是插桩,从直观上讲,可以分为三种方案:

源代码插桩:直接在源码中的每行可执行语句中加入计数器。该方案开发成本太高,且影响包大小和运行时性能中间文件插桩:在编译过程的中间文件插入汇编代码,成本低,速度快,是理想的插桩方案可执行文件插桩:目前暂时没有合理的方案。

因此,我们采用中间文件插桩方案,过程如下图所示:

源代码文件首先经过编译预处理,之后编译成汇编文件,在生成汇编文件的同时完成插桩。每个桩点插入若干条汇编语句,直接插入生成的*.s文件中,最后将汇编文件生成目标文件。在程序运行过程中桩点负责收集程序的执行信息。

以源文件test.c为例:

预处理命令(gcc-Etest.c-otest.i)之后,生成test.i文件,对该文件进行编译(gcc-fprofile-arcs-ftest-coverage-Stest.i),生成test.s汇编文件。可以看到gcc是通过增加编译选项-fprofile-arcs-ftest-coverage来进行插桩。

打开汇编文件test.s,其中可以看到代码运行过程中计数器增加的逻辑:

上图中展示了计数器自增的过程,将llvm_gcov_ctr赋值给寄存器rax,之后将rax加1,最后将rax赋值给llvm_gcov_ctr。

除了上述逻辑,汇编文件中还列出了覆盖率收集的入口方法__gcov_init,该方法中的逻辑为:

在可执行文件进入用户代码段main函数之前,先调用__gcov_init函数初始化统计数据区,即将所有文件的gcov_info结构组织成一个链表,链表头就是gcov_list,其为全局链表,将在输出统计数据时应用。

之后__gcov_init调用atexit(gcov_exit)将gcov_exit函数注册为exithandlers。

App运行完调用exit正常结束时,gcov_exit函数就会得到调用,其内部调用gcov_flush函数输出统计数据到*.gcda文件中,也就是从gcov_list的第一个gcov_info结构开始,为每个被测文件生成一个.gcda文件。.gcda文件的主要内容就是gcov_info结构的内容。

插桩之后,会生成test.gcno文件。运行可执行文件后,生成test.gcda文件。

最后,运行gcovtest.c命令可生成test.c.gcov文件,如下所示:

每行前面的数字表示被执行次数,####表示该行未被执行,需要重点


转载请注明:http://www.vviuov.com/xgyy/1063113.html

  • 上一篇文章:
  • 下一篇文章: 没有了