深入了解KUnit:Linux内核新一代单元测试工具(上)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 深入了解KUnit:Linux内核新一代单元测试工具

KUnit(内核单元测试框架)为 Linux 内核中的单元测试提供了一个通用框架。使用 KUnit,您可以定义称为测试套件的测试用例组。这些测试要么在内核启动时运行(如果内置),要么作为模块加载。 KUnit 自动在内核日志中标记并报告失败的测试用例。

KUnit 测试是内核的一部分,用 C(编程)语言编写,并且测试内核实现的部分(例如:C 语言函数)。排除构建时间,从调用到完成,KUnit 可以在不到 10 秒的时间内运行大约 100 个测试。 KUnit 可以测试任何内核组件,例如:文件系统、系统调用、内存管理、设备驱动程序等。

KUnit 遵循白盒测试方法。该测试可以访问内部系统功能。 KUnit 在内核空间中运行,并且不限于暴露于用户空间的事物。

此外,KUnit 还有 kunit_tool,一个tools/testing/kunit/kunit.py配置 Linux 内核的脚本 ( ),在 QEMU 或 UML(用户模式 Linux)下运行 KUnit 测试,解析测试结果并以用户友好的方式显示它们。

特点

  • 提供编写单元测试的框架。
  • 在任何内核架构上运行测试。
  • 以毫秒为单位运行测试。

先决条件

  • 任何 Linux 内核兼容的硬件。
  • 对于测试的内核,Linux 内核版本 5.5 或更高版本。

一、KUnit 入门

1.1安装依赖项

KUnit 与 Linux 内核具有相同的依赖关系。只要你能构建内核,就可以运行 KUnit。

使用 kunit_tool 运行测试

kunit_tool 是一个 Python 脚本,用于配置和构建内核、运行测试并格式化测试结果。从内核存储库中,您可以运行 kunit_tool:

./tools/testing/kunit/kunit.py run
笔记——您可能会看到以下错误:“源代码树不干净,请运行 'make ARCH=um mrproper'”发生这种情况是因为 kunit.py 在内部通过 参数指定.kunit (默认选项)作为命令中的构建目录。因此,在开始树外构建之前,源树必须是干净的。make O=output/dir--build_dir管理指南的“构建内核目录”部分中也提到了相同的警告,即它的使用,它必须用于make.好消息是,它确实可以通过运行来解决 ,只是要注意这会删除当前配置和所有生成的文件。make ARCH=um mrproper

如果一切正常,您应该看到以下内容:

Configuring KUnit Kernel ...
Building KUnit Kernel ...
Starting KUnit Kernel ...

测试将通过或失败。

笔记——由于这是第一次构建大量源,因此该步骤可能需要一段时间。Building KUnit Kernel

选择要运行的测试

默认情况下,kunit_tool 运行使用最小配置即可到达的所有测试,即使用大多数 kconfig 选项的默认值。但是,您可以选择要运行的测试:

  • 自定义用于编译内核的Kconfig ,或者
  • 按名称过滤测试以专门选择要运行的已编译测试。

1.2自定义Kconfig

.kunitconfigKUnit 默认配置是一个很好的起点。如果您尚未运行,可以通过运行以下命令来生成它:kunit.pyrun

cd $PATH_TO_LINUX_REPO
tools/testing/kunit/kunit.py config
cat .kunit/.kunitconfig
笔记 .kunitconfig存在于 --build_dirkunit.py 使用的 .kunit默认情况下。

在运行测试之前,kunit_tool 确保.kunitconfig在 kernel 中设置所有配置选项.config。如果您没有包含所使用选项的依赖项,它会警告您。

有多种方法可以自定义配置:

编辑.kunit/.kunitconfig。该文件应包含运行所需测试所需的 kconfig 选项列表,包括它们的依赖项。您可能需要从其中删除 CONFIG_KUNIT_ALL_TESTS,.kunitconfig因为它将启用许多您可能不想要的其他测试。如果您需要在 UML 以外的体系结构上运行。

启用额外的 kconfig 选项.kunit/.kunitconfig。例如,要包含内核的链表测试,您可以运行:

./tools/testing/kunit/kunit.py run \
        --kconfig_add CONFIG_LIST_KUNIT_TEST=y

提供树中一个或多个 .kunitconfig 文件的路径。例如,要仅运行FAT_FSEXT4测试,您可以运行:

./tools/testing/kunit/kunit.py run \
        --kunitconfig ./fs/fat/.kunitconfig \
        --kunitconfig ./fs/ext4/.kunitconfig

