首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

使用GDB进行代码覆盖率测试(1)

使用GDB进行代码覆盖率测试(1)

简介熟悉 Excel 的程序员都知道,Excel 不仅是一个应用软件,还能作为一个开发平台。这不仅是因为 Excel 提供了 VBA,更重要的是 Excel 本身处理了数据库连接,数据处理以及报表生成等复杂的工作。程序员从而避免了自己实现这些功能的负担。
同样,我们认为 gdb 本身的强大功能也使得它可以成为一个开发平台,充分利用它的符号处理能力和进程控制功能,我们可以开发出一些新的功能。
测试工程师经常面对的一个问题就是如何获得测试的代码覆盖率。很多专业软件可以提供这种专门的代码覆盖率检测。通过对 GDB 的小小改造,也可以令其提供代码覆盖率测试功能。这种改动与平台无关,只要 GDB 支持的平台,都可以运行。
基本原理GDB的一个基本功能就是单步运行程序,我们想到,如果在每次单步运行的时候,记录下运行过的代码数量,将此数据与总代码段长度比较,不就可以获得代码覆盖率了吗?
最初的想法很简单,但是让测试人员不停地单步执行显然是不现实的,因此我们扩充了基本的gdb命令,增加了一条命令叫做covertest。该命令不断地自动调用单步执行命令,并在每一个单步命令之后,记录下运行过的代码行数。直到程序运行结束。然后covertest命令读取ELF文件头,得到总的代码段长度。最后,用记录下的运行过的代码数量除以总的代码段长度,从而得到代码覆盖率。
经过几周的调试,我们在RedHat9.0/x86平台上,修改GDB5.3,成功地实现了代码覆盖率测试功能。
代码覆盖率定义和代码长度我们把代码覆盖率定义为运行过的代码长度除以程序总的代码长度。
代码长度是二进制代码长度。而不是在C源文件中的代码长度。比如一条赋值语句在C语言中就是一条语句,但是编译为汇编语言后可能是一条,也可能是多条汇编指令。而且在Intel IA处理器中,指令长度是可变的。因此我们所说的代码长度是指最终的二进制代码的字节长度。
这种定义可能不是最佳的定义.但是是最容易实现的定义。在本文中,代码覆盖率采用机器指令长度作为衡量标准。
下面的例子比较了不同的代码长度的定义:
增加命令covertestgdb是一个命令行工具,它基本的工作模式类似Shell。接收用户输入的命令然后执行相应的处理函数。gdb中CLI(command line interface)子系统负责用户界面的工作,它显示提示符,接收用户输入,分析用户输入并调用相应的处理函数。
CLI子系统的设计非常完善,它为用户添加新命令提供了几个专门函数。add_com()就是最基本的一个。它有四个入口参数,第一个参数是命令的名字,类型为字符串;第二个参数表明该命令的类型;第三个参数是该命令的处理函数,第四个参数是关于该命令的帮助说明。:
1
2
3
4
struct cmd_list_element *add_com (char *name,  
                                  enum command_class class,
                                  void (*fun) (char *, int),
                                  char *doc)




下面的代码显示了如何添加新的gdb命令.
1
2
3
4
5
_initialize_mark (void)
{
  struct cmd_list_element *c;
  c = add_com("covertest",class_breakpoint,set_mark,"test coverage");
}




_initialize_mark函数调用add_com()为gdb添加新的命令。covertest对应的处理函数为cover_command()。其中class_breakpoint是一个枚举变量,表示命令covertest属于断点类的命令。当用户键入help breakpoint后,就能看到命令covertest以及对它的说明,即add_com()的第四个参数”test coverage”。
选择合适的单步命令GDB提供了几种不同的单步调试命令:step,stepi,next和nexti。
首先attach到setmark时fork的新进程,该进程ID已经保存在全局变量org_pid中。直接调用gdb函数attach_command()完成attach工作。
我们选择step命令来单步执行程序。因为该命令遇到子函数能够进入子函数内部。step命令不会进入动态链接库函数,比如printf。因为没有debug信息。这种特性非常符合代码覆盖率测试的要求。用户使用代码覆盖率测试工具只希望了解自己编写的代码的覆盖率情况。而不需要了解第三方库函数以及系统库函数的覆盖率.比如下面的代码片段:
1
2
3
4
void main(){
  a = 10;
  printf(“a is %d\n”,a);
}




运行该程序的代码覆盖率显然为100%。但是printf()函数本身非常复杂,用户并不希望了解printf()的覆盖率。该函数非常复杂,显然上述调用不可能百分百地覆盖printf()。如果单步进入printf(),则最终的测试覆盖率结果就包含了对printf的测试,其结果就不会是100%了。
利用gdb这个特性可以自动区分第三方库函数和用户自己编写的函数,这使得代码覆盖率测试的工作更加简单了。
返回列表