KUnit:设备模拟重定向

news/2024/11/15 11:54:03/文章来源:https://www.cnblogs.com/alanli07/p/18399767

设备模拟

有些驱动文件是需要device的,所以KUnit提供了一些设备模拟的方法,并且还提供了总线来管理设备的生命周期。
下面先以clock device模拟举例(drivers/clk/clk_test.c)

  1. 首先用一个struct来模拟这个clk设备。其中clk_hw是clk的描述,rate相当于模拟设备的波特率寄存器
struct clk_dummy_context {struct clk_hw hw;unsigned long rate;
};
  1. 并且注册了一些用于这个fake设备的接口函数
static const struct clk_ops clk_dummy_rate_ops = {.recalc_rate = clk_dummy_recalc_rate,.determine_rate = clk_dummy_determine_rate,.set_rate = clk_dummy_set_rate,
};static unsigned long clk_dummy_recalc_rate(struct clk_hw *hw,unsigned long parent_rate)
{struct clk_dummy_context *ctx =container_of(hw, struct clk_dummy_context, hw);return ctx->rate;
}static int clk_dummy_determine_rate(struct clk_hw *hw,struct clk_rate_request *req)
{/* Just return the same rate without modifying it */return 0;
}static int clk_dummy_set_rate(struct clk_hw *hw, unsigned long rate,unsigned long parent_rate)
{struct clk_dummy_context *ctx =container_of(hw, struct clk_dummy_context, hw);ctx->rate = rate;return 0;
}

到此为止一个设备就模拟完了,后续在测试中使用 clk_get_clk 就会调用到 clk_dummy_recalc_rate。从而进行后续的判断

/** Test that the actual rate matches what is returned by clk_get_rate()*/
static void clk_test_get_rate(struct kunit *test)
{struct clk_dummy_context *ctx = test->priv;struct clk_hw *hw = &ctx->hw;struct clk *clk = clk_hw_get_clk(hw, NULL);unsigned long rate;rate = clk_get_rate(clk);KUNIT_ASSERT_GT(test, rate, 0);KUNIT_EXPECT_EQ(test, rate, ctx->rate);clk_put(clk);
}

为了更好的展示,我自己写了一个使用cache的测试用例(真正使用clk的时候不要开cache)。

static int clk_cached_test_init(struct kunit *test)
{struct clk_dummy_context *ctx;int ret;ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);if (!ctx)return -ENOMEM;test->priv = ctx;ctx->rate = DUMMY_CLOCK_INIT_RATE;
//开启cachectx->hw.init = CLK_HW_INIT_NO_PARENT("test-clk", &clk_dummy_rate_ops,CLK_SET_RATE_NO_REPARENT);ret = clk_hw_register(NULL, &ctx->hw);if (ret)return ret;return 0;
}/** Test cached rate*/
static void clk_test_cached_get_rate(struct kunit *test)
{struct clk_dummy_context *ctx = test->priv;struct clk_hw *hw = &ctx->hw;struct clk *clk = clk_hw_get_clk(hw, NULL);unsigned long rate;rate = clk_get_rate(clk);KUNIT_ASSERT_GT(test, rate, 0);KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_INIT_RATE);//这里正常得到最开始的初值/* We change the rate behind the clock framework's back */ctx->rate = DUMMY_CLOCK_RATE_1;//相当于物理设备rate寄存器的值改了rate = clk_get_rate(clk);KUNIT_ASSERT_GT(test, rate, 0);KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_INIT_RATE);//由于有cache,可以发现读取的还是初值clk_put(clk);
}static struct kunit_case clk_cached_test_cases[] = {KUNIT_CASE(clk_test_cached_get_rate),{}
};/** Test suite for a basic, uncached, rate clock, without any parent.** These tests exercise the rate API with simple scenarios*/
static struct kunit_suite clk_cached_test_suite = {.name = "clk-cached-test",.init = clk_cached_test_init,.exit = clk_test_exit,.test_cases = clk_cached_test_cases,
};

几种注册设备的方法

sound/soc/soc-card-test.c:注册fake device

sound/pci/hda/cirrus_scodec_test.c:创建假gpio。这源码值得读一读

其实还有很多用到mock device的方法,都很值得看一看。

重定向

示例sound/soc/codecs/cs-amp-lib-test.c