如果更改.kunitconfig,kunit.py 将触发文件的重建.config。但您可以.config直接编辑文件或使用.只要它是 的超集,kunit.py 就不会覆盖您的更改。makemenuconfigO=.kunit.kunitconfig

笔记——找到满意的配置后保存 .kunitconfig:make savedefconfig O=.kunit cp .kunit/defconfig .kunit/.kunitconfig

按名称过滤测试

如果您想要比 Kconfig 提供的更具体,还可以通过传递 glob 过滤器来选择在引导时执行哪些测试(阅读联机帮助页glob(7)中有关模式的说明)。如果过滤器中有"."(句点),它将被解释为测试套件名称和测试用例之间的分隔符,否则,它将被解释为测试套件名称。例如,假设我们使用默认配置:

告知测试套件的名称,例如"kunit_executor_test",以运行它包含的每个测试用例:

./tools/testing/kunit/kunit.py run "kunit_executor_test"

通知以其测试套件为前缀的测试用例的名称,例如"example.example_simple_test",以专门运行该测试用例:

./tools/testing/kunit/kunit.py run "example.example_simple_test"

使用通配符 ( *?[) 运行与该模式匹配的任何测试用例,就像运行任何测试套件中名称中"*.*64*"包含的测试用例一样:"64"

./tools/testing/kunit/kunit.py run "*.*64*"

在没有 KUnit 包装器的情况下运行测试

如果您不想使用 KUnit Wrapper(例如:您希望测试中的代码与其他系统集成,或使用不同/不支持的架构或配置),KUnit 可以包含在任何内核中,并读出结果并手动解析。

笔记—— CONFIG_KUNIT不应在生产环境中启用。启用 KUnit 会禁用内核地址空间布局随机化 (KASLR),测试可能会以不适合生产的方式影响内核状态。

1.3配置内核

要启用 KUnit 本身,您需要启用CONFIG_KUNITKconfig 选项(在 内核黑客/内核测试和覆盖 下 menuconfig)。从那里,您可以启用任何 KUnit 测试。它们通常具有以_KUNIT_TEST.

KUnit 和 KUnit 测试可以编译为模块。模块中的测试将在模块加载时运行。

运行测试(没有 KUnit 包装器)

构建并运行您的内核。在内核日志中,测试输出以TAP格式打印出来。仅当 KUnit/tests 是内置的时,默认情况下才会发生这种情况。否则需要加载该模块。

笔记一些线路和/或数据可能散布在 TAP 输出中

编写你的第一个测试

在您的内核存储库中,我们添加一些可以测试的代码。

  1. 创建一个文件drivers/misc/example.h,其中包括:
int misc_example_add(int left, int right);

2.创建一个文件drivers/misc/example.c,其中包括:

#include "example.h"
int misc_example_add(int left, int right)
{
        return left + right;
}

3.将以下行添加到drivers/misc/Kconfig:

config MISC_EXAMPLE
        bool "My example"

4.将以下行添加到drivers/misc/Makefile:

obj-$(CONFIG_MISC_EXAMPLE) += example.o

现在我们准备编写测试用例:

  1. 添加以下测试用例drivers/misc/example_test.c
#include "example.h"
/* Define the test cases. */
static void misc_example_add_test_basic(struct kunit *test)
{
        KUNIT_EXPECT_EQ(test, 1, misc_example_add(1, 0));
        KUNIT_EXPECT_EQ(test, 2, misc_example_add(1, 1));
        KUNIT_EXPECT_EQ(test, 0, misc_example_add(-1, 1));
        KUNIT_EXPECT_EQ(test, INT_MAX, misc_example_add(0, INT_MAX));
        KUNIT_EXPECT_EQ(test, -1, misc_example_add(INT_MAX, INT_MIN));
}
static void misc_example_test_failure(struct kunit *test)
{
        KUNIT_FAIL(test, "This test never passes.");
}
static struct kunit_case misc_example_test_cases[] = {
        KUNIT_CASE(misc_example_add_test_basic),
        KUNIT_CASE(misc_example_test_failure),
        {}
};
static struct kunit_suite misc_example_test_suite = {
        .name = "misc-example",
        .test_cases = misc_example_test_cases,
};
kunit_test_suite(misc_example_test_suite);
MODULE_LICENSE("GPL");

2.将以下行添加到drivers/misc/Kconfig:

config MISC_EXAMPLE_TEST
        tristate "Test for my example" if !KUNIT_ALL_TESTS
        depends on MISC_EXAMPLE && KUNIT
        default KUNIT_ALL_TESTS

注意:如果您的测试不支持构建为可加载模块(不鼓励这样做),请用 bool 替换 tristate,并依赖 KUNIT=y 而不是 KUNIT。

3.将以下行添加到drivers/misc/Makefile:

obj-$(CONFIG_MISC_EXAMPLE_TEST) += example_test.o

4.将以下行添加到.kunit/.kunitconfig

CONFIG_MISC_EXAMPLE=y
CONFIG_MISC_EXAMPLE_TEST=y

5.运行测试:

./tools/testing/kunit/kunit.py run

您应该看到以下失败:

...
[16:08:57] [PASSED] misc-example:misc_example_add_test_basic
[16:08:57] [FAILED] misc-example:misc_example_test_failure
[16:08:57] EXPECTATION FAILED at drivers/misc/example-test.c:17
[16:08:57]      This test never passes.
...

恭喜!您刚刚编写了第一个 KUnit 测试。


【文章福利】小编推荐自己的Linux内核技术交流群:【 865977150】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!

二、KUnit架构

KUnit架构分为两部分:

  • 内核内测试框架
  • kunit_tool(命令行测试工具)

2.1内核内测试框架

内核测试库支持使用 KUnit 以 C 语言编写的 KUnit 测试。这些 KUnit 测试是内核代码。 KUnit 执行以下任务:

  • 组织测试
  • 报告测试结果
  • 提供测试实用程序

测试用例

测试用例是KUnit 中的基本单元。 KUnit 测试用例被组织成套件。 KUnit 测试用例是具有类型签名的函数 。这些测试用例函数包装在一个名为 的结构中。void (*)(struct kunit *test)struct kunit_case

每个 KUnit 测试用例都会接收一个跟踪正在运行的测试的上下文对象。 KUnit 断言宏和其他 KUnit 实用程序使用 上下文对象。作为例外,有两个字段:struct kunitstruct kunit

  • ->priv:设置功能可以使用它来存储任意测试用户数据。
  • ->param_value:它包含可以在参数化测试中检索的参数值。

测试套件

KUnit 套件包括一组测试用例。 KUnit 套件由.例如:structkunit_suite

static struct kunit_case example_test_cases[] = {
        KUNIT_CASE(example_test_foo),
        KUNIT_CASE(example_test_bar),
        KUNIT_CASE(example_test_baz),
        {}
};
static struct kunit_suite example_test_suite = {
        .name = "example",
        .init = example_test_init,
        .exit = example_test_exit,
        .test_cases = example_test_cases,
};
kunit_test_suite(example_test_suite);

在上面的示例中,测试套件example_test_suite运行测试用例example_test_foo、example_test_bar和 example_test_baz。在运行测试之前,example_test_init 调用 ,在运行测试之后example_test_exit调用 。将kunit_test_suite(example_test_suite)测试套件注册到 KUnit 测试框架。

执行者

KUnit 执行器可以在启动时列出并运行内置 KUnit 测试。测试套件存储在名为 的链接器部中.kunit_test_suites。链接器部分由指向 的指针数组组成 ,并由宏填充 。 KUnit 执行器迭代链接器节数组,以便运行编译到内核中的所有测试。struct kunit_suitekunit_test_suites()。

KUnit套件内存图

在内核启动时,KUnit 执行器使用此部分的开始和结束地址来迭代并运行所有测试。当构建为模块时,kunit_test_suites()宏定义一个module_init()函数,该函数在编译单元中运行所有测试,而不是利用执行器。

在 KUnit 测试中,某些错误类不会影响其他测试或内核部分,每个 KUnit 案例都在单独的线程上下文中执行。

断言宏

KUnit 测试使用期望/断言来验证状态。所有期望/断言的格式如下:KUNIT_{EXPECT|ASSERT}_<op>[_MSG](kunit,property[,message])

{EXPECT|ASSERT}确定检查是断言还是期望。如果发生故障,测试流程会有所不同,如下所示:

  • 为了满足预期,测试被标记为失败并记录失败。
  • 另一方面,失败的断言会导致测试用例立即终止。
断言调用函数: 。void __noreturn __kunit_abort(struct kunit *)
__kunit_abort调用函数: .void __noreturn kunit_try_catch_throw(struct kunit_try_catch *try_catch)
kunit_try_catch_throw调用函数: 并终止特殊线程上下文。void kthread_complete_and_exit(struct completion *, long) __noreturn;
  • <op>表示对选项进行检查:(TRUE提供的属性具有布尔值“true”),EQ(提供的两个属性相等),NOT_ERR_OR_NULL(提供的指针不为空且不包含“err”值)。
  • [_MSG]失败时打印自定义消息。

测试结果报告

KUnit 以 KTAP 格式打印测试结果。 KTAP 与 KUnit 和 Kselftest 配合使用。 KUnit 执行器将 KTAP 结果打印到 dmesg 和 debugfs(如果已配置)。

参数化测试

每个 KUnit 参数化测试都与一组参数相关联。该测试被多次调用,每个参数值调用一次,并且参数存储在字段中param_value。测试用例包含一个KUNIT_CASE_PARAM()接受生成器函数的宏。生成器函数传递前一个参数并返回下一个参数。它还包括一个用于生成基于数组的常见情况生成器的宏。

2.2kunit_tool(命令行测试工具)

kunit_tool是一个Python脚本,可以在tools/testing/kunit/kunit.py.它用于配置、构建、执行、解析测试结果并以正确的顺序运行所有先前的命令(即配置、构建、执行和解析)。您有两种运行 KUnit 测试的选项:在启用 KUnit 的情况下构建内核并手动解析结果。

configure.config命令从 文件(以及任何特定于体系结构的选项)生成内核.kunitconfig。qemu_configs文件夹(例如)中提供的 Python 脚本包含针对特定体系结构的其他配置选项。它解析现有文件和文件以确保它是.如果没有,它将结合两者并运行以重新生成文件。然后它检查是否已成为超集。这将验证文件中是否正确指定了所有 Kconfig 依赖项。该脚本包含解析 Kconfigs 的代码。运行的代码是脚本的一部分 。

您可以通过以下方式调用该命令:

tools/testing/kunit/qemu configs/powerpc.py.config.kunitconfig.config.kunitconfigmake 
olddefconfig.config.config.kunitconfigkunit_config.pymake 
olddefconfigkunit_kernel.py./tools/testing/kunit/kunit.py config并生成一个.config文件。

buildmake使用所需选项(取决于体系结构和某些选项,例如:build_dir)在内核树上运行并报告任何错误。要从当前的 构建 KUnit 内核.config,您可以使用 build参数:。./tools/testing/kunit/kunit.py build

exec命令直接(使用用户模式 Linux 配置)或通过 QEMU 等模拟器执行内核结果。它使用标准输出 (stdout) 从日志中读取结果,并将其传递给parse进行解析。如果您已经构建了带有内置 KUnit 测试的内核,则可以使用参数运行该内核并显示测试结果exec :。./tools/testing/kunit/kunit.py exec

parse从内核日志中提取 KTAP 输出,解析测试结果并打印摘要。对于失败的测试,将包括任何诊断输出。

1)使用 kunit_tool 运行测试

