内核角落:Linux 测试项目
Linux 测试项目 (LTP) 的开发旨在通过将自动化测试引入内核设计来改进 Linux 内核。在 LTP 之前,Linux 开发人员没有可用的正式测试环境。尽管大多数开发人员都对自己的增强功能和补丁的效果进行了单元测试,但系统化的集成测试并不存在。LTP 的主要目标是为开源社区提供一个测试套件,以帮助验证 Linux 内核的可靠性、鲁棒性和稳定性。该套件测试内核功能和回归,包括带压力和不带压力的情况。LTP 不是性能基准测试,但基准测试通常用于在测试期间驱动内核。
LTP 最初是 SGI 开发的 100 个测试程序。现在,通过 SGI、IBM、OSDL、Bull、Wipro Technologies 和各个 Linux 开发人员的共同努力,LTP 包含超过 2,500 个测试程序(也称为测试用例)和许多自动化工具。LTP 支持多种架构,包括 x86、IA32/64、PPC32/64 以及 32 位和 64 位 s/390。
尽管存在其他测试套件和项目,但 LTP 包含一个用于定义新测试、集成现有基准测试和分析测试结果的环境。软件测试自动化框架 (STAF/STAX) 是一个开源系统,允许您计划、分发、执行和收集来自大型多平台测试主机的测试结果池。STAF/STAX 还提供了一个强大的 GUI 监控应用程序,允许您与作业进度进行交互和监控。测试覆盖率可视化工具让您了解内核执行了多少测试的源代码。
IBM Linux 技术中心 (LTC) 在使用 LTP 发现 Linux 内核中的缺陷方面发挥了关键作用。通过使用 LTP,LTC 测试了 50 多个新的内核版本,并发现了 500 多个缺陷。正如 Linda Scott 的白皮书(请参阅在线资源)中所述,典型的内核测试周期使用 LTP 进行重点测试,以隔离和验证 Linux 组件和应用程序的稳定性。这包括对新内核进行回归测试,以确保它们满足先前内核的功能。然后,集成测试验证组件交互,由宏基准工作负载驱动。最后,可靠性和压力测试通过延长持续时间测试(96 小时到 30 天)来验证系统鲁棒性。
本文的其余部分描述了如何使用自动化工具下载和运行 LTP 测试套件。我们还将讨论一些可用于帮助改进内核开发和测试的 LTP 工具。
这些测试涵盖了广泛的内核功能,包括系统调用、网络和文件系统功能。测试套件的基本构建块是一个测试程序,它执行一系列操作并验证结果。测试结果通常仅限于通过或失败。所有测试程序和工具共同构成了 LTP 包。
LTP 是一个 GPL 包,可从 SourceForge.net 获取。LTP 测试套件源的稳定版本 ltp-yyyymmdd.tgz 每月发布一次。截至本文撰写之时,最新版本是 ltp-20040405。下载软件包后,按如下方式解压和安装
tar zxf ltp-20040405.tgz cd ltp-20040405 make make install
您需要 root 访问权限才能执行最后一步以及运行测试套件。测试套件也以二进制和源 RPM 格式提供。对于那些喜欢冒险的人,可以通过匿名 CVS 下载开发快照(请参阅资源)。
安装完成后,可以使用多种选项来运行 LTP 测试套件。最常用的方法是使用 runalltests.sh 脚本,该脚本执行大约 800 个原始测试。runall 中未包含的测试具有破坏性、需要监控,或者由于其他原因无法自动化。runall 脚本具有默认行为,即运行测试套件的单个迭代并生成详细的屏幕输出。可以使用 quiet 选项 (-q) 省略此输出。作为一个简单的介绍,我们现在忽略屏幕信息,并使用 -l logfile_name 和 -p 选项来生成人类可读的日志结果。
测试用例由名为 Pan 的测试驱动程序执行。Pan 包含在 LTP 包中,是一个轻量级驱动程序,用于运行和清理测试程序。runalltests 脚本调用 Pan 来执行一组测试用例或单个测试用例。您可以通过向 runalltests 提供 -f 场景文件来执行一组测试用例。场景文件是一个简单的 ASCII 文本文件,包含两列。第一列包含测试用例的名称,第二列包含要运行的命令。注释以井号开头。例如
# Testcase to test mmap function of the kernel testcase1 mmap3 -l 100 -n 50 # Testcase to stress the kernel scheduler testcase2 sched_stress.sh
测试驱动程序使用测试用例的退出值来决定测试的成功或失败。如果测试用例以非零值退出,Pan 将其记录为失败。如果测试用例以零值退出,则驱动程序将其记录为通过。
测试套件最简单的用法是在您的系统上运行它,以确保没有失败
runalltests.sh -l log -p -o output
对于已知的失败,LTP 包包含解释和指向更多信息位置的指针。以下是从在 2.6.3 内核上运行 ltp-20040506 的部分日志文件
Test Start Time: Mon May 17 14:20:45 2004 ----------------------------------------- Testcase Result Exit Value -------- ------ ---------- abort01 PASS 0 accept01 PASS 0 access01 PASS 0 ... rwtest01 PASS 0 rwtest02 PASS 0 rwtest03 FAIL 2 rwtest04 FAIL 2 rwtest05 PASS 0 iogen01 PASS 0 ... ----------------------------------------------- Total Tests: 797 Total Failures: 6 Kernel Version: 2.6.3-gcov Machine Architecture: i686 Hostname: ltp2
在此部分日志中,运行了 797 个测试,其中 6 个失败。rwtest03 和 rwtest04 是由于 mmap 资源耗尽而失败的 I/O 测试。此问题已解决。其余失败(未在日志中显示)如下所述
setegid01:验证 setegid 是否不修改保存的 gid 或实际 gid — 由于 glibc 2.3.2 中的错误而失败。
dio18,dio22:I/O 测试 — 由于数据比较不匹配而失败。
nanosleep02:验证 nanosleep 是否会在接收到信号后暂停并返回剩余睡眠时间 — 由于缺少微秒时钟精度而失败。
编写测试程序非常简单。测试用例以 ANSI C 和 BASH 编写,并使用 LTP 库 libltp 提供的 LTP 应用程序编程接口 (API) 来报告测试状态。提供了模板,向您展示如何使用 libltp 开发测试用例。测试用例可以使用该接口打印结果消息、跳出测试序列并报告测试状态,例如通过或失败。有关使用这些 API 的手册页在测试套件包中以及 LTP 网站上提供。有关 LTP 的深奥用途以及有关开发可以包含在 LTP 中的测试的教程,请参阅资源中的 Iyer 和 Larson 论文。
虽然不是运行测试套件所必需的,但 LTP 有许多相关的工具和项目可以促进测试自动化。其中两个项目是软件测试自动化框架 (STAF/STAX) 和开源开发实验室 (OSDL) 测试平台。
LTC 使用 STAF/STAX 来管理测试机器池。使用 STAF/STAX Web 界面,您可以查找和配置测试机器,然后运行和监控任何一组测试程序并返回结果。STAF 是一个开源、多平台、多语言测试框架。它基于可重用服务的概念,例如进程控制、日志记录和事件处理,这些服务可以自动化测试活动。STAF 的核心是一个消息路由守护程序,它维护本地和远程服务网络,并将请求路由到这些服务。通过在专用网络主机上运行 STAF 客户端来构建 STAF 支持的机器网络。STAX 是一个基于 STAF、XML 和 Python 构建的基于 GUI 的执行引擎。它为测试人员提供了一个界面,用于分发、执行和处理测试结果。
OSDL 可扩展测试平台为开发人员提供了一个框架,用于通过基于 Web 的界面针对特定内核和内核补丁执行测试。LTP 是 OSDL 执行的测试之一。使用 Web 界面,您还可以搜索历史测试结果。LTP 网站上有关于此框架的详细信息。
软件测试中经常不言而喻的假设是,测试用例涵盖了编写的大部分软件源代码。如果运行测试执行了代码行,则测试涵盖了该代码行。覆盖率分析衡量在测试期间运行了多少目标代码,并且是评估 LTP 测试套件有效性的有用机制。给定两个成功运行的测试用例,代码覆盖率较高的测试在某种程度上更能保证代码没有错误。当然,未经测试的代码中仍然可能存在错误,即使 100% 的覆盖率也不能保证代码没有错误。
Cornett 的论文很好地介绍了多种类型的覆盖率。我们基于 GCC 编译器的 LTP 覆盖率提供了语句和分支覆盖率。如前所述,语句覆盖率报告执行了哪些源代码行。分支条件覆盖率报告测试并采用了控制语句(例如“if”或“while”)中的哪些布尔条件。在下面的代码中,分支条件覆盖率将告诉我们何时采用分支、执行了 statement1 以及何时由于 condition1 或 condition2 为真而执行。
if ( condition1 || condition2 ) statement1; else statement2;
GCC 覆盖率的工作原理是将选项 -fprofile-arcs -ftest-coverage 传递给编译器,并将处理覆盖率数据的 GCOV 程序传递给编译器。GCOV 生成一个源代码文件,其中注释了每行代码和分支条件的执行次数。GCC 覆盖率最初旨在用于用户空间程序,并且需要针对内核进行调整,因为覆盖率数据仅在程序终止时生成,而内核永远不会终止。
此外,由于内核未与标准 C 库链接,因此内核中不存在许多 GCOV 结构。LTP 发布了一个 GCOV-kernel 补丁到 Linux 内核,以解决这些问题,并允许开发人员使用现有的 GCOV 工具从正在运行的内核收集覆盖率数据。安装说明以及补丁提供的功能的详细描述可以在 LTP 网站以及 Paul Larson 等人在渥太华 Linux 研讨会上发表的论文中找到。
GCOV-kernel 补丁在 LTP 网站上作为单独的软件包发布,但它包含在 LTP 开发树中。除了内核代码更改外,安装后,补丁会配置 Makefile,以便在编译内核时将覆盖率选项传递给 GCC。覆盖率选项指示编译器生成代码和数据结构,以捕获用于确定执行了哪些内核代码行的信息。用户空间工具 GCOV 结合了源文件和运行启用 GCOV 的程序(在我们的例子中是内核)生成的文件,以生成一个新的源代码文件,其中包含每行 C 代码的计数,表示该行执行的次数。由于 GCOV 所需的程序输出直到程序结束时才创建,并且内核不会终止,因此该补丁还创建了一个 /proc/gcov/... 树,GCOV 可以随时使用该树从内核获取计数器数据。
为了进一步促进覆盖率分析,LTP 开发了一个实用程序 LCOV,以创建更有用的图形 GCOV 输出。LCOV 可以从 GCOV-kernel 网站下载。LCOV 自动化了从内核提取覆盖率数据、运行 GCOV 和生成 HTML 的过程。应用并编译 GCOV-kernel 补丁后,可以按如下方式使用覆盖率系统。
首先,加载 gcov-proc 内核模块
insmod gcov-proc.o
清除 GCOV 计数器
lcov --reset
接下来运行 LTP 测试套件或您最喜欢的测试程序,然后捕获 GCOV 数据
lcov -c -o coverage.info
创建 HTML 覆盖率树
genhtml coverage.info
genhtml 是 LCOV 工具之一,它在目录和文件级别生成 HTML 输出,如图 1 和图 2 所示。图 1 是 Linux 内核源子目录的部分屏幕截图。在此示例中,该目录的代码总覆盖率为 47.3%。每行显示一个文件名。使用彩色编码的仪表来表示文件的覆盖率:绿色表示覆盖率大于或等于 50% 的文件,黄色表示覆盖率在 10% 到 50% 之间的文件,红色表示覆盖率小于 10% 的文件。最后两列显示文件的百分比覆盖率以及执行的行数与检测的总行数。这两个图都显示在彩色编码的背景上。图 2 是 printk.c 文件的部分视图。这是原始 GCOV 输出的图形视图。使用类似的颜色编码,以便您可以快速识别未充分利用的代码。目前,LCOV 输出仅显示语句覆盖率,而不显示分支覆盖率。

