[igt-dev] [Intel-gfx] [PATCH i-g-t v3 15/17] lib/kunit: Parse KTAP report from the main process thread

Mauro Carvalho Chehab mauro.chehab at linux.intel.com
Wed Sep 20 18:05:12 UTC 2023


On Mon, 18 Sep 2023 15:43:05 +0200
Janusz Krzysztofik <janusz.krzysztofik at linux.intel.com> wrote:

> There was an attempt to parse KTAP reports in the background while a kunit
> test module is loading.  However, since dynamic sub-subtests can be
> executed only from the main thread, that attempt was not quite successful,
> as IGT results from all executed kunit test cases were generated only
> after loading of kunit test module completed.
> 
> Now that the parser maintains its state and we can call it separately for
> each input line of a KTAP report, it is perfectly possible to call the
> parser from the main thread while the module is loading in the background,
> and convert results from kunit test cases immediately to results of IGT
> dynamic sub-subtests by running an igt_dynamic() section for each result
> as soon as returned by the parser.
> 
> Drop igt_ktap_parser() thread and execute igt_dynamic() for each kunit
> result obtained from igt_ktap_parse() called from the main thread.
> 
> Also, drop no longer needed functions from igt_ktap soruces.
> 
> v2: Interrupt blocking read() on modprobe failure.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik at linux.intel.com>

LGTM.

Acked-by: Mauro Carvalho Chehab <mchehab at kernel.org>