我们可以使用 kunit_tool 运行 KUnit 测试,也可以手动运行测试,然后使用 kunit_tool 解析结果。要手动运行测试,请参阅:不使用 kunit_tool 运行测试。只要我们能够构建内核,我们就可以运行 KUnit。

kunit_tool 是一个 Python 脚本,用于配置和构建内核、运行测试并格式化测试结果。

运行命令:

./tools/testing/kunit/kunit.py run

我们应该看到以下内容:

Configuring KUnit Kernel ...
Building KUnit kernel...
Starting KUnit kernel...

我们可能想要使用以下选项:

./tools/testing/kunit/kunit.py run --timeout=30 --jobs=`nproc --all`
  • --timeout设置测试运行的最长时间。
  • --jobs设置构建内核的线程数。

如果不存在其他文件(在构建目录中) , kunit_tool 将生成.kunitconfig具有默认配置的文件。.kunitconfig此外,它还验证生成的.config文件是否CONFIG包含 .kunitconfig.也可以将单独的.kunitconfig片段传递给 kunit_tool。如果我们有多个不同的测试组想要独立运行,或者如果我们想要对某些子系统使用预定义的测试配置,这非常有用。

要使用不同的.kunitconfig文件(例如为测试特定子系统而提供的文件),请将其作为选项传递:

