Kernel Exploring
  • 前言
  • 支持
  • 老司机带你探索内核编译系统
    • 编译出你的第一个内核
    • 内核编译中的小目标
    • 可能是kbuild中最直接的小目标 – help
    • 使用了一个kbuild函数的目标 – cscope
    • 内核中单个.o文件的编译过程
    • 根目录vmlinux的编译过程
    • 启动镜像bzImage的前世今生
    • setup.bin的诞生记
    • 真假vmlinux–由vmlinux.bin揭开的秘密
    • bzImage的全貌
    • kbuild系统浅析
  • 启动时的小秘密
    • INIT_CALLS的秘密
    • 内核参数
  • 内核加载全流程
    • bootloader如何加载bzImage
    • 内核压缩与解压
    • 内核加载的几个阶段
    • 保护模式内核代码赏析
  • 内存管理
    • 内核页表成长记
      • 未解压时的内核页表
      • 内核早期的页表
      • cleanup_highmap之后的页表
      • 映射完整物理地址
      • 启用init_level4_pgt
    • 自底而上话内存
      • e820从硬件获取内存分布
      • 原始内存分配器--memblock
      • 页分配器
        • 寻找页结构体的位置
        • 眼花的页结构体
        • Node-Zone-Page
        • 传说的伙伴系统
        • Compound Page
        • GFP的功效
        • 页分配器的用户们
      • slub分配器
        • slub的理念
        • 图解slub
      • 内存管理的不同粒度
      • 挑战和进化
        • 扩展性的设计和实现
        • 减少竞争 per_cpu_pageset
        • 海量内存
        • 延迟初始化
        • 内存热插拔
        • 连续内存分配器
    • 虚拟内存空间
      • 页表和缺页中断
      • 虚拟地址空间的管家--vma
      • 匿名反向映射的前世今生
      • 图解匿名反向映射
      • THP和mapcount之间的恩恩怨怨
      • 透明大页的玄机
      • NUMA策略
      • numa balance
      • 老版vma
    • 内存的回收再利用
      • 水线
      • Big Picture
      • 手动触发回收
      • Page Fram Reclaim Algorithm
      • swapfile原理使用和演进
    • 内存隔离
      • memcg初始化
      • 限制memcg大小
      • 对memcg记账
    • 通用
      • 常用全局变量
      • 常用转换
    • 测试
      • 功能测试
      • 性能测试
  • 中断和异常
    • 从IDT开始
    • 中断?异常?有什么区别
    • 系统调用的实现
    • 异常向量表的设置
    • 中断向量和中断函数
    • APIC
    • 时钟中断
    • 软中断
    • 中断、软中断、抢占和多处理器
  • 设备模型
    • 总线
    • 驱动
    • 设备
    • 绑定
  • nvdimm初探
    • 使用手册
    • 上帝视角
    • nvdimm_bus
    • nvdimm
    • nd_region
    • nd_namespace_X
    • nd_dax
      • dev_dax
  • KVM
    • 内存虚拟化
      • Qemu内存模型
      • KVM内存管理
  • cgroup
    • 使用cgroup控制进程cpu和内存
    • cgroup文件系统
    • cgroup层次结构
    • cgroup和进程的关联
    • cgroup数据统计
  • 同步机制
    • 内存屏障
    • RCU
  • Trace/Profie/Debug
    • ftrace的使用
    • 探秘ftrace
    • 内核热补丁的黑科技
    • eBPF初探
    • TraceEvent
    • Drgn
  • 内核中的数据结构
    • 双链表
    • 优先级队列
    • 哈希表
    • xarray
    • B树
    • Maple Tree
    • Interval Tree
  • Tools
    • 发补丁
    • 检查文件变化
    • selftest
      • 构建过程
      • 编写测试
  • Good To Read
    • 内核自带文档
    • 内存相关
    • 下载社区邮件
Powered by GitBook
On this page
  • 定义测试用例的两种方式
  • TEST()
  • TEST_F()

Was this helpful?

  1. Tools
  2. selftest

编写测试

这里我们来看下如何编写selftest。还是从简单的例子开始,在目录kselftest_harness中的代码就是很好的开始。

这个例子使用了新版本的测试框架,引用的是kselftest_harness.h头文件。老版本的引用的是kselftest.h。