图 1. genhtml 从 GCOV 数据生成代码覆盖率报告。
随着 Linux 在企业计算领域中扮演着越来越重要的角色,鲁棒性和可靠性要求导致了更正式的测试方法。LTP 是一个功能回归测试套件,用于帮助提高 Linux 的可靠性。对于任何内核开发项目,运行 LTP 测试套件都可以为您提供一种方法,以帮助确保您的更改不会破坏内核。在测试内核修改时,启用 GCOV 的内核和随附的 LTP 工具将帮助您可视化测试的有效性,并帮助测试团队专注于覆盖率较低的区域。
除了显示内核回归和代码覆盖率差距的测试结果外,LTP 和覆盖率分析还可能提供一种衡量内核随时间推移改进的方法。考虑一个简单的论点:结合更高的内核代码覆盖率,更少的内核故障表明 Linux 内核的可靠性正在提高。关于 LTP 如何跟踪 Linux 改进的研究是我们未来工作的一部分。
最后,我们鼓励开发人员提交他们的测试以包含在 LTP 套件中。与往常一样,欢迎提出建议和意见,应将其发送到 LTP 网站上的邮件列表。
本文的资源: /article/7809。
Nigel Hinds 是 IBM T. J. Watson 研究中心的技术人员。他为 Linux 测试项目开发测试工具并维护内核覆盖率系统。他的其他兴趣包括网络和分布式系统。可以通过 nhinds@us.ibm.com 与他联系。