./tools/testing/kunit/kunit.py run --kunitconfig=fs/ext4/.kunitconfig

要查看 kunit_tool 标志(可选命令行参数),请运行:

./tools/testing/kunit/kunit.py run --help

创建.kunitconfig文件

如果我们想运行一组特定的测试(而不是 KUnit 中列出的测试defconfig),我们可以在 .kunitconfig文件中提供 Kconfig 选项。

A.kunitconfig是 a minconfig(通过运行生成的 .config ),用于运行一组特定的测试。该文件包含具有特定测试目标的常规内核配置。它还包含测试所需的任何其他配置选项(例如:测试中功能的依赖项、启用/禁用某些代码块的配置、架构配置等)。

make savedefconfig.kunitconfig要创建.kunitconfig,使用 KUnit defconfig:

cd $PATH_TO_LINUX_REPO
cp tools/testing/kunit/configs/default.config .kunit/.kunitconfig

然后我们可以添加任何其他 Kconfig 选项。例如:

CONFIG_LIST_KUNIT_TEST=y

kunit_tool 确保在运行测试之前.kunitconfig在内核中设置所有配置选项。.config如果我们没有包含选项依赖项,它会发出警告。

笔记从中删除某些内容 .kunitconfig不会重建.仅当不是 的子集时才会更新配置。这意味着我们可以使用其他工具(例如:)来调整其他配置选项。需要设置构建目录才能工作,因此默认情况下使用. .config file.kunitconfig.configmake menuconfigmake menuconfigmake O=.kunit menuconfig

