diff --git a/sys/modules/ktest/ktest_example/Makefile b/sys/modules/ktest/ktest_example/Makefile index b4a3e778e2ed..3ca304a19a99 100644 --- a/sys/modules/ktest/ktest_example/Makefile +++ b/sys/modules/ktest/ktest_example/Makefile @@ -1,13 +1,14 @@ # $FreeBSD$ PACKAGE= tests SYSDIR?=${SRCTOP}/sys .include "${SYSDIR}/conf/kern.opts.mk" .PATH: ${SYSDIR}/tests KMOD= ktest_example SRCS= ktest_example.c +SRCS+= opt_netlink.h .include diff --git a/sys/tests/ktest.h b/sys/tests/ktest.h index feadb800551b..c767aa31e8e5 100644 --- a/sys/tests/ktest.h +++ b/sys/tests/ktest.h @@ -1,141 +1,142 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2023 Alexander V. Chernikov * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef SYS_TESTS_KTEST_H_ #define SYS_TESTS_KTEST_H_ #ifdef _KERNEL #include #include #include #include struct nlattr; struct nl_pstate; struct nlmsghdr; struct ktest_test_context { void *arg; struct nl_pstate *npt; struct nlmsghdr *hdr; char *buf; size_t bufsize; }; typedef int (*ktest_run_t)(struct ktest_test_context *ctx); typedef int (*ktest_parse_t)(struct ktest_test_context *ctx, struct nlattr *container); struct ktest_test_info { const char *name; const char *desc; ktest_run_t func; ktest_parse_t parse; }; struct ktest_module_info { const char *name; const struct ktest_test_info *tests; int num_tests; void *module_ptr; }; int ktest_default_modevent(module_t mod, int type, void *arg); bool ktest_start_msg(struct ktest_test_context *ctx); void ktest_add_msg_meta(struct ktest_test_context *ctx, const char *func, const char *fname, int line); void ktest_add_msg_text(struct ktest_test_context *ctx, int msg_level, const char *fmt, ...); void ktest_end_msg(struct ktest_test_context *ctx); #define KTEST_LOG_LEVEL(_ctx, _l, _fmt, ...) { \ if (ktest_start_msg(_ctx)) { \ ktest_add_msg_meta(_ctx, __func__, __FILE__, __LINE__); \ ktest_add_msg_text(_ctx, _l, _fmt, ## __VA_ARGS__); \ ktest_end_msg(_ctx); \ } \ } #define KTEST_LOG(_ctx, _fmt, ...) \ KTEST_LOG_LEVEL(_ctx, LOG_DEBUG, _fmt, ## __VA_ARGS__) #define KTEST_MAX_BUF 512 #define KTEST_MODULE_DECLARE(_n, _t) \ static struct ktest_module_info _module_info = { \ .name = #_n, \ .tests = _t, \ .num_tests = nitems(_t), \ }; \ \ static moduledata_t _module_data = { \ - "__" #_n "_module", \ + #_n, \ ktest_default_modevent, \ &_module_info, \ }; \ \ DECLARE_MODULE(ktest_##_n, _module_data, SI_SUB_PSEUDO, SI_ORDER_ANY); \ MODULE_VERSION(ktest_##_n, 1); \ MODULE_DEPEND(ktest_##_n, ktestmod, 1, 1, 1); \ +MODULE_DEPEND(ktest_##_n, netlink, 1, 1, 1); \ #endif /* _KERNEL */ /* genetlink definitions */ #define KTEST_FAMILY_NAME "ktest" /* commands */ enum { KTEST_CMD_UNSPEC = 0, KTEST_CMD_LIST = 1, KTEST_CMD_RUN = 2, KTEST_CMD_NEWTEST = 3, KTEST_CMD_NEWMESSAGE = 4, __KTEST_CMD_MAX, }; #define KTEST_CMD_MAX (__KTEST_CMD_MAX - 1) enum ktest_attr_type_t { KTEST_ATTR_UNSPEC, KTEST_ATTR_MOD_NAME = 1, /* string: test module name */ KTEST_ATTR_TEST_NAME = 2, /* string: test name */ KTEST_ATTR_TEST_DESCR = 3, /* string: test description */ KTEST_ATTR_TEST_META = 4, /* nested: container with test-specific metadata */ }; enum ktest_msg_attr_type_t { KTEST_MSG_ATTR_UNSPEC, KTEST_MSG_ATTR_TS = 1, /* struct timespec */ KTEST_MSG_ATTR_FUNC = 2, /* string: function name */ KTEST_MSG_ATTR_FILE = 3, /* string: file name */ KTEST_MSG_ATTR_LINE = 4, /* u32: line in the file */ KTEST_MSG_ATTR_TEXT = 5, /* string: actual message data */ KTEST_MSG_ATTR_LEVEL = 6, /* u8: syslog loglevel */ KTEST_MSG_ATTR_META = 7, /* nested: message metadata */ }; #endif diff --git a/tests/atf_python/ktest.py b/tests/atf_python/ktest.py index 4cd9970aaec1..a18f47d1dd06 100644 --- a/tests/atf_python/ktest.py +++ b/tests/atf_python/ktest.py @@ -1,173 +1,173 @@ import logging import time from typing import NamedTuple import pytest from atf_python.sys.netlink.attrs import NlAttrNested from atf_python.sys.netlink.attrs import NlAttrStr from atf_python.sys.netlink.netlink import NetlinkMultipartIterator from atf_python.sys.netlink.netlink import NlHelper from atf_python.sys.netlink.netlink import Nlsock from atf_python.sys.netlink.netlink_generic import KtestAttrType from atf_python.sys.netlink.netlink_generic import KtestInfoMessage from atf_python.sys.netlink.netlink_generic import KtestLogMsgType from atf_python.sys.netlink.netlink_generic import KtestMsgAttrType from atf_python.sys.netlink.netlink_generic import KtestMsgType from atf_python.sys.netlink.netlink_generic import timespec from atf_python.sys.netlink.utils import NlConst from atf_python.utils import BaseTest from atf_python.utils import libc from atf_python.utils import nodeid_to_method_name datefmt = "%H:%M:%S" fmt = "%(asctime)s.%(msecs)03d %(filename)s:%(funcName)s:%(lineno)d %(message)s" logging.basicConfig(level=logging.DEBUG, format=fmt, datefmt=datefmt) logger = logging.getLogger("ktest") NETLINK_FAMILY = "ktest" class KtestItem(pytest.Item): def __init__(self, *, descr, kcls, **kwargs): super().__init__(**kwargs) self.descr = descr self._kcls = kcls def runtest(self): self._kcls().runtest() class KtestCollector(pytest.Class): def collect(self): obj = self.obj exclude_names = set([n for n in dir(obj) if not n.startswith("_")]) autoload = obj.KTEST_MODULE_AUTOLOAD module_name = obj.KTEST_MODULE_NAME loader = KtestLoader(module_name, autoload) ktests = loader.load_ktests() if not ktests: return orig = pytest.Class.from_parent(self.parent, name=self.name, obj=obj) for py_test in orig.collect(): yield py_test for ktest in ktests: name = ktest["name"] descr = ktest["desc"] if name in exclude_names: continue yield KtestItem.from_parent(self, name=name, descr=descr, kcls=obj) class KtestLoader(object): def __init__(self, module_name: str, autoload: bool): self.module_name = module_name self.autoload = autoload self.helper = NlHelper() self.nlsock = Nlsock(NlConst.NETLINK_GENERIC, self.helper) self.family_id = self._get_family_id() def _get_family_id(self): try: family_id = self.nlsock.get_genl_family_id(NETLINK_FAMILY) except ValueError: if self.autoload: libc.kldload(self.module_name) family_id = self.nlsock.get_genl_family_id(NETLINK_FAMILY) else: raise return family_id def _load_ktests(self): msg = KtestInfoMessage(self.helper, self.family_id, KtestMsgType.KTEST_CMD_LIST) msg.set_request() msg.add_nla(NlAttrStr(KtestAttrType.KTEST_ATTR_MOD_NAME, self.module_name)) self.nlsock.write_message(msg, verbose=False) nlmsg_seq = msg.nl_hdr.nlmsg_seq ret = [] for rx_msg in NetlinkMultipartIterator(self.nlsock, nlmsg_seq, self.family_id): - # test_msg.print_message() + # rx_msg.print_message() tst = { "mod_name": rx_msg.get_nla(KtestAttrType.KTEST_ATTR_MOD_NAME).text, "name": rx_msg.get_nla(KtestAttrType.KTEST_ATTR_TEST_NAME).text, "desc": rx_msg.get_nla(KtestAttrType.KTEST_ATTR_TEST_DESCR).text, } ret.append(tst) return ret def load_ktests(self): ret = self._load_ktests() if not ret and self.autoload: libc.kldload(self.module_name) ret = self._load_ktests() return ret def generate_ktests(collector, name, obj): if getattr(obj, "KTEST_MODULE_NAME", None) is not None: return KtestCollector.from_parent(collector, name=name, obj=obj) return None class BaseKernelTest(BaseTest): KTEST_MODULE_AUTOLOAD = True KTEST_MODULE_NAME = None def _get_record_time(self, msg) -> float: timespec = msg.get_nla(KtestMsgAttrType.KTEST_MSG_ATTR_TS).ts epoch_ktime = timespec.tv_sec * 1.0 + timespec.tv_nsec * 1.0 / 1000000000 if not hasattr(self, "_start_epoch"): self._start_ktime = epoch_ktime self._start_time = time.time() epoch_time = self._start_time else: epoch_time = time.time() - self._start_time + epoch_ktime return epoch_time def _log_message(self, msg): # Convert syslog-type l syslog_level = msg.get_nla(KtestMsgAttrType.KTEST_MSG_ATTR_LEVEL).u8 if syslog_level <= 6: loglevel = logging.INFO else: loglevel = logging.DEBUG rec = logging.LogRecord( self.KTEST_MODULE_NAME, loglevel, msg.get_nla(KtestMsgAttrType.KTEST_MSG_ATTR_FILE).text, msg.get_nla(KtestMsgAttrType.KTEST_MSG_ATTR_LINE).u32, "%s", (msg.get_nla(KtestMsgAttrType.KTEST_MSG_ATTR_TEXT).text), None, msg.get_nla(KtestMsgAttrType.KTEST_MSG_ATTR_FUNC).text, None, ) rec.created = self._get_record_time(msg) logger.handle(rec) def _runtest_name(self, test_name: str, test_data): module_name = self.KTEST_MODULE_NAME # print("Running kernel test {} for module {}".format(test_name, module_name)) helper = NlHelper() nlsock = Nlsock(NlConst.NETLINK_GENERIC, helper) family_id = nlsock.get_genl_family_id(NETLINK_FAMILY) msg = KtestInfoMessage(helper, family_id, KtestMsgType.KTEST_CMD_RUN) msg.set_request() msg.add_nla(NlAttrStr(KtestAttrType.KTEST_ATTR_MOD_NAME, module_name)) msg.add_nla(NlAttrStr(KtestAttrType.KTEST_ATTR_TEST_NAME, test_name)) if test_data is not None: msg.add_nla(NlAttrNested(KtestAttrType.KTEST_ATTR_TEST_META, test_data)) nlsock.write_message(msg, verbose=False) for log_msg in NetlinkMultipartIterator( nlsock, msg.nl_hdr.nlmsg_seq, family_id ): self._log_message(log_msg) def runtest(self, test_data=None): self._runtest_name(nodeid_to_method_name(self.test_id), test_data)