> ---
>  lib/igt_kmod.c | 208 ++++++++++++++----
>  lib/igt_ktap.c | 568 -------------------------------------------------
>  lib/igt_ktap.h |  22 --
>  3 files changed, 166 insertions(+), 632 deletions(-)
> 
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index 8fbd274ccf..7fa5b4aa80 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright © 2016 Intel Corporation
> + * Copyright © 2016-2023 Intel Corporation
>   *
>   * Permission is hereby granted, free of charge, to any person obtaining a
>   * copy of this software and associated documentation files (the "Software"),
> @@ -26,7 +26,10 @@
>  #include <errno.h>
>  #include <fcntl.h>
>  #include <pthread.h>
> +#include <stdlib.h>
> +#include <string.h>
>  #include <sys/utsname.h>
> +#include <unistd.h>
>  
>  #include "igt_aux.h"
>  #include "igt_core.h"
> @@ -748,20 +751,109 @@ void igt_kselftest_get_tests(struct kmod_module *kmod,
>  }
>  
>  struct modprobe_data {
> +	pthread_t parent;
>  	struct kmod_module *kmod;
>  	const char *opts;
>  	int err;
> +	pthread_mutex_t lock;
> +	pthread_t thread;
>  };
>  
>  static void *modprobe_task(void *arg)
>  {
>  	struct modprobe_data *data = arg;
> +	int err;
> +
> +	err = modprobe(data->kmod, data->opts);
>  
> -	data->err = modprobe(data->kmod, data->opts);
> +	if (err) {
> +		while (pthread_mutex_trylock(&data->lock) == EBUSY)
> +			pthread_kill(data->parent, SIGCHLD);
> +
> +		data->err = err;
> +		pthread_mutex_unlock(&data->lock);
> +	}
>  
>  	return NULL;
>  }
>  
> +static void kunit_sigchld_handler(int signal)
> +{
> +	return;
> +}
> +
> +static int kunit_kmsg_result_get(struct igt_list_head *results,
> +				 struct modprobe_data *modprobe,
> +				 int fd, struct igt_ktap_results *ktap)
> +{
> +	struct sigaction sigchld = { .sa_handler = kunit_sigchld_handler, };
> +	char record[BUF_LEN + 1], *buf;
> +	unsigned long taints;
> +	int ret;
> +
> +	do {
> +		if (igt_kernel_tainted(&taints))
> +			return -ENOTRECOVERABLE;
> +
> +		pthread_mutex_lock(&modprobe->lock);
> +		if (!pthread_tryjoin_np(modprobe->thread, NULL) &&
> +		    modprobe->err) {
> +			pthread_mutex_unlock(&modprobe->lock);
> +			return modprobe->err;
> +		}
> +
> +		sigaction(SIGCHLD, &sigchld, NULL);
> +		ret = read(fd, record, BUF_LEN);
> +		sigaction(SIGCHLD, NULL, NULL);
> +		pthread_mutex_unlock(&modprobe->lock);
> +
> +		if (!ret)
> +			return -ENODATA;
> +		if (ret == -1)
> +			return -errno;
> +
> +		igt_assert_lt(0, ret);
> +
> +		/* skip kmsg continuation lines */
> +		if (igt_debug_on(*record == ' '))
> +			continue;
> +
> +		/* NULL-terminate the record */
> +		record[ret] = '\0';
> +
> +		/* detect start of log message, continue if not found */
> +		buf = strchrnul(record, ';');
> +		if (igt_debug_on(*buf == '\0'))
> +			continue;
> +		buf++;
> +
> +		ret = igt_ktap_parse(buf, ktap);
> +		if (ret != -EINPROGRESS)
> +			break;
> +	} while (igt_list_empty(results));
> +
> +	return ret;
> +}
> +
> +static void kunit_result_free(struct igt_ktap_result *r,
> +			      char **suite_name, char **case_name)
> +{
> +	igt_list_del(&r->link);
> +
> +	if (r->suite_name != *suite_name) {
> +		free(*suite_name);
> +		*suite_name = r->suite_name;
> +	}
> +
> +	if (r->case_name != *case_name) {
> +		free(*case_name);
> +		*case_name = r->case_name;
> +	}
> +
> +	free(r->msg);
> +	free(r);
> +}
> +
>  /**
>   * igt_kunit:
>   * @module_name: the name of the module
> @@ -773,11 +865,12 @@ static void *modprobe_task(void *arg)
>   */
>  static void __igt_kunit(struct igt_ktest *tst, const char *opts)
>  {
> -	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
> -	struct kmod_module *kunit_kmod;
> -	bool is_builtin;
> -	struct ktap_test_results *results;
> -	pthread_t modprobe_thread;
> +	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0,
> +					  PTHREAD_MUTEX_INITIALIZER, };
> +	char *suite_name = NULL, *case_name = NULL;
> +	struct igt_ktap_result *r, *rn;
> +	struct igt_ktap_results *ktap;
> +	IGT_LIST_HEAD(results);
>  	unsigned long taints;
>  	int flags, ret;
>  
> @@ -787,62 +880,93 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
>  	igt_skip_on_f(fcntl(tst->kmsg, F_SETFL, flags) == -1,
>  		      "Could not set /dev/kmsg to blocking mode\n");
>  
> -	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
> -
> -	igt_skip_on(kmod_module_new_from_name(kmod_ctx(), "kunit", &kunit_kmod));
> -	is_builtin = kmod_module_get_initstate(kunit_kmod) == KMOD_MODULE_BUILTIN;
> -	kmod_module_unref(kunit_kmod);
> +	ktap = igt_ktap_alloc(&results);
> +	igt_require(ktap);
>  
> -	results = ktap_parser_start(tst->kmsg, is_builtin);
> +	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
>  
> -	if (igt_debug_on(pthread_create(&modprobe_thread, NULL,
> +	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
>  					modprobe_task, &modprobe))) {
> -		ktap_parser_cancel();
> -		igt_ignore_warn(ktap_parser_stop());
> +		igt_ktap_free(ktap);
>  		igt_skip("Failed to create a modprobe thread\n");
>  	}
>  
> -	while (READ_ONCE(results->still_running) || !igt_list_empty(&results->list))
> -	{
> -		struct ktap_test_results_element *result;
> -
> -		if (!pthread_tryjoin_np(modprobe_thread, NULL) && modprobe.err) {
> -			ktap_parser_cancel();
> +	do {
> +		ret = kunit_kmsg_result_get(&results, &modprobe,
> +					    tst->kmsg, ktap);
> +		if (igt_debug_on(ret && ret != -EINPROGRESS))
>  			break;
> -		}
>  
> -		if (igt_kernel_tainted(&taints)) {
> -			ktap_parser_cancel();
> -			pthread_cancel(modprobe_thread);
> +		if (igt_debug_on(igt_list_empty(&results)))
>  			break;
> -		}
>  
> -		pthread_mutex_lock(&results->mutex);
> -		if (igt_list_empty(&results->list)) {
> -			pthread_mutex_unlock(&results->mutex);
> -			continue;
> -		}
> +		r = igt_list_first_entry(&results, r, link);
> +
> +		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
> +			if (r->code == IGT_EXIT_INVALID) {
> +				/* parametrized test case, get actual result */
> +				kunit_result_free(r, &suite_name, &case_name);
> +				r = NULL;
> +
> +				igt_assert(igt_list_empty(&results));
> +
> +				ret = kunit_kmsg_result_get(&results, &modprobe,
> +							    tst->kmsg, ktap);
> +				if (ret != -EINPROGRESS)
> +					igt_fail_on(ret);
>  
> -		result = igt_list_first_entry(&results->list, result, link);
> +				igt_fail_on(igt_list_empty(&results));
>  
> -		igt_list_del(&result->link);
> -		pthread_mutex_unlock(&results->mutex);
> +				r = igt_list_first_entry(&results, r, link);
>  
> -		igt_dynamic(result->test_name) {
> -			igt_assert(READ_ONCE(result->passed));
> +				igt_fail_on_f(strcmp(r->suite_name, suite_name),
> +					      "suite_name expected: %s, got: %s\n",
> +					      suite_name, r->suite_name);
> +				igt_fail_on_f(strcmp(r->case_name, case_name),
> +					      "case_name expected: %s, got: %s\n",
> +					      case_name, r->case_name);
> +			}
> +
> +			igt_assert_neq(r->code, IGT_EXIT_INVALID);
> +
> +			if (r->msg && *r->msg) {
> +				igt_skip_on_f(r->code == IGT_EXIT_SKIP,
> +					      "%s", r->msg);
> +				igt_fail_on_f(r->code == IGT_EXIT_FAILURE,
> +					      "%s", r->msg);
> +				igt_abort_on_f(r->code == IGT_EXIT_ABORT,
> +					      "%s", r->msg);
> +			} else {
> +				igt_skip_on(r->code == IGT_EXIT_SKIP);
> +				igt_fail_on(r->code == IGT_EXIT_FAILURE);
> +				if (igt_debug_on(r->code != IGT_EXIT_SUCCESS))
> +					igt_fail(r->code);
> +			}
>  
> -			if (!pthread_tryjoin_np(modprobe_thread, NULL))
> +			if (ret != -EINPROGRESS)
> +				igt_assert_eq(ret, 0);
> +
> +			if (!pthread_tryjoin_np(modprobe.thread, NULL))
>  				igt_assert_eq(modprobe.err, 0);
>  
>  			igt_fail_on(igt_kernel_tainted(&taints));
>  		}
>  
> -		free(result);
> -	}
> +		kunit_result_free(r, &suite_name, &case_name);
> +
> +	} while (ret == -EINPROGRESS);
>  
> -	pthread_join(modprobe_thread, NULL);
> +	igt_list_for_each_entry_safe(r, rn, &results, link)
> +		kunit_result_free(r, &suite_name, &case_name);
> +
> +	free(case_name);
> +	free(suite_name);
> +
> +	if (ret)
> +		pthread_cancel(modprobe.thread);
> +	pthread_join(modprobe.thread, NULL);
>  
> -	ret = ktap_parser_stop();
> +	igt_ktap_free(ktap);
>  
>  	igt_skip_on(modprobe.err);
>  	igt_skip_on(igt_kernel_tainted(&taints));
> diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
> index 5eac102417..53a6c63288 100644
> --- a/lib/igt_ktap.c
> +++ b/lib/igt_ktap.c
> @@ -4,17 +4,11 @@
>   * Copyright © 2023 Intel Corporation
>   */
>  
> -#include <ctype.h>
> -#include <limits.h>
> -#include <libkmod.h>
> -#include <pthread.h>
>  #include <errno.h>
>  #include <stdio.h>
>  #include <stdlib.h>
>  #include <string.h>
> -#include <unistd.h>
>  
> -#include "igt_aux.h"
>  #include "igt_core.h"
>  #include "igt_ktap.h"
>  #include "igt_list.h"
> @@ -312,565 +306,3 @@ void igt_ktap_free(struct igt_ktap_results *ktap)
>  {
>  	free(ktap);
>  }
> -
> -#define DELIMITER "-"
> -
> -struct ktap_parser_args {
> -	int fd;
> -	bool is_builtin;
> -	int ret;
> -} ktap_args;
> -
> -static struct ktap_test_results results;
> -
> -static int log_to_end(enum igt_log_level level, int fd,
> -		      char *record, const char *format, ...) __attribute__((format(printf, 4, 5)));
> -
> -/**
> - * log_to_end:
> - * @level: #igt_log_level
> - * @record: record to store the read data
> - * @format: format string
> - * @...: optional arguments used in the format string
> - *
> - * This is an altered version of the generic structured logging helper function
> - * igt_log capable of reading to the end of a given line.
> - *
> - * Returns: 0 for success, or -2 if there's an error reading from the file
> - */
> -static int log_to_end(enum igt_log_level level, int fd,
> -		      char *record, const char *format, ...)
> -{
> -	va_list args;
> -	const char *lend;
> -
> -	/* Cutoff after newline character, in order to not display garbage */
> -	char *cutoff = strchr(record, '\n');
> -	if (cutoff) {
> -		if (cutoff - record < BUF_LEN)
> -			cutoff[1] = '\0';
> -	}
> -
> -	va_start(args, format);
> -	igt_vlog(IGT_LOG_DOMAIN, level, format, args);
> -	va_end(args);
> -
> -	lend = strchrnul(record, '\n');
> -	while (*lend == '\0') {
> -		igt_log(IGT_LOG_DOMAIN, level, "%s", record);
> -
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("an error occurred while reading kmsg: %m\n");
> -
> -			return -2;
> -		}
> -
> -		lend = strchrnul(record, '\n');
> -	}
> -	return 0;
> -}
> -
> -/**
> - * lookup_value:
> - * @haystack: the string to search in
> - * @needle: the string to search for
> - *
> - * Returns: the value of the needle in the haystack, or -1 if not found.
> - */
> -static long lookup_value(const char *haystack, const char *needle)
> -{
> -	const char *needle_rptr;
> -	char *needle_end;
> -	long num;
> -
> -	needle_rptr = strcasestr(haystack, needle);
> -
> -	if (needle_rptr == NULL)
> -		return -1;
> -
> -	/* Skip search string and whitespaces after it */
> -	needle_rptr += strlen(needle);
> -
> -	num = strtol(needle_rptr, &needle_end, 10);
> -
> -	if (needle_rptr == needle_end)
> -		return -1;
> -
> -	if (num == LONG_MIN || num == LONG_MAX)
> -		return 0;
> -
> -	return num > 0 ? num : 0;
> -}
> -
> -/**
> - * tap_version_present:
> - * @record: buffer with tap data
> - * @print_info: whether tap version should be printed or not
> - *
> - * Returns:
> - * 0 if not found
> - * 1 if found
> - */
> -static int tap_version_present(char* record, bool print_info)
> -{
> -	/*
> -	 * "(K)TAP version XX" should be the first line on all (sub)tests as per
> -	 * https://kernel.org/doc/html/latest/dev-tools/ktap.html#version-lines
> -	 *
> -	 * but actually isn't, as it currently depends on the KUnit module
> -	 * being built-in, so we can't rely on it every time
> -	 */
> -	const char *version_rptr = strcasestr(record, "TAP version ");
> -	char *cutoff;
> -
> -	if (version_rptr == NULL)
> -		return 0;
> -
> -	/* Cutoff after newline character, in order to not display garbage */
> -	cutoff = strchr(version_rptr, '\n');
> -	if (cutoff)
> -		cutoff[0] = '\0';
> -
> -	if (print_info)
> -		igt_info("%s\n", version_rptr);
> -
> -	return 1;
> -}
> -
> -/**
> - * find_next_tap_subtest:
> - * @fd: file descriptor
> - * @record: buffer used to read fd
> - * @is_builtin: whether KUnit is built-in or not
> - *
> - * Returns:
> - * 0 if there's missing information
> - * -1 if not found
> - * -2 if there are problems while reading the file.
> - * any other value corresponds to the amount of cases of the next (sub)test
> - */
> -static int find_next_tap_subtest(int fd, char *record, char *test_name, bool is_builtin)
> -{
> -	const char *test_lookup_str, *subtest_lookup_str, *name_rptr;
> -	long test_count;
> -	char *cutoff;
> -
> -	test_name[0] = '\0';
> -	test_name[BUF_LEN] = '\0';
> -
> -	test_lookup_str = " subtest: ";
> -	subtest_lookup_str = " test: ";
> -
> -	if (!tap_version_present(record, true))
> -		return -1;
> -
> -	if (is_builtin) {
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("an error occurred while reading kmsg: %m\n");
> -
> -			return -2;
> -		}
> -	}
> -
> -	name_rptr = strcasestr(record, test_lookup_str);
> -	if (name_rptr != NULL) {
> -		name_rptr += strlen(test_lookup_str);
> -	} else {
> -		name_rptr = strcasestr(record, subtest_lookup_str);
> -		if (name_rptr != NULL)
> -			name_rptr += strlen(subtest_lookup_str);
> -	}
> -
> -	if (name_rptr == NULL) {
> -		if (!is_builtin)
> -			/* We've probably found nothing */
> -			return -1;
> -		igt_info("Missing test name\n");
> -	} else {
> -		strncpy(test_name, name_rptr, BUF_LEN);
> -		/* Cutoff after newline character, in order to not display garbage */
> -		cutoff = strchr(test_name, '\n');
> -		if (cutoff)
> -			cutoff[0] = '\0';
> -
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("unknown error reading kmsg (%m)\n");
> -
> -			return -2;
> -		}
> -
> -		/* Now we can be sure we found tests */
> -		if (!is_builtin)
> -			igt_info("KUnit is not built-in, skipping version check...\n");
> -	}
> -
> -	/*
> -	 * Total test count will almost always appear as 0..N at the beginning
> -	 * of a run, so we use it to reliably identify a new run
> -	 */
> -	test_count = lookup_value(record, "..");
> -
> -	if (test_count <= 0) {
> -		igt_info("Missing test count\n");
> -		if (test_name[0] == '\0')
> -			return 0;
> -		if (log_to_end(IGT_LOG_INFO, fd, record,
> -				"Running some tests in: %s\n",
> -				test_name) < 0)
> -			return -2;
> -		return 0;
> -	} else if (test_name[0] == '\0') {
> -		igt_info("Running %ld tests...\n", test_count);
> -		return 0;
> -	}
> -
> -	if (log_to_end(IGT_LOG_INFO, fd, record,
> -			"Executing %ld tests in: %s\n",
> -			test_count, test_name) < 0)
> -		return -2;
> -
> -	return test_count;
> -}
> -
> -/**
> - * parse_kmsg_for_tap:
> - * @fd: file descriptor
> - * @record: buffer used to read fd
> - * @test_name: buffer to store the test name
> - *
> - * Returns:
> - * 1 if no results were found
> - * 0 if a test succeded
> - * -1 if a test failed
> - * -2 if there are problems reading the file
> - */
> -static int parse_kmsg_for_tap(int fd, char *record, char *test_name)
> -{
> -	const char *lstart, *ok_lookup_str, *nok_lookup_str,
> -	      *ok_rptr, *nok_rptr, *comment_start, *value_parse_start;
> -	char *test_name_end;
> -
> -	ok_lookup_str = "ok ";
> -	nok_lookup_str = "not ok ";
> -
> -	lstart = strchrnul(record, ';');
> -
> -	if (*lstart == '\0') {
> -		igt_warn("kmsg truncated: output malformed (%m)\n");
> -		return -2;
> -	}
> -
> -	lstart++;
> -	while (isspace(*lstart))
> -		lstart++;
> -
> -	nok_rptr = strstr(lstart, nok_lookup_str);
> -	if (nok_rptr != NULL) {
> -		nok_rptr += strlen(nok_lookup_str);
> -		while (isdigit(*nok_rptr) || isspace(*nok_rptr) || *nok_rptr == '-')
> -			nok_rptr++;
> -		test_name_end = strncpy(test_name, nok_rptr, BUF_LEN);
> -		while (!isspace(*test_name_end))
> -			test_name_end++;
> -		*test_name_end = '\0';
> -		if (log_to_end(IGT_LOG_WARN, fd, record,
> -			       "%s", lstart) < 0)
> -			return -2;
> -		return -1;
> -	}
> -
> -	comment_start = strchrnul(lstart, '#');
> -
> -	/* Check if we're still in a subtest */
> -	if (*comment_start != '\0') {
> -		comment_start++;
> -		value_parse_start = comment_start;
> -
> -		if (lookup_value(value_parse_start, "fail: ") > 0) {
> -			if (log_to_end(IGT_LOG_WARN, fd, record,
> -				       "%s", lstart) < 0)
> -				return -2;
> -			return -1;
> -		}
> -	}
> -
> -	ok_rptr = strstr(lstart, ok_lookup_str);
> -	if (ok_rptr != NULL) {
> -		ok_rptr += strlen(ok_lookup_str);
> -		while (isdigit(*ok_rptr) || isspace(*ok_rptr) || *ok_rptr == '-')
> -			ok_rptr++;
> -		test_name_end = strncpy(test_name, ok_rptr, BUF_LEN);
> -		while (!isspace(*test_name_end))
> -			test_name_end++;
> -		*test_name_end = '\0';
> -		return 0;
> -	}
> -
> -	return 1;
> -}
> -
> -/**
> - * parse_tap_level:
> - * @fd: file descriptor
> - * @base_test_name: test_name from upper recursion level
> - * @test_count: test_count of this level
> - * @failed_tests: top level failed_tests pointer
> - * @found_tests: top level found_tests pointer
> - * @is_builtin: whether the KUnit module is built-in or not
> - *
> - * Returns:
> - * 0 if succeded
> - * -1 if error occurred
> - */
> -__maybe_unused
> -static int parse_tap_level(int fd, char *base_test_name, int test_count, bool *failed_tests,
> -			   bool *found_tests, bool is_builtin)
> -{
> -	char record[BUF_LEN + 1];
> -	struct ktap_test_results_element *r;
> -	int internal_test_count;
> -	char test_name[BUF_LEN + 1];
> -	char base_test_name_for_next_level[BUF_LEN + 1];
> -
> -	for (int i = 0; i < test_count; i++) {
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("error reading kmsg (%m)\n");
> -
> -			return -1;
> -		}
> -
> -		/* Sublevel found */
> -		if (tap_version_present(record, false))
> -		{
> -			internal_test_count = find_next_tap_subtest(fd, record, test_name,
> -								    is_builtin);
> -			switch (internal_test_count) {
> -			case -2:
> -				/* No more data to read */
> -				return -1;
> -			case -1:
> -				/* No test found */
> -				return -1;
> -			case 0:
> -				/* Tests found, but they're missing info */
> -				*found_tests = true;
> -				return -1;
> -			default:
> -				*found_tests = true;
> -
> -				memcpy(base_test_name_for_next_level, base_test_name, BUF_LEN);
> -				if (strlen(base_test_name_for_next_level) < BUF_LEN - 1 &&
> -				    base_test_name_for_next_level[0])
> -					strncat(base_test_name_for_next_level, DELIMITER,
> -						BUF_LEN - strlen(base_test_name_for_next_level));
> -				memcpy(base_test_name_for_next_level + strlen(base_test_name_for_next_level),
> -				       test_name, BUF_LEN - strlen(base_test_name_for_next_level));
> -
> -				if (parse_tap_level(fd, base_test_name_for_next_level,
> -						    internal_test_count, failed_tests, found_tests,
> -						    is_builtin) == -1)
> -					return -1;
> -				break;
> -			}
> -		}
> -
> -		switch (parse_kmsg_for_tap(fd, record, test_name)) {
> -		case -2:
> -			return -1;
> -		case -1:
> -			*failed_tests = true;
> -
> -			r = malloc(sizeof(*r));
> -
> -			memcpy(r->test_name, base_test_name, BUF_LEN);
> -			if (strlen(r->test_name) < BUF_LEN - 1)
> -				if (r->test_name[0])
> -					strncat(r->test_name, DELIMITER,
> -						BUF_LEN - strlen(r->test_name));
> -			memcpy(r->test_name + strlen(r->test_name), test_name,
> -			       BUF_LEN - strlen(r->test_name));
> -			r->test_name[BUF_LEN] = '\0';
> -
> -			r->passed = false;
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&r->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -
> -			test_name[0] = '\0';
> -			break;
> -		case 0:
> -			r = malloc(sizeof(*r));
> -
> -			memcpy(r->test_name, base_test_name, BUF_LEN);
> -			if (strlen(r->test_name) < BUF_LEN - 1)
> -				if (r->test_name[0])
> -					strncat(r->test_name, DELIMITER,
> -						BUF_LEN - strlen(r->test_name));
> -			memcpy(r->test_name + strlen(r->test_name), test_name,
> -			       BUF_LEN - strlen(r->test_name));
> -			r->test_name[BUF_LEN] = '\0';
> -
> -			r->passed = true;
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&r->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -
> -			test_name[0] = '\0';
> -			break;
> -		default:
> -			break;
> -		}
> -	}
> -	return 0;
> -}
> -
> -/**
> - * igt_ktap_parser:
> - *
> - * This function parses the output of a ktap script and passes it to main thread.
> - */
> -void *igt_ktap_parser(void *unused)
> -{
> -	char record[BUF_LEN + 1], *buf, *suite_name = NULL, *case_name = NULL;
> -	struct igt_ktap_results *ktap = NULL;
> -	int fd = ktap_args.fd;
> -	IGT_LIST_HEAD(list);
> -	int err;
> -
> -	ktap = igt_ktap_alloc(&list);
> -	if (igt_debug_on(!ktap))
> -		goto igt_ktap_parser_end;
> -
> -	while (err = read(fd, record, BUF_LEN), err > 0) {
> -		struct igt_ktap_result *r, *rn;
> -
> -		/* skip kmsg continuation lines */
> -		if (igt_debug_on(*record == ' '))
> -			continue;
> -
> -		/* NULL-terminate the record */
> -		record[err] = '\0';
> -
> -		/* detect start of log message, continue if not found */
> -		buf = strchrnul(record, ';');
> -		if (igt_debug_on(*buf == '\0'))
> -			continue;
> -		buf++;
> -
> -		err = igt_ktap_parse(buf, ktap);
> -
> -		/* parsing error */
> -		if (err && err != -EINPROGRESS)
> -			goto igt_ktap_parser_end;
> -
> -		igt_list_for_each_entry_safe(r, rn, &list, link) {
> -			struct ktap_test_results_element *result = NULL;
> -			int code = r->code;
> -
> -			if (code != IGT_EXIT_INVALID)
> -				result = calloc(1, sizeof(*result));
> -
> -			if (result) {
> -				snprintf(result->test_name, sizeof(result->test_name),
> -					 "%s-%s", r->suite_name, r->case_name);
> -
> -				if (code == IGT_EXIT_SUCCESS)
> -					result->passed = true;
> -			}
> -
> -			igt_list_del(&r->link);
> -			if (r->suite_name != suite_name) {
> -				free(suite_name);
> -				suite_name = r->suite_name;
> -			}
> -			if (r->case_name != case_name) {
> -				free(case_name);
> -				case_name = r->case_name;
> -			}
> -			free(r->msg);
> -			free(r);
> -
> -			/*
> -			 * no extra result record expected on start
> -			 * of parametrized test case -- skip it
> -			 */
> -			if (code == IGT_EXIT_INVALID)
> -				continue;
> -
> -			if (!result) {
> -				err = -ENOMEM;
> -				goto igt_ktap_parser_end;
> -			}
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&result->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -		}
> -
> -		/* end of KTAP report */
> -		if (!err)
> -			goto igt_ktap_parser_end;
> -	}
> -
> -	if (err < 0) {
> -		if (errno == EPIPE)
> -			igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -		else
> -			igt_warn("error reading kmsg (%m)\n");
> -	}
> -
> -igt_ktap_parser_end:
> -	free(suite_name);
> -	free(case_name);
> -
> -	if (!err)
> -		ktap_args.ret = IGT_EXIT_SUCCESS;
> -
> -	results.still_running = false;
> -
> -	if (ktap)
> -		igt_ktap_free(ktap);
> -
> -	return NULL;
> -}
> -
> -static pthread_t ktap_parser_thread;
> -
> -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin)
> -{
> -	IGT_INIT_LIST_HEAD(&results.list);
> -	pthread_mutex_init(&results.mutex, NULL);
> -	results.still_running = true;
> -
> -	ktap_args.fd = fd;
> -	ktap_args.is_builtin = is_builtin;
> -	ktap_args.ret = IGT_EXIT_FAILURE;
> -	pthread_create(&ktap_parser_thread, NULL, igt_ktap_parser, NULL);
> -
> -	return &results;
> -}
> -
> -void ktap_parser_cancel(void)
> -{
> -	pthread_cancel(ktap_parser_thread);
> -}
> -
> -int ktap_parser_stop(void)
> -{
> -	pthread_join(ktap_parser_thread, NULL);
> -	return ktap_args.ret;
> -}
> diff --git a/lib/igt_ktap.h b/lib/igt_ktap.h
> index 6f8da3eab6..c422636bfc 100644
> --- a/lib/igt_ktap.h
> +++ b/lib/igt_ktap.h
> @@ -27,8 +27,6 @@
>  
>  #define BUF_LEN 4096
>  
> -#include <pthread.h>
> -
>  #include "igt_list.h"
>  
>  struct igt_ktap_result {
> @@ -45,24 +43,4 @@ struct igt_ktap_results *igt_ktap_alloc(struct igt_list_head *results);
>  int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap);
>  void igt_ktap_free(struct igt_ktap_results *ktap);
>  
> -void *igt_ktap_parser(void *unused);
> -
> -typedef struct ktap_test_results_element {
> -	char test_name[BUF_LEN + 1];
> -	bool passed;
> -	struct igt_list_head link;
> -} ktap_test_results_element;
> -
> -struct ktap_test_results {
> -	struct igt_list_head list;
> -	pthread_mutex_t mutex;
> -	bool still_running;
> -};
> -
> -
> -
> -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin);
> -void ktap_parser_cancel(void);
> -int ktap_parser_stop(void);
> -
>  #endif /* IGT_KTAP_H */


More information about the igt-dev mailing list