配置、构建和运行测试

如果我们想手动更改 KUnit 构建过程,我们可以独立运行部分 KUnit 构建过程。当运行 kunit_tool 时,.kunitconfig我们可以.config使用config参数从 a 生成 a:

./tools/testing/kunit/kunit.py config

要从当前构建 KUnit 内核.config,我们可以使用以下build参数:

./tools/testing/kunit/kunit.py build

如果我们已经构建了带有内置 KUnit 测试的 UML 内核,我们可以运行该内核,并使用参数显示测试结果exec

./tools/testing/kunit/kunit.py exec

使用 kunit_tool 运行测试run部分中讨论的命令相当于按顺序运行上述三个命令。

解析测试结果

KUnit 测试输出以 TAP(测试任何协议)格式显示结果。运行测试时,kunit_tool 会解析此输出并打印摘要。要查看 TAP 格式的原始测试结果,我们可以传递参数--raw_output:

./tools/testing/kunit/kunit.py run --raw_output

parse如果我们有原始 TAP 格式的 KUnit 结果,我们可以使用kunit_tool 命令解析它们并打印人类可读的摘要。这接受参数的文件名,或者将从标准输入读取。

# Reading from a file
./tools/testing/kunit/kunit.py parse /var/log/dmesg
# Reading from stdin
dmesg | ./tools/testing/kunit/kunit.py parse

过滤测试

通过将 bash 样式的全局过滤器传递给execorrun命令,我们可以运行内核中内置的测试子集。例如:如果我们只想运行 KUnit 资源测试,请使用:

./tools/testing/kunit/kunit.py run 'kunit-resource*'

这使用带有通配符的标准 glob 格式。

在QEMU上运行测试

kunit_tool 支持在 qemu 上以及通过 UML 运行测试。要在 qemu 上运行测试,默认情况下需要两个标志:

  • --arch:选择一个配置集合(Kconfig、qemu 配置选项等),允许 KUnit 测试以最小的方式在指定的架构上运行。架构参数与传递给ARCHKbuild 使用的变量的选项名称相同。当前并非所有架构都支持此标志,但我们可以用来 --qemu_config处理它。如果um通过(或者忽略该标志),测试将通过 UML 运行。非UML架构,例如:i386、x86_64、arm等;在 qemu 上运行。
  • --cross_compile:指定 Kbuild 工具链。它传递的参数与传递给CROSS_COMPILEKbuild 使用的变量的参数相同。提醒一下,这将是 GCC 等工具链二进制文件的前缀。例如:
  • sparc64-linux-gnu如果我们的系统上安装了 sparc 工具链。
  • $HOME/toolchains/microblaze/gcc-9.2.0-nolibc/microblaze-linux/bin/microblaze-linux 如果我们已经从 0day 网站下载了 microblaze 工具链到我们的主目录中名为 toolchains 的目录中。

这意味着对于大多数架构来说,在 qemu 下运行非常简单:

./tools/testing/kunit/kunit.py run --arch=x86_64

交叉编译时,我们可能需要指定不同的工具链,例如:

./tools/testing/kunit/kunit.py run \
        --arch=s390 \
        --cross_compile=s390x-linux-gnu-

如果我们想要在该--arch标志不支持的架构上运行 KUnit 测试,或者想要使用非默认配置在 qemu 上运行 KUnit 测试;然后我们可以编写自己的``QemuConfig``。这些QemuConfigs是用 Python 编写的。它们在文件顶部有一个导入行 。该文件必须包含一个名为 的变量,该变量已为其分配了一个实例。请参阅以下示例:

from..qemu_config import QemuArchParamsQEMU_ARCHQemuArchParamstools/testing/kunit/qemu_configs/x86_64.py

一旦我们有了一个QemuConfig,我们就可以使用该--qemu_config标志将其传递到 kunit_tool 中。使用时,该标志会替换--arch标志。例如:使用tools/testing/kunit/qemu_configs/x86_64.py,调用显示为

./tools/testing/kunit/kunit.py run \
        --timeout=60 \
        --jobs=12 \
        --qemu_config=./tools/testing/kunit/qemu_configs/x86_64.py

运行命令行参数

kunit_tool 有许多其他命令行参数,它们对我们的测试环境很有用。以下是最常用的命令行参数:

--help:列出所有可用选项。要列出常用选项,请放在--help命令之前。要列出特定于该命令的选项,请放在--help该命令之后。

笔记:不同的命令( configbuildrun等)具有不同的支持选项。
  • --build_dir:指定 kunit_tool 构建目录。它包括.kunitconfig.config文件和已编译的内核。
  • --make_options:指定在编译内核时(使用buildrun命令)传递给 make 的附加选项。例如:要启用编译器警告,我们可以传递.--make_optionsW=1
  • --alltests:启用一组预定义的选项,以便构建尽可能多的测试。
笔记——已启用选项的列表可以在 中找到 tools/testing/kunit/configs/all_tests.config。如果您只想启用具有其他满足的依赖项的所有测试,请将其添加 CONFIG_KUNIT_ALL_TESTS=y到您的 .kunitconfig.
  • --kunitconfig:指定文件的路径或目录.kunitconfig 。例如:
  • lib/kunit/.kunitconfig可以是文件的路径。
  • lib/kunit可以是文件所在的目录。

该文件用于构建和运行一组预定义的测试及其依赖项。例如,为给定子系统运行测试。

  • --kconfig_add:指定要附加到.kunitconfig文件的其他配置选项。例如:
    ./tools/testing/kunit/kunit.py run --kconfig_add CONFIG_KASAN=y
  • --arch:在指定的架构上运行测试。架构参数与 Kbuild ARCH 环境变量相同。例如,i386、x86_64、arm、um 等。非 UML 架构在 qemu 上运行。默认是嗯。
  • --cross_compile:指定 Kbuild 工具链。它传递的参数与传递给CROSS_COMPILEKbuild 使用的变量的参数相同。这将是 GCC 等工具链二进制文件的前缀。例如:
  • sparc64-linux-gnu-如果我们的系统上安装了 sparc 工具链。
  • $HOME/toolchains/microblaze/gcc-9.2.0-nolibc/microblaze-linux/bin/microblaze-linux 如果我们已经从 0day 网站下载了 microblaze 工具链到我们的主目录中名为 toolchains 的指定路径。
  • --qemu_config:指定包含自定义 qemu 架构定义的文件的路径。这应该是一个包含QemuArchParams对象的 python 文件。
  • --qemu_args:指定其他 qemu 参数,例如。-smp8
  • --jobs:指定同时运行的作业(命令)数量。默认情况下,这设置为系统上的核心数。
  • --timeout:指定允许所有测试运行的最大秒数。这不包括构建测试所需的时间。
  • --kernel_args:指定附加内核命令行参数。可能会重复。
  • --run_isolated:如果设置,则为每个单独的套件/测试启动内核。这对于调试非密封测试非常有用,该测试可能根据之前运行的内容而通过/失败。
  • --raw_output:如果设置,则从内核生成未格式化的输出。可能的选项有:
  • all:要查看完整的内核输出,请使用--raw_output=all.
  • kunit:这是默认选项并过滤到 KUnit 输出。使用--raw_output--raw_output=kunit.
  • --json:如果设置,则以 JSON 格式存储测试结果并打印到stdout或保存到文件(如果指定了文件名)。
  • --filter:指定测试属性的过滤器,例如speed!=slow。通过将输入括在引号中并用逗号分隔过滤器,可以使用多个过滤器。示例:.--filter"speed>slow,module=example"
  • --filter_action:如果设置为skip,过滤后的测试将在输出中显示为已跳过,而不是不显示输出。
  • --list_tests:如果设置,则列出将运行的所有测试。
  • --list_tests_attr:如果设置,则列出将运行的所有测试及其所有属性。

2)不使用 kunit_tool 运行测试

如果我们不想使用kunit_tool(例如:我们想与其他系统集成,或者在真实硬件上运行测试),我们可以将KUnit包含在任何内核中,读出结果并手动解析。

笔记——KUnit 并非设计用于生产系统。测试可能会降低系统的稳定性或安全性。

配置内核

KUnit 测试可以在没有 kunit_tool 的情况下运行。如果满足以下条件,这可能很有用:

  • 我们有一个现有的内核配置需要测试。
  • 需要在真实硬件上运行(或者使用模拟器/VM kunit_tool 不支持)。
  • 希望与一些现有的测试系统集成。

KUnit 使用该CONFIG_KUNIT选项进行配置,并且还可以通过在我们的.config. KUnit 测试通常(但并不总是)具有以_KUNIT_TEST.大多数测试可以构建为模块,也可以构建到内核中。

