一个Kbuild工程生成多个ko文件及其在驱动单元测试上的应用

背景

Linux驱动是基于Kbuild框架开发的,一般情况下只会生成一个ko文件,如果想添加单元测试(Unit Test即UT),用户要么在模块入口函数的末尾添加UT代码,要么额外创建一个单独的UT工程,前者把测试代码跟驱动代码放置于同一个文件比较混乱,后者创建额外的工程维护比较麻烦。

能否既避免混乱,又避免麻烦呢?可以的。

思路

Kbuild支持生成多个ko,只需要给Makefile变量obj-m追加一个模块名即可。

MODULE_NAME := your_drv
MODULE_UT_NAME := your_drv_ut

SRCS := your_drv_part1.c your_drv_part2.c
SRCS_UT := your_drv_ut_suite1.c

$(MODULE_NAME)-objs := $(SRCS:%.c=%.o)
$(MODULE_UT_NAME)-objs := $(SRCS_UT:%.c=%.o)

obj-m := $(MODULE_NAME).o
obj-m += $(MODULE_UT_NAME).o

clean-objs := $(SRCS:%.c=%.o)
clean-objs += $(join $(dir $(SRCS)), $(patsubst %.c, .%.o.cmd, $(notdir $(SRCS))))
clean-objs += $(SRCS_UT:%.c=%.o)
clean-objs += $(join $(dir $(SRCS_UT)), $(patsubst %.c, .%.o.cmd, $(notdir $(SRCS_UT))))

注意:

  1. 模块追加是通过+=操作符实现的
  2. 模块的.c源文件不要跟模块名重复,否则链接程序ld会报no input files,原因是同名导致的循环依赖被make程序检测到,从而丢弃该源文件。

UT实践

UT框架选择

如果你是5.5以上版本的内核,则建议使用内核自带的kunit框架,不用引入额外依赖,功能也基本够用。

kunit用法示例

#include 

void your_drv_test_basic(struct kunit *test)
{
	int reg_value = 0;
	
	// ...
	KUNIT_EXPECT_EQ(test, reg_value, 42);
}

AX_S32 your_drv_test_init(struct kunit *test)
{
	// 一些资源分配
}

AX_S32 your_drv_test_exit(struct kunit *test)
{
	// 一些资源回收
}

static struct kunit_case your_drv_test_cases[] = {
				KUNIT_CASE(your_drv_test_basic),
                {}
};

struct kunit_suite your_drv_test_suite1 = {
    .name = "your_drv kunit",
    .init = your_drv_test_init,
    .exit = your_drv_test_exit,
    .test_cases = your_drv_test_cases,
};
kunit_test_suites(&your_drv_test_suite1);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("your name");
MODULE_DESCRIPTION("your_drv unit test");

kunit运行示例

# 先加载kunit和你的驱动ko
insmod kunit.ko
insmod you_drv.ko
# 再加载ut的ko,ut代码会在模块加载时执行
insmod you_drv_ut.ko

一些补充

  1. Kbuild是基于Makefile的,所以掌握Makefile的语法对定制Kbuild工程至关重要。
  2. 如果要添加多个test suite,则需要定义多个kunit_suite结构体(必要的话将每个suite拆分到单独的.c文件,这样的话记得给UT模块添加多个.c文件),并将其一并传递给kunit_test_suites函数(该函数支持可变数目的参数)

你可能感兴趣的:(驱动开发,单元测试,linux,kunit,kbuild)