其实就是为了导出static函数使用,static函数只是在链接阶段看不见,并不是在kernel启动后也看不见,启动后这些函数都有自己的地址的,所以可以通过hook的方式得到这些static函数地址,然后通过 `kunit_activate_static_stub' 连接static函数和fake函数的地址。
1.

//测试文件
static void cs_amp_lib_test_cal_data_too_short_test(struct kunit *test)
{struct cs_amp_lib_test_priv *priv = test->priv;struct cirrus_amp_cal_data result_data;int ret;/* Redirect calls to get EFI data */
//这对static函数和fake函数做了关联kunit_activate_static_stub(test,cs_amp_test_hooks->get_efi_variable,cs_amp_lib_test_get_efi_variable_nohead);ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 0, &result_data);KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW);kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
}
//sound/soc/codecs/cs-amp-lib.cstatic const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = {.get_efi_variable = cs_amp_get_efi_variable,.write_cal_coeff = cs_amp_write_cal_coeff,
};
//hook导出
const struct cs_amp_test_hooks * const cs_amp_test_hooks =PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST), &cs_amp_test_hook_ptrs);
EXPORT_SYMBOL_NS_GPL(cs_amp_test_hooks, SND_SOC_CS_AMP_LIB);
//sound/soc/codecs/cs-amp-lib.c
//在被重定向的函数中插桩子
static efi_status_t cs_amp_get_efi_variable(efi_char16_t *name,efi_guid_t *guid,unsigned long *size,void *buf)
{u32 attr;
//插桩 由于这个语句才可以在Kunit框架中重定向到1中指定的地址KUNIT_STATIC_STUB_REDIRECT(cs_amp_get_efi_variable, name, guid, size, buf);
//然后本函数后续的代码就不执行了if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))return efi.get_variable(name, guid, &attr, size, buf);return EFI_NOT_FOUND;
}

这样在后续调用到 cs_amp_get_efi_variable 函数的时候就会跳转到fake函数中执行。

重定向原理

总的来说一句话,就是在static函数中插了桩,然后在Kunit的test中先连接好目标的函数地址,这样在Kunit上下文中就可以跳转过去执行.

//首先看一下 kunit_active_static_stub的实现
void __kunit_activate_static_stub(struct kunit *test,void *real_fn_addr,void *replacement_addr)
{struct kunit_static_stub_ctx *ctx;struct kunit_resource *res;KUNIT_ASSERT_PTR_NE_MSG(test, real_fn_addr, NULL,"Tried to activate a stub for function NULL");/* If the replacement address is NULL, deactivate the stub. */if (!replacement_addr) {kunit_deactivate_static_stub(test, replacement_addr);return;}/* Look up any existing stubs for this function, and replace them. */res = kunit_find_resource(test,__kunit_static_stub_resource_match,real_fn_addr);
// 将real_fn_addr 和 replacement_addr 做关联,放到上下文的resource中if (res) {ctx = res->data;ctx->replacement_addr = replacement_addr;/* We got an extra reference from find_resource(), so put it. */kunit_put_resource(res);} else {ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);ctx->real_fn_addr = real_fn_addr;ctx->replacement_addr = replacement_addr;res = kunit_alloc_resource(test, NULL,&__kunit_static_stub_resource_free,GFP_KERNEL, ctx);}
}
EXPORT_SYMBOL_GPL(__kunit_activate_static_stub);

下面再来看一下 KUNIT_STATIC_STUB_REDIRECT 这个宏,可以看到就是一个简单的获取到 replacement_addr地址然后直接跳过去。

#define KUNIT_STATIC_STUB_REDIRECT(real_fn_name, args...)		\
do {									\typeof(&real_fn_name) replacement;				\struct kunit *current_test = kunit_get_current_test();		\\if (likely(!current_test))					\break;							\\replacement = kunit_hooks.get_static_stub_address(current_test,	\&real_fn_name);	\\if (unlikely(replacement))					\return replacement(args);				\
} while (0)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/792950.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IDA 动态调试初步学习

题目 https://files.buuoj.cn/files/985826f5dda0d8665ed997a49321dd88/attachment.zip 1C这个值太小导致加密失败,所以考虑动态调试修改1C为更大的值选择调试F2下一些断点找到1C在内存中的位置F9开始调试先F7单步,观察右下角的Stack view,内存中出现1C先选中,然后按F2,然后修改…

JavaScript 中 structuredClone 和 JSON.parse(JSON.stringify()) 克隆对象的区别

JavaScript 中 structuredClone 和 JSON.parse(JSON.stringify()) 克隆对象的区别JavaScript 中 structuredClone 和 JSON.parse(JSON.stringify()) 克隆对象的异同点 一、什么是 structuredClone? 1. structuredClone 的发展 structuredClone 是在 ECMAScript 2021(ES12)标…

【c】printf()中%占位符的选取和使用: %d, %s等

格式占位符速通 格式占位符 %格式占位符 % 是在 C/C++ 语言中格式输入函数,如 scanf、printf 等函数中使用。 其意义就是起到格式占位的意思,表示在该位置有输入或者输出。规定符%d 十进制有符号整数 %u 十进制无符号整数 %f 浮点数 %s 字符串 %c 单个字符 %p 指针的值 %e 指…

kingbase——创建truncate函数

写上一篇比较round函数与truncate函数时,顺手试了一下KINGBASE数据库,应该是没有TRUNCATE这个函数,要使用估计得手动创建。创建函数如下 create or replace function sys.truncate(numeric,int4) returns numeric as declarevalue numeric;result numeric; beginvalue := l…

LeetCode Hot100刷题记录-21. 合并两个有序链表

题目描述:将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。需要知道的pre-knowledge: list1和list2起初可直接代表两个链表的头节点,无需用另外的变量比如 current 来表示头节点。 思路:准备一个虚拟节点,指向合并完成新链…

Playwright 源码 BrowserType

playwright-java 的 Browser、BrowserContext、Page 挺好理解的,唯独这厮,就有一丢丢 ……playwright-java 的 Browser、BrowserContext、Page 挺好理解的,唯独这厮,就有一丢丢 ……package com.microsoft.playwright;/*** BrowserType provides methods to launch a speci…

【优技教育】Oracle 19c OCP 082题库(第14题)- 2024年修正版

【优技教育】Oracle 19c OCP 082题库(Q 14题)- 2024年修正版 考试科目:1Z0-082 考试题量:90 通过分数:60% 考试时间:150min 本文为(CUUG 原创)整理并解析,转发请注明出处,禁止抄袭及未经注明出处的转载。 原文地址:http://www.cuug.com.cn/ocp/082kaoshitiku/3818373495…

安装了跑神经网络的环境,所遇到的问题及解惑1

cuda:12.2 cudnn:8.9.7 tensorflow库:2.17.0(python310_test) {9:37}/home/code/python ➭ python mnist_test.py 2024-09-06 09:39:29.473128: E external/local_xla/xla/stream_executor/cuda/cuda_dnn.cc:9261] Unable to register cuDNN factory: Attempting to registe…

记一次阿里云搭建K8S在恢复镜像快照之后etcd一个节点无法启动问题

环境查看 系统环境# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) # uname -a Linux CentOS7K8SMaster01005101 3.10.0-1160.114.2.el7.x86_64 #1 SMP Wed Mar 20 15:54:52 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux软件环境 # kubectl version Client Vers…

无风扇嵌入式工控机的技术优势

无风扇嵌入式工控机专为优先考虑低维护和可靠性的应用而设计。这些工控机是不间断性能至关重要的行业的完美选择,因为它们消除了与风扇相关的故障风险。通过消除对风扇的需求,无风扇工控机提供了无缝且可靠的性能,不会受到潜在风扇故障或故障的影响。此外,无风扇嵌入式工控…

哎呀,当时怎么没有想到

在我们的测试工作中,是不是经常遇到这样的情形,发生了线上问题,产品、研发或者测试同学一拍脑袋:当时怎么没有想到,怎么给漏掉了呢?明明是一个非常简单的事情,用大拇指都能想到的验证场景,为何当时就漏测了呢?但实际情况是,逃逸到线上的缺陷,疑难杂症式的极端异常的…

Optuna发布 4.0 重大更新:多目标TPESampler自动化超参数优化速度提升显著

Optuna这个备受欢迎的超参数优化框架在近期发布了其第四个主要版本。自2018年首次亮相以来,Optuna不断发展,现已成为机器学习领域的重要工具。其用户社区持续壮大,目前已达到以下里程碑:10,000+ GitHub星标 每月300万+ 下载量 16,000+ 代码库使用 5,000+ 论文引用 18,000+ …