笔记——我们可以启用 KUNIT_ALL_TESTS配置选项来自动启用具有满足依赖关系的所有测试。这是快速测试适用于当前配置的所有内容的好方法。

一旦我们构建了内核(和/或模块),运行测试就很简单。如果测试是内置的,它们将在内核启动时自动运行。结果将以dmesgTAP 格式写入内核日志 ( )。

如果测试构建为模块,则它们将在加载模块时运行。

# modprobe example-test

结果将以 TAP 格式显示在dmesg

笔记——如果 CONFIG_KUNIT_DEBUGFS启用,则可以从 debugfs文件系统(如果已安装)访问 KUnit 测试结果。它们将采用 /sys/kernel/debug/kunit/<test_suite>/resultsTAP 格式。

三、Kunit编写测试

3.1测试用例

KUnit 中的基本单元是测试用例。测试用例是带有签名的函数。它调用被测试的函数,然后设置对应该发生的情况的期望。例如:void(*)(structkunit*test)

void example_test_success(struct kunit *test)
{
}
void example_test_failure(struct kunit *test)
{
        KUNIT_FAIL(test, "This test never passes.");
}

在上面的例子中,example_test_success总是通过,因为它什么也没做;没有设定任何期望,因此所有期望都会过去。另一方面,example_test_failure总是会失败,因为它调用KUNIT_FAIL,这是一种特殊的期望,会记录消息并导致测试用例失败。

期望

期望指定我们期望一段代码在测试中执行某些操作期望像函数一样被调用。测试是通过设置对被测代码的行为的期望来进行的。当一个或多个期望失败时,测试用例就会失败并记录有关失败的信息。例如:

void add_test_basic(struct kunit *test)
{
        KUNIT_EXPECT_EQ(test, 1, add(1, 0));
        KUNIT_EXPECT_EQ(test, 2, add(1, 1));
}

在上面的示例中,add_test_basic对名为 的函数的行为做出了许多断言add。第一个参数始终为 类型 ,其中包含有关当前测试上下文的信息。在本例中,第二个参数是预期值。最后一个值是实际值。如果满足所有这些期望,则测试用例将通过;如果这些期望中的任何一个失败,测试用例就会失败。struct kunit *addadd_test_basic

当任何期望被违反时,测试用例就会失败;但是,测试将继续运行,并尝试其他期望,直到测试用例结束或以其他方式终止。这与稍后讨论的断言相反。

笔记——单个测试用例应该简短、易于理解并且专注于单个行为。

例如,如果我们想严格测试add上面的函数,请创建额外的测试用例来测试函数应add具有的每个属性,如下所示:

void add_test_basic(struct kunit *test)
{
        KUNIT_EXPECT_EQ(test, 1, add(1, 0));
        KUNIT_EXPECT_EQ(test, 2, add(1, 1));
}
void add_test_negative(struct kunit *test)
{
        KUNIT_EXPECT_EQ(test, 0, add(-1, 1));
}
void add_test_max(struct kunit *test)
{
        KUNIT_EXPECT_EQ(test, INT_MAX, add(0, INT_MAX));
        KUNIT_EXPECT_EQ(test, -1, add(INT_MAX, INT_MIN));
}
void add_test_overflow(struct kunit *test)
{
        KUNIT_EXPECT_EQ(test, INT_MIN, add(INT_MAX, 1));
}

断言

断言类似于期望,只不过如果条件不满足,断言会立即终止测试用例。例如:

static void test_sort(struct kunit *test)
{
        int *a, i, r = 1;
        a = kunit_kmalloc_array(test, TEST_LEN, sizeof(*a), GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, a);
        for (i = 0; i < TEST_LEN; i++) {
                r = (r * 725861) % 6599;
                a[i] = r;
        }
        sort(a, TEST_LEN, sizeof(*a), cmpint, NULL);
        for (i = 0; i < TEST_LEN-1; i++)
                KUNIT_EXPECT_LE(test, a[i], a[i + 1]);
}

在此示例中,我们需要能够分配一个数组来测试该sort()函数。因此,如果出现分配错误,我们通常KUNIT_ASSERT_NOT_ERR_OR_NULL()会中止测试。

笔记——在其他测试框架中, ASSERT宏通常通过调用来实现 return,因此它们只能在测试函数中工作。在 KUnit 中,我们在失败时停止当前的 kthread,因此您可以从任何地方调用它们。警告:上述规则有一个例外。您不应在套件的 exit() 函数或资源的 free 函数中使用断言。它们在测试关闭时运行,并且此处的断言会阻止进一步的清理代码运行,从而可能导致内存泄漏。


