This adds functions for both executing the tests as well as parsing TAP
kmsg output.
Changes since v1:
- Correct handling of read to deal with partial buffer.
Signed-off-by: Isabella Basso <isabbasso@riseup.net>
---
lib/igt_kmod.c | 169 +++++++++++++++++++++++++++++++++++++++++++++++++
lib/igt_kmod.h | 13 ++++
2 files changed, 182 insertions(+)
diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index cf7a3b22..5d8e8b94 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -345,6 +345,175 @@ igt_kmod_list_loaded(void)
kmod_module_unref_list(list);
}
+static void kmsg_parse_tap(int fd)
+{
+ char record[4096 + 1];
+ const char *num_tests_str, *nok_str, *ok_str, *end_str_success, *end_str_fail;
+ bool has_num = false;
+
+ num_tests_str = "..";
+ nok_str = " not ok ";
+ ok_str = " ok ";
+ end_str_fail = "not ok ";
+ end_str_success = "ok ";
+
+ if (fd == -1) {
+ igt_warn("Unable to retrieve kernel log (from /dev/kmsg)\n");
+ return;
+ }
+
+ record[sizeof(record) - 1] = '\0';
+
+ for (;;) {
+ const char *end, *result;
+ ssize_t r;
+
+ r = read(fd, record, sizeof(record) - 1);
+ if (r < 0) {
+ if (errno == EINTR)
+ continue;
+
+ if (errno == EPIPE) {
+ igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
+ continue;
+ }
+
+ if (errno != EAGAIN)
+ igt_warn("kmsg truncated: unknown error (%m)\n");
+
+ break;
+ }
+
+ /* deal with partial string from read */
+ if (record[r - 1] == NULL) {
+ fseek(fd, 0, end);
+ continue;
+ }
+
+ if (!has_num) {
+ result = strstr(record, num_tests_str);
+ if (result) {
+ result += strlen(num_tests_str);
+ end = strchrnul(result, '\n');
+ igt_info("[IGT] running %.*s tests...\n",
+ (int)(end - result), result);
+ has_num = true;
+ }
+ continue;
+ }
+
+ /* all results have 'ok' in them */
+ result = strstr(record, "ok");
+
+ if (result) {
+ end = strchrnul(result, '\n');
+
+ /* subtest succeded (i.e. ok_str) */
+ result = strstr(record, ok_str);
+
+ if (result) {
+ result += strlen(ok_str);
+ igt_info("[IGT] SUBTEST %.*s\n",
+ (int)(end - result), result);
+ continue;
+ }
+
+ /* subtest failed (i.e. nok_str) */
+ result = strstr(record, nok_str);
+
+ if (result) {
+ result += strlen(nok_str);
+ igt_warn("[IGT] SUBTEST FAILED %.*s\n",
+ (int)(end - result), result);
+ continue;
+ }
+
+ /* test ended (i.e. end_str_success) */
+ result = strchr(record, ';') + 1;
+
+ if (strncmp(result, end_str_success, strlen(end_str_success)) == 0) {
+ igt_info("[IGT] TEST SUCCEEDED %.*s\n",
+ (int)(end - result), result);
+
+ igt_success();
+ }
+
+ if (strncmp(result, end_str_fail, strlen(end_str_fail)) == 0) {
+ igt_warn("[IGT] TEST FAILED %.*s\n",
+ (int)(end - result), result);
+ igt_fail(IGT_EXIT_FAILURE);
+ }
+ }
+ }
+}
+
+/**
+ * igt_kunit: tests a kunit module
+ * @mod_name: the name of the module
+ * @options: options to load the module
+ * @req_mods: array with additional dependencies to run the KUnit test
+ * @req_mod_len: size of req_mods array
+ *
+ * Loads the kunit module, parses its dmesg output, then unloads it
+ */
+void igt_kunit(const char *mod_name, const char *options,
+ const char **req_mods, int req_mod_len)
+{
+ int kmsg_fd, cmi;
+ struct igt_kselftest tst;
+ const char *norm_name;
+
+ /* get normalized module name */
+ igt_require(igt_kselftest_init(&tst, mod_name) == 0);
+
+ norm_name = kmod_module_get_name(tst.kmod);
+
+ if (!igt_kmod_is_loaded(norm_name)) {
+ /* The kunit module is required for running any kunit tests */
+ if (!igt_kmod_is_loaded("kunit"))
+ igt_require(igt_kmod_load("kunit", NULL) == 0);
+
+ for (cmi = 0; cmi < req_mod_len; cmi++) {
+ if (strcmp(req_mods[cmi], "kunit") == 0)
+ continue;
+ if (igt_kmod_load(req_mods[cmi], NULL))
+ goto unload;
+ }
+ } else {
+ /* Unload module if loaded (maybe previous run bailed out?) */
+ igt_kmod_unload(mod_name, 0);
+ }
+
+ kmsg_fd = open("/dev/kmsg", O_RDONLY | O_NONBLOCK);
+
+ if (kmsg_fd < 0) {
+ igt_warn("Could not open /dev/kmsg");
+ goto unload;
+ }
+
+ if (lseek(kmsg_fd, 0, SEEK_END)) {
+ igt_warn("Could not seek the end of /dev/kmsg");
+ close(kmsg_fd);
+ goto unload;
+ }
+
+ igt_require(igt_kmod_load(mod_name, options) == 0);
+
+ kmsg_parse_tap(kmsg_fd);
+
+ igt_kmod_unload(mod_name, 0);
+
+ close(kmsg_fd);
+unload:
+ igt_kmod_unload("kunit", 0);
+
+ for (cmi--; cmi >= 0; cmi--) {
+ if (strcmp(req_mods[cmi], "kunit") == 0)
+ continue;
+ igt_kmod_unload(req_mods[cmi], 0);
+ }
+}
+
/**
* igt_i915_driver_load:
* @opts: options to pass to i915 driver
diff --git a/lib/igt_kmod.h b/lib/igt_kmod.h
index 0898122b..60e7a750 100644
--- a/lib/igt_kmod.h
+++ b/lib/igt_kmod.h
@@ -28,6 +28,16 @@
#include "igt_list.h"
+#define MAX_MOD_NAME_LEN 40
+
+/**
+ * ARRAY_SIZE:
+ * @arr: static array
+ *
+ * Macro to compute the size of the static array @arr.
+ */
+#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
+
bool igt_kmod_is_loaded(const char *mod_name);
void igt_kmod_list_loaded(void);
@@ -36,6 +46,9 @@ bool igt_kmod_has_param(const char *mod_name, const char *param);
int igt_kmod_load(const char *mod_name, const char *opts);
int igt_kmod_unload(const char *mod_name, unsigned int flags);
+void igt_kunit(const char *mod_name, const char *options,
+ const char **req_mods, int req_mod_len);
+
int igt_i915_driver_load(const char *opts);
int igt_i915_driver_unload(void);
int __igt_i915_driver_unload(const char **whom);
--
2.35.1
This adds a converted KUnit test module to IGT.
Signed-off-by: Isabella Basso <isabbasso@riseup.net>
---
lib/igt_kmod.c | 41 ++++++++++++++++++++++++++++++-----------
tests/kms_kunit.c | 34 ++++++++++++++++++++++++++++++++++
tests/meson.build | 1 +
3 files changed, 65 insertions(+), 11 deletions(-)
create mode 100644 tests/kms_kunit.c
diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index 5d8e8b94..14f1939d 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -364,12 +364,24 @@ static void kmsg_parse_tap(int fd)
record[sizeof(record) - 1] = '\0';
- for (;;) {
+ while (true) {
const char *end, *result;
ssize_t r;
+ bool skip = true;
- r = read(fd, record, sizeof(record) - 1);
- if (r < 0) {
+ if (!skip) {
+ end = strchrnul(record, '\n');
+ } else {
+ end = NULL;
+ skip = false;
+ }
+
+ r = read(fd, record,
+ end != NULL ?
+ (int) (end - record) :
+ sizeof(record) - 1);
+
+ if (r <= 0) {
if (errno == EINTR)
continue;
@@ -384,19 +396,19 @@ static void kmsg_parse_tap(int fd)
break;
}
- /* deal with partial string from read */
- if (record[r - 1] == NULL) {
- fseek(fd, 0, end);
- continue;
- }
-
if (!has_num) {
result = strstr(record, num_tests_str);
if (result) {
result += strlen(num_tests_str);
end = strchrnul(result, '\n');
- igt_info("[IGT] running %.*s tests...\n",
- (int)(end - result), result);
+ /* in case line > 4096 */
+ if (end == NULL) {
+ igt_info("%s\n", record);
+ }
+ else {
+ igt_info("[IGT] running %.*s tests...\n",
+ (int)(end - result), result);
+ }
has_num = true;
}
continue;
@@ -407,6 +419,11 @@ static void kmsg_parse_tap(int fd)
if (result) {
end = strchrnul(result, '\n');
+ /* in case line > 4096 */
+ if (end == NULL) {
+ igt_info("%s\n", record);
+ continue;
+ }
/* subtest succeded (i.e. ok_str) */
result = strstr(record, ok_str);
@@ -429,6 +446,7 @@ static void kmsg_parse_tap(int fd)
}
/* test ended (i.e. end_str_success) */
+ /* we should skip the ; in the beginning of the string */
result = strchr(record, ';') + 1;
if (strncmp(result, end_str_success, strlen(end_str_success)) == 0) {
@@ -444,6 +462,7 @@ static void kmsg_parse_tap(int fd)
igt_fail(IGT_EXIT_FAILURE);
}
}
+ skip = true;
}
}
diff --git a/tests/kms_kunit.c b/tests/kms_kunit.c
new file mode 100644
index 00000000..31566910
--- /dev/null
+++ b/tests/kms_kunit.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright © 2021 Isabella Basso do Amaral <isabbasso@riseup.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <libkmod.h>
+
+#include "igt.h"
+#include "igt_kmod.h"
+
+IGT_TEST_DESCRIPTION("KUnit DRM damage helper module testing");
+
+igt_simple_main
+{
+ igt_kunit("test-drm_damage_helper", NULL, NULL, 0);
+}
diff --git a/tests/meson.build b/tests/meson.build
index 3dbba7a1..6d3685c3 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -36,6 +36,7 @@ test_progs = [
'kms_hdmi_inject',
'kms_hdr',
'kms_invalid_mode',
+ 'kms_kunit',
'kms_lease',
'kms_multipipe_modeset',
'kms_panel_fitting',
--
2.35.1