[igt-dev] [PATCH i-g-t 5/8] Change logic of ktap parser to run on a thread
Mauro Carvalho Chehab
mauro.chehab at linux.intel.com
Mon Jun 5 11:03:03 UTC 2023
On Mon, 5 Jun 2023 12:47:13 +0200
Dominik Karol Piatkowski <dominik.karol.piatkowski at intel.com> wrote:
> The ktap parser should be listening and parsing messages as the tests
> are executed, and not after the end of module load.
>
> v1 -> v2:
> - fix coding style
> - remove usleep
> - add error check logic
> - follow the structure of igt_kselftests more closely
>
> v2 -> v3:
> - fixed sublevel issues by rewriting tap parser flow
>
> Signed-off-by: Dominik Karol Piątkowski <dominik.karol.piatkowski at intel.com>
> Cc: Janusz Krzysztofik <janusz.krzysztofik at linux.intel.com>
> Cc: Mauro Carvalho Chehab <mauro.chehab at linux.intel.com>
Acked-by: Mauro Carvalho Chehab <mchehab at kernel.org>
> ---
> lib/igt_kmod.c | 99 ++++++++----
> lib/igt_ktap.c | 431 ++++++++++++++++++++++++++++++++++++++++---------
> lib/igt_ktap.h | 21 ++-
> 3 files changed, 440 insertions(+), 111 deletions(-)
>
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index 21e801bd..8cb9cb2e 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -758,10 +758,11 @@ int igt_kunit(const char *module_name, const char *opts)
> {
> struct igt_ktest tst;
> struct kmod_module *kunit_kmod;
> - char record[BUF_LEN + 1];
> FILE *f;
> bool is_builtin;
> int ret;
> + struct ktap_test_results *results;
> + struct ktap_test_results_element *temp;
>
> ret = IGT_EXIT_INVALID;
>
> @@ -771,55 +772,83 @@ int igt_kunit(const char *module_name, const char *opts)
> return ret;
> }
>
> - if (igt_ktest_begin(&tst) != 0) {
> - igt_warn("Unable to begin ktest for %s\n", module_name);
> + igt_fixture {
> + if (igt_ktest_begin(&tst) != 0) {
> + igt_warn("Unable to begin ktest for %s\n", module_name);
>
> - igt_ktest_fini(&tst);
> - return ret;
> - }
> + igt_ktest_fini(&tst);
> + return ret;
> + }
>
> - if (tst.kmsg < 0) {
> - igt_warn("Could not open /dev/kmsg\n");
> - goto unload;
> - }
> + if (tst.kmsg < 0) {
> + igt_warn("Could not open /dev/kmsg\n");
> + goto unload;
> + }
>
> - if (lseek(tst.kmsg, 0, SEEK_END)) {
> - igt_warn("Could not seek the end of /dev/kmsg\n");
> - goto unload;
> - }
> + if (lseek(tst.kmsg, 0, SEEK_END)) {
> + igt_warn("Could not seek the end of /dev/kmsg\n");
> + goto unload;
> + }
>
> - f = fdopen(tst.kmsg, "r");
> + f = fdopen(tst.kmsg, "r");
>
> - if (f == NULL) {
> - igt_warn("Could not turn /dev/kmsg file descriptor into a FILE pointer\n");
> - goto unload;
> - }
> + if (f == NULL) {
> + igt_warn("Could not turn /dev/kmsg file descriptor into a FILE pointer\n");
> + goto unload;
> + }
> +
> + /* The KUnit module is required for running any KUnit tests */
> + if (igt_kmod_load("kunit", NULL) != 0 ||
> + kmod_module_new_from_name(kmod_ctx(), "kunit", &kunit_kmod) != 0) {
> + igt_warn("Unable to load KUnit\n");
> + igt_fail(IGT_EXIT_FAILURE);
> + }
>
> - /* The KUnit module is required for running any KUnit tests */
> - if (igt_kmod_load("kunit", NULL) != 0 ||
> - kmod_module_new_from_name(kmod_ctx(), "kunit", &kunit_kmod) != 0) {
> - igt_warn("Unable to load KUnit\n");
> - igt_fail(IGT_EXIT_FAILURE);
> + is_builtin = kmod_module_get_initstate(kunit_kmod) == KMOD_MODULE_BUILTIN;
> +
> + results = ktap_parser_start(f, is_builtin);
> +
> + if (igt_kmod_load(module_name, opts) != 0) {
> + igt_warn("Unable to load %s module\n", module_name);
> + ret = ktap_parser_stop();
> + igt_fail(IGT_EXIT_FAILURE);
> + }
> }
>
> - is_builtin = kmod_module_get_initstate(kunit_kmod) == KMOD_MODULE_BUILTIN;
> + while (READ_ONCE(results->still_running) || READ_ONCE(results->head) != NULL)
> + {
> + if (READ_ONCE(results->head) != NULL) {
> + pthread_mutex_lock(&results->mutex);
>
> - if (igt_kmod_load(module_name, opts) != 0) {
> - igt_warn("Unable to load %s module\n", module_name);
> - igt_fail(IGT_EXIT_FAILURE);
> + igt_subtest(results->head->test_name) {
> + if (READ_ONCE(results->head->passed))
> + igt_success();
> + else
> + igt_fail(IGT_EXIT_FAILURE);
> + }
> +
> + temp = results->head;
> + results->head = results->head->next;
> + free(temp);
> +
> + pthread_mutex_unlock(&results->mutex);
> + }
> }
>
> - ret = igt_ktap_parser(f, record, is_builtin);
> - if (ret != 0)
> - ret = IGT_EXIT_ABORT;
> unload:
> - igt_ktest_end(&tst);
> + igt_fixture {
> + igt_ktest_end(&tst);
>
> - igt_ktest_fini(&tst);
> + igt_ktest_fini(&tst);
> +
> + ret = ktap_parser_stop();
>
> - if (ret == 0)
> - igt_success();
> + if (ret != 0)
> + ret = IGT_EXIT_ABORT;
>
> + if (ret == 0)
> + igt_success();
> + }
> return ret;
> }
>
> diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
> index 117598fa..d8dec52d 100644
> --- a/lib/igt_ktap.c
> +++ b/lib/igt_ktap.c
> @@ -5,11 +5,25 @@
>
> #include <ctype.h>
> #include <limits.h>
> +#include <libkmod.h>
> +#include <pthread.h>
> +#include <errno.h>
>
> #include "igt_aux.h"
> #include "igt_core.h"
> #include "igt_ktap.h"
>
> +#define DELIMITER "__"
> +
> +struct ktap_parser_args {
> + FILE *fp;
> + bool is_builtin;
> + volatile bool is_running;
> + int ret;
> +} ktap_args;
> +
> +static struct ktap_test_results results;
> +
> static int log_to_end(enum igt_log_level level, FILE *f,
> char *record, const char *format, ...) __attribute__((format(printf, 4, 5)));
>
> @@ -30,6 +44,14 @@ static int log_to_end(enum igt_log_level level, FILE *f,
> {
> va_list args;
> const char *lend;
> + int f_fd = fileno(f);
> +
> + /* 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);
> @@ -38,10 +60,29 @@ static int log_to_end(enum igt_log_level level, FILE *f,
> lend = strchrnul(record, '\n');
> while (*lend == '\0') {
> igt_log(IGT_LOG_DOMAIN, level, "%s", record);
> - if (fgets(record, BUF_LEN, f) == NULL) {
> +
> + while (read(f_fd, record, BUF_LEN) < 0) {
> + if (!READ_ONCE(ktap_args.is_running)) {
> + igt_warn("ktap parser stopped\n");
> + return -2;
> + }
> +
> + 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");
> + return -2;
> + }
> +
> + if (errno == EAGAIN)
> + /* No records available */
> + continue;
> +
> igt_warn("kmsg truncated: unknown error (%m)\n");
> return -2;
> }
> +
> lend = strchrnul(record, '\n');
> }
> return 0;
> @@ -65,7 +106,7 @@ static long lookup_value(const char *haystack, const char *needle)
> if (needle_rptr == NULL)
> return -1;
>
> - /* skip search string and whitespaces after it */
> + /* Skip search string and whitespaces after it */
> needle_rptr += strlen(needle);
>
> num = strtol(needle_rptr, &needle_end, 10);
> @@ -79,6 +120,41 @@ static long lookup_value(const char *haystack, const char *needle)
> 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:
> * @fp: FILE pointer
> @@ -91,11 +167,12 @@ static long lookup_value(const char *haystack, const char *needle)
> * -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(FILE *fp, char *record, bool is_builtin)
> +static int find_next_tap_subtest(FILE *fp, char *record, char *test_name, bool is_builtin)
> {
> - const char *test_lookup_str, *subtest_lookup_str, *name_rptr, *version_rptr;
> - char test_name[BUF_LEN + 1];
> + const char *test_lookup_str, *subtest_lookup_str, *name_rptr;
> long test_count;
> + int fp_fd = fileno(fp);
> + char *cutoff;
>
> test_name[0] = '\0';
> test_name[BUF_LEN] = '\0';
> @@ -103,21 +180,28 @@ static int find_next_tap_subtest(FILE *fp, char *record, bool is_builtin)
> test_lookup_str = " subtest: ";
> subtest_lookup_str = " test: ";
>
> - /*
> - * "(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
> - */
> + if (!tap_version_present(record, true))
> + return -1;
> +
> if (is_builtin) {
> - version_rptr = strcasestr(record, "TAP version ");
> - if (version_rptr == NULL)
> - return -1;
> + while (read(fp_fd, record, BUF_LEN) < 0) {
> + if (!READ_ONCE(ktap_args.is_running)) {
> + igt_warn("ktap parser stopped\n");
> + return -2;
> + }
> +
> + if (errno == EINTR)
> + continue;
>
> - igt_info("%s", version_rptr);
> + if (errno == EPIPE) {
> + igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> + return -2;
> + }
> +
> + if (errno == EAGAIN)
> + /* No records available */
> + continue;
>
> - if (fgets(record, BUF_LEN, fp) == NULL) {
> igt_warn("kmsg truncated: unknown error (%m)\n");
> return -2;
> }
> @@ -134,22 +218,45 @@ static int find_next_tap_subtest(FILE *fp, char *record, bool is_builtin)
>
> if (name_rptr == NULL) {
> if (!is_builtin)
> - /* we've probably found nothing */
> + /* We've probably found nothing */
> return -1;
> igt_info("Missing test name\n");
> } else {
> strncpy(test_name, name_rptr, BUF_LEN);
> - if (fgets(record, BUF_LEN, fp) == NULL) {
> + /* Cutoff after newline character, in order to not display garbage */
> + cutoff = strchr(test_name, '\n');
> + if (cutoff)
> + cutoff[0] = '\0';
> +
> + while (read(fp_fd, record, BUF_LEN) < 0) {
> + if (!READ_ONCE(ktap_args.is_running)) {
> + igt_warn("ktap parser stopped\n");
> + return -2;
> + }
> +
> + 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");
> + return -2;
> + }
> +
> + if (errno == EAGAIN)
> + /* No records available */
> + continue;
> +
> igt_warn("kmsg truncated: unknown error (%m)\n");
> return -2;
> }
> - /* now we can be sure we found tests */
> +
> + /* 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
> + * 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, "..");
> @@ -159,7 +266,7 @@ static int find_next_tap_subtest(FILE *fp, char *record, bool is_builtin)
> if (test_name[0] == '\0')
> return 0;
> if (log_to_end(IGT_LOG_INFO, fp, record,
> - "Running some tests in: %s",
> + "Running some tests in: %s\n",
> test_name) < 0)
> return -2;
> return 0;
> @@ -169,7 +276,7 @@ static int find_next_tap_subtest(FILE *fp, char *record, bool is_builtin)
> }
>
> if (log_to_end(IGT_LOG_INFO, fp, record,
> - "Executing %ld tests in: %s",
> + "Executing %ld tests in: %s\n",
> test_count, test_name) < 0)
> return -2;
>
> @@ -177,7 +284,7 @@ static int find_next_tap_subtest(FILE *fp, char *record, bool is_builtin)
> }
>
> /**
> - * find_next_tap_test:
> + * parse_kmsg_for_tap:
> * @fp: FILE pointer
> * @record: buffer used to read fp
> * @test_name: buffer to store the test name
> @@ -225,7 +332,7 @@ static int parse_kmsg_for_tap(FILE *fp, char *record, char *test_name)
>
> comment_start = strchrnul(lstart, '#');
>
> - /* check if we're still in a subtest */
> + /* Check if we're still in a subtest */
> if (*comment_start != '\0') {
> comment_start++;
> value_parse_start = comment_start;
> @@ -254,81 +361,255 @@ static int parse_kmsg_for_tap(FILE *fp, char *record, char *test_name)
> }
>
> /**
> - * igt_ktap_parser:
> + * parse_tap_level:
> * @fp: FILE pointer
> - * @record: buffer used to read fp
> + * @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
> *
> - * This function parses the output of a ktap script and prints the test results,
> - * as well as any other output to stdout.
> - *
> - * Returns: IGT default codes
> + * Returns:
> + * 0 if succeded
> + * -1 if error occurred
> */
> -int igt_ktap_parser(FILE *fp, char *record, bool is_builtin)
> +static int parse_tap_level(FILE *fp, char *base_test_name, int test_count, bool *failed_tests,
> + bool *found_tests, bool is_builtin)
> {
> + int fp_fd = fileno(fp);
> + char record[BUF_LEN + 1];
> + struct ktap_test_results_element *r, *temp;
> + int internal_test_count;
> char test_name[BUF_LEN + 1];
> - bool failed_tests, found_tests;
> - int sublevel = 0;
> + char base_test_name_for_next_level[BUF_LEN + 1];
>
> - test_name[0] = '\0';
> - test_name[BUF_LEN] = '\0';
> + for (int i = 0; i < test_count; i++) {
> + while (read(fp_fd, record, BUF_LEN) < 0) {
> + if (!READ_ONCE(ktap_args.is_running)) {
> + igt_warn("ktap parser stopped\n");
> + return -1;
> + }
>
> - failed_tests = false;
> - found_tests = false;
> + if (errno == EINTR)
> + continue;
>
> - while (sublevel >= 0) {
> - if (fgets(record, BUF_LEN, fp) == NULL) {
> - if (!found_tests)
> - igt_warn("kmsg truncated: unknown error (%m)\n");
> - break;
> + if (errno == EAGAIN)
> + /* No records available */
> + continue;
> +
> + if (errno == EPIPE) {
> + igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> + return -1;
> + }
> +
> + igt_warn("kmsg truncated: unknown error (%m)\n");
> + return -1;
> }
>
> - switch (find_next_tap_subtest(fp, record, is_builtin)) {
> - case -2:
> - /* no more data to read */
> - return IGT_EXIT_FAILURE;
> - case -1:
> - /* no test found, so we keep parsing */
> - break;
> - case 0:
> - /*
> - * tests found, but they're missing info, so we might
> - * have read into test output
> - */
> - found_tests = true;
> - sublevel++;
> - break;
> - default:
> - if (fgets(record, BUF_LEN, fp) == NULL) {
> - igt_warn("kmsg truncated: unknown error (%m)\n");
> - return -2;
> + /* Sublevel found */
> + if (tap_version_present(record, false))
> + {
> + internal_test_count = find_next_tap_subtest(fp, 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(fp, base_test_name_for_next_level,
> + internal_test_count, failed_tests, found_tests,
> + is_builtin) == -1)
> + return -1;
> + break;
> }
> - found_tests = true;
> - sublevel++;
> - break;
> }
>
> switch (parse_kmsg_for_tap(fp, record, test_name)) {
> case -2:
> - return IGT_EXIT_FAILURE;
> + return -1;
> case -1:
> - sublevel--;
> - failed_tests = true;
> - igt_subtest(test_name)
> - igt_fail(IGT_EXIT_FAILURE);
> + *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;
> + r->next = NULL;
> +
> + pthread_mutex_lock(&results.mutex);
> + if (results.head == NULL) {
> + results.head = r;
> + } else {
> + temp = results.head;
> + while (temp->next != NULL)
> + temp = temp->next;
> + temp->next = r;
> + }
> + pthread_mutex_unlock(&results.mutex);
> +
> test_name[0] = '\0';
> break;
> - case 0: /* fallthrough */
> - igt_subtest(test_name)
> - igt_success();
> + 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;
> + r->next = NULL;
> +
> + pthread_mutex_lock(&results.mutex);
> + if (results.head == NULL) {
> + results.head = r;
> + } else {
> + temp = results.head;
> + while (temp->next != NULL)
> + temp = temp->next;
> + temp->next = r;
> + }
> + 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)
> +{
> + FILE *fp = ktap_args.fp;
> + int fp_fd = fileno(fp);
> + char record[BUF_LEN + 1];
> + bool is_builtin = ktap_args.is_builtin;
> + char test_name[BUF_LEN + 1];
> + bool failed_tests, found_tests;
> + int test_count;
> +
> + failed_tests = false;
> + found_tests = false;
> +
> + if (!READ_ONCE(ktap_args.is_running))
> + goto igt_ktap_parser_end;
> +
> +igt_ktap_parser_start:
> + test_name[0] = '\0';
> + test_name[BUF_LEN] = '\0';
> +
> + while (read(fp_fd, record, BUF_LEN) < 0) {
> + if (!READ_ONCE(ktap_args.is_running)) {
> + igt_warn("ktap parser stopped\n");
> + goto igt_ktap_parser_end;
> + }
> +
> + if (errno == EAGAIN)
> + /* No records available */
> + continue;
> +
> + 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");
> + goto igt_ktap_parser_end;
> + }
> + }
> +
> + test_count = find_next_tap_subtest(fp, record, test_name, is_builtin);
> +
> + switch (test_count) {
> + case -2:
> + /* Problems while reading the file */
> + goto igt_ktap_parser_end;
> + case -1:
> + /* No test found */
> + goto igt_ktap_parser_start;
> + case 0:
> + /* Tests found, but they're missing info */
> + found_tests = true;
> + goto igt_ktap_parser_end;
> + default:
> + found_tests = true;
> +
> + if (parse_tap_level(fp, test_name, test_count, &failed_tests, &found_tests,
> + is_builtin) == -1)
> + goto igt_ktap_parser_end;
> +
> + break;
> + }
> +
> + /* Parse topmost level */
> + test_name[0] = '\0';
> + parse_tap_level(fp, test_name, test_count, &failed_tests, &found_tests, is_builtin);
> +
> +igt_ktap_parser_end:
> + results.still_running = false;
>
> if (failed_tests || !found_tests)
> - return IGT_EXIT_FAILURE;
> + ktap_args.ret = IGT_EXIT_FAILURE;
> + else
> + ktap_args.ret = IGT_EXIT_SUCCESS;
> +
> + return NULL;
> +}
> +
> +static pthread_t ktap_parser_thread;
> +
> +struct ktap_test_results *ktap_parser_start(FILE *fp, bool is_builtin)
> +{
> + results.head = NULL;
> + pthread_mutex_init(&results.mutex, NULL);
> + results.still_running = true;
>
> - return IGT_EXIT_SUCCESS;
> + ktap_args.fp = fp;
> + ktap_args.is_builtin = is_builtin;
> + ktap_args.is_running = true;
> + pthread_create(&ktap_parser_thread, NULL, igt_ktap_parser, NULL);
> +
> + return &results;
> +}
> +
> +int ktap_parser_stop(void)
> +{
> + ktap_args.is_running = false;
> + pthread_join(ktap_parser_thread, NULL);
> + return ktap_args.ret;
> }
> diff --git a/lib/igt_ktap.h b/lib/igt_ktap.h
> index b2f69df2..34fe0957 100644
> --- a/lib/igt_ktap.h
> +++ b/lib/igt_ktap.h
> @@ -26,6 +26,25 @@
>
> #define BUF_LEN 4096
>
> -int igt_ktap_parser(FILE *fp, char *record, bool is_builtin);
> +#include <pthread.h>
> +
> +void *igt_ktap_parser(void *unused);
> +
> +typedef struct ktap_test_results_element {
> + char test_name[BUF_LEN + 1];
> + bool passed;
> + struct ktap_test_results_element *next;
> +} ktap_test_results_element;
> +
> +struct ktap_test_results {
> + ktap_test_results_element *head;
> + pthread_mutex_t mutex;
> + bool still_running;
> +};
> +
> +
> +
> +struct ktap_test_results *ktap_parser_start(FILE *fp, bool is_builtin);
> +int ktap_parser_stop(void);
>
> #endif /* IGT_KTAP_H */
More information about the igt-dev
mailing list