相关文章
|
8天前
|
安全 Linux 虚拟化
|
1天前
|
存储 监控 安全
Linux内核调优的艺术:从基础到高级###
本文深入探讨了Linux操作系统的心脏——内核的调优方法。文章首先概述了Linux内核的基本结构与工作原理,随后详细阐述了内核调优的重要性及基本原则。通过具体的参数调整示例(如sysctl、/proc/sys目录中的设置),文章展示了如何根据实际应用场景优化系统性能,包括提升CPU利用率、内存管理效率以及I/O性能等关键方面。最后,介绍了一些高级工具和技术,如perf、eBPF和SystemTap,用于更深层次的性能分析和问题定位。本文旨在为系统管理员和高级用户提供实用的内核调优策略,以最大化Linux系统的效率和稳定性。 ###
|
1天前
|
缓存 算法 Linux
深入理解Linux内核调度器:公平性与性能的平衡####
真知灼见 本文将带你深入了解Linux操作系统的核心组件之一——完全公平调度器(CFS),通过剖析其设计原理、工作机制以及在实际系统中的应用效果,揭示它是如何在众多进程间实现资源分配的公平性与高效性的。不同于传统的摘要概述,本文旨在通过直观且富有洞察力的视角,让读者仿佛亲身体验到CFS在复杂系统环境中游刃有余地进行任务调度的过程。 ####
17 6
|
2天前
|
Linux 数据库
Linux内核中的锁机制:保障并发操作的数据一致性####
【10月更文挑战第29天】 在多线程编程中,确保数据一致性和防止竞争条件是至关重要的。本文将深入探讨Linux操作系统中实现的几种关键锁机制,包括自旋锁、互斥锁和读写锁等。通过分析这些锁的设计原理和使用场景,帮助读者理解如何在实际应用中选择合适的锁机制以优化系统性能和稳定性。 ####
15 6
|
2天前
|
机器学习/深度学习 负载均衡 算法
深入探索Linux内核调度机制的优化策略###
本文旨在为读者揭开Linux操作系统中至关重要的一环——CPU调度机制的神秘面纱。通过深入浅出地解析其工作原理,并探讨一系列创新优化策略,本文不仅增强了技术爱好者的理论知识,更为系统管理员和软件开发者提供了实用的性能调优指南,旨在促进系统的高效运行与资源利用最大化。 ###
|
5天前
|
算法 Linux 开发者
深入探究Linux内核中的内存管理机制
本文旨在对Linux操作系统的内存管理机制进行深入分析,探讨其如何通过高效的内存分配和回收策略来优化系统性能。文章将详细介绍Linux内核中内存管理的关键技术点,包括物理内存与虚拟内存的映射、页面置换算法、以及内存碎片的处理方法等。通过对这些技术点的解析,本文旨在为读者提供一个清晰的Linux内存管理框架,帮助理解其在现代计算环境中的重要性和应用。
|
2天前
|
监控 网络协议 算法
Linux内核优化:提升系统性能与稳定性的策略####
本文深入探讨了Linux操作系统内核的优化策略,旨在通过一系列技术手段和最佳实践,显著提升系统的性能、响应速度及稳定性。文章首先概述了Linux内核的核心组件及其在系统中的作用,随后详细阐述了内存管理、进程调度、文件系统优化、网络栈调整及并发控制等关键领域的优化方法。通过实际案例分析,展示了这些优化措施如何有效减少延迟、提高吞吐量,并增强系统的整体健壮性。最终,文章强调了持续监控、定期更新及合理配置对于维持Linux系统长期高效运行的重要性。 ####
|
3天前
|
缓存 网络协议 Linux
Linux操作系统内核
Linux操作系统内核 1、进程管理: 进程调度 进程创建与销毁 进程间通信 2、内存管理: 内存分配与回收 虚拟内存管理 缓存管理 3、驱动管理: 设备驱动程序接口 硬件抽象层 中断处理 4、文件和网络管理: 文件系统管理 网络协议栈 网络安全及防火墙管理
20 4
|
5天前
|
人工智能 算法 大数据
Linux内核中的调度算法演变:从O(1)到CFS的优化之旅###
本文深入探讨了Linux操作系统内核中进程调度算法的发展历程,聚焦于O(1)调度器向完全公平调度器(CFS)的转变。不同于传统摘要对研究背景、方法、结果和结论的概述,本文创新性地采用“技术演进时间线”的形式,简明扼要地勾勒出这一转变背后的关键技术里程碑,旨在为读者提供一个清晰的历史脉络,引领其深入了解Linux调度机制的革新之路。 ###
|
7天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
31 4
下一篇
无影云桌面