从kselftest_harness.h文件的文档中,我们可以看到使用方法。结构比较清晰:

  • TEST/TEST_F来定义测试用例,写一个就表示一个用例

  • TEST定义的是比较简单的,没有额外的数据

  • TEST_F定义的相对复杂,可以用FIXTURE定义一个数据结构,并且在测试正式运行前后,通过FIXTURE_SETUP/FIXTURE_TEARDOWN来准备和销毁测试数据。

  • 用ASSERT_EQ等宏来判断测试是否成功

  • 用SKIP来上报跳过测试,如系统配置不符合等情况

定义测试用例的两种方式

例子中我们可以看到有两种定义测试用例的方式,TEST()简洁一些,TEST_F()有更多的定制空间。

TEST()

#define TEST(test_name) __TEST_IMPL(test_name, -1)

#define __TEST_IMPL(test_name, _signal) \
	static void test_name(struct __test_metadata *_metadata); \
	static void wrapper_##test_name( \
		struct __test_metadata *_metadata, \
		struct __fixture_variant_metadata __attribute__((unused)) *variant) \
	{ \
		test_name(_metadata); \
	} \
	static struct __test_metadata _##test_name##_object = \
		{ .name = #test_name, \
		  .fn = &wrapper_##test_name, \
		  .fixture = &_fixture_global, \
		  .termsig = _signal, \
		  .timeout = TEST_TIMEOUT_DEFAULT, }; \
	static void __attribute__((constructor)) _register_##test_name(void) \
	{ \
		__register_test(&_##test_name##_object); \
	} \
	static void test_name( \
		struct __test_metadata __attribute__((unused)) *_metadata)

这个定义还是比较简洁的:

  • 定义了一个__test_metadata结构的变量(_##test_name##_object), 这个变量中包含了定义的代码块,预期的信号量,超时设置

  • 通过构造函数_register_##test_name,将变量注册到某个链表上

TEST_F()

#define TEST_F(fixture_name, test_name) \
	__TEST_F_IMPL(fixture_name, test_name, -1, TEST_TIMEOUT_DEFAULT)


#define __TEST_F_IMPL(fixture_name, test_name, signal, tmout) \
	static void fixture_name##_##test_name( \
		struct __test_metadata *_metadata, \
		FIXTURE_DATA(fixture_name) *self, \
		const FIXTURE_VARIANT(fixture_name) *variant); \
	static void wrapper_##fixture_name##_##test_name( \
		struct __test_metadata *_metadata, \
		struct __fixture_variant_metadata *variant) \
	{ \
	...
	...
	...
	} \
	static void wrapper_##fixture_name##_##test_name##_teardown( \
		bool in_parent, struct __test_metadata *_metadata, \
		void *self, const void *variant) \
	{ \
		if (fixture_name##_teardown_parent == in_parent && \
				!__atomic_test_and_set(_metadata->no_teardown, __ATOMIC_RELAXED)) \
			fixture_name##_teardown(_metadata, self, variant); \
	} \
	static struct __test_metadata *_##fixture_name##_##test_name##_object; \
	static void __attribute__((constructor)) \
			_register_##fixture_name##_##test_name(void) \
	{ \
		struct __test_metadata *object = mmap(NULL, sizeof(*object), \
			PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); \
		object->name = #test_name; \
		object->fn = &wrapper_##fixture_name##_##test_name; \
		object->fixture = &_##fixture_name##_fixture_object; \
		object->teardown_fn = &wrapper_##fixture_name##_##test_name##_teardown; \
		object->termsig = signal; \
		object->timeout = tmout; \
		_##fixture_name##_##test_name##_object = object; \
		__register_test(object); \
	} \
	static void fixture_name##_##test_name( \
		struct __test_metadata __attribute__((unused)) *_metadata, \
		FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \
		const FIXTURE_VARIANT(fixture_name) \
			__attribute__((unused)) *variant)

相比前面一个定义,这个要长很多。为了看清楚,我先把中间一个wrapper函数的定义省略。

这么来看,主要工作和上面差不多:

  • 定义了一个__test_metadata结构的变量

  • 定义了一个构造函数,将变量注册到链表上

区别是这次的变量,是通过mmap(MAP_SHARED)分配的。PS: 其实我不太懂,全局变量不应该也是大家都能访问的吗?

接下来我们就看看,刚才省略的,被写入object->fn的定义wrapper_##fixture_name##_##test_name。

Previous构建过程NextGood To Read

Last updated 17 hours ago

Was this helpful?