[igt-dev] [PATCH i-g-t 1/7] lib/runnercomms: Structured communication from tests to igt_runner
Kamil Konieczny
kamil.konieczny at linux.intel.com
Fri Oct 28 13:42:59 UTC 2022
On 2022-10-10 at 17:57:02 +0300, Petri Latvala wrote:
> Instead of letting the tests output their logs as text and parsing
> that in the runner during and after test execution, introduce a
> structured log format that is passed through a UNIX datagram socket.
>
> This patch only introduces the datagram format and helpers for
> creating/reading them. Key points about the format:
>
> It's binary, and has some amount of forwards and backwards
> compatibility. Passing through UNIX datagram sockets makes the
> communication atomic which avoids message interleaving between child
> process logging. (Threaded logging already gets correct serialization
> through the use of a mutex, no change there.)
>
> Having atomic logging also gives the possibility of igt_runner
> injecting messages into test logs without having to worry whether the
> stdout pipe is still in the middle of printing a subtest completion
> message for example.
>
> On-disk storage of datagrams will use a canary chunk to verify
> correctly sized reads of datagrams, and to ensure that the endianness
> of the reader and the dump match.
>
> Signed-off-by: Petri Latvala <petri.latvala at intel.com>
> Cc: Arkadiusz Hiler <arek at hiler.eu>
Acked-by: Kamil Konieczny <kamil.konieczny at linux.intel.com>
> ---
> lib/meson.build | 1 +
> lib/runnercomms.c | 635 ++++++++++++++++++++++++++++++++++++++++++++++
> lib/runnercomms.h | 259 +++++++++++++++++++
> 3 files changed, 895 insertions(+)
> create mode 100644 lib/runnercomms.c
> create mode 100644 lib/runnercomms.h
>
> diff --git a/lib/meson.build b/lib/meson.build
> index c665bd25..0f8e862f 100644
> --- a/lib/meson.build
> +++ b/lib/meson.build
> @@ -66,6 +66,7 @@ lib_sources = [
> 'rendercopy_gen7.c',
> 'rendercopy_gen8.c',
> 'rendercopy_gen9.c',
> + 'runnercomms.c',
> 'sw_sync.c',
> 'intel_aux_pgtable.c',
> 'intel_reg_map.c',
> diff --git a/lib/runnercomms.c b/lib/runnercomms.c
> new file mode 100644
> index 00000000..344312bd
> --- /dev/null
> +++ b/lib/runnercomms.c
> @@ -0,0 +1,635 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2022 Intel Corporation
> + */
> +
> +#include <assert.h>
> +#include <signal.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/mman.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +
> +#include "igt_aux.h"
> +#include "runnercomms.h"
> +
> +/**
> + * SECTION:runnercomms
> + * @short_description: Structured communication to igt_runner
> + * @title: runnercomms
> + * @include: runnercomms.h
> + *
> + * This library provides means for the tests to communicate to
> + * igt_runner with a formally specified protocol, avoiding
> + * shortcomings and pain points of text-based communication.
> + */
> +
> +static sig_atomic_t runner_socket_fd = -1;
> +
> +/**
> + * set_runner_socket:
> + * @fd: socket connected to runner
> + *
> + * If the passed fd is a valid socket, globally sets it to be the fd
> + * to use to talk to igt_runner.
> + */
> +void set_runner_socket(int fd)
> +{
> + struct stat sb;
> +
> + if (fstat(fd, &sb))
> + return;
> +
> + if (!S_ISSOCK(sb.st_mode))
> + return;
> +
> + /*
> + * We only sanity-check that the fd is a socket. We don't
> + * check that it's a datagram socket etc.
> + */
> +
> + runner_socket_fd = fd;
> +}
> +
> +/**
> + * runner_connected:
> + *
> + * Returns whether set_runner_socket has been called with a valid
> + * socket fd. Note: Will be true forever after that point. This
> + * function is used to mainly determine whether log strings will be
> + * output to the socket or to stdout/stderr and that cannot be changed
> + * even if the socket is lost midway.
> + */
> +bool runner_connected(void)
> +{
> + return runner_socket_fd >= 0;
> +}
> +
> +/**
> + * send_to_runner:
> + * @packet: packet to send
> + *
> + * Sends the given communications packet to igt_runner. Calls free()
> + * on the packet, don't reuse it.
> + */
> +void send_to_runner(struct runnerpacket *packet)
> +{
> + if (runner_connected())
> + write(runner_socket_fd, packet, packet->size);
> + free(packet);
> +}
> +
> +/* If enough data left, copy the data to dst, advance p, reduce size */
> +static void read_integer(void* dst, size_t bytes, const char **p, uint32_t *size)
> +{
> + if (*size < bytes) {
> + *size = 0;
> + return;
> + }
> +
> + memcpy(dst, *p, bytes);
> + *p += bytes;
> + *size -= bytes;
> +}
> +
> +/* If nul-termination can be found, set dststr to point to the cstring, advance p, reduce size */
> +static void read_cstring(const char **dststr, const char **p, uint32_t *size)
> +{
> + const char *end;
> +
> + end = memchr(*p, '\0', *size);
> + if (end == NULL) {
> + *size = 0;
> + return;
> + }
> +
> + *dststr = *p;
> + *size -= end - *p + 1;
> + *p = end + 1;
> +}
> +
> +/**
> + * read_runnerpacket:
> + * @packet: runner communications packet to read
> + *
> + * Checks that the internal data of the communications packet is valid
> + * and the contents can safely be inspected without further checking
> + * for out-of-bounds etc. Constructs a runnerpacket_read_helper which
> + * will, for c-style strings, point to various sub-values directly in
> + * the #data field within @packet. Those are valid only as long as
> + * @packet is valid.
> + *
> + * Returns: An appropriately constructed runnerpacket_read_helper. On
> + * data validation errors, the #type of the returned value will be
> + * #PACKETTYPE_INVALID.
> + */
> +runnerpacket_read_helper read_runnerpacket(const struct runnerpacket *packet)
> +{
> + runnerpacket_read_helper ret = {};
> + uint32_t sizeleft;
> + const char *p;
> +
> + if (packet->size < sizeof(*packet)) {
> + ret.type = PACKETTYPE_INVALID;
> + return ret;
> + }
> +
> + ret.type = packet->type;
> + sizeleft = packet->size - sizeof(*packet);
> + p = packet->data;
> +
> + switch (packet->type) {
> + case PACKETTYPE_LOG:
> + read_integer(&ret.log.stream, sizeof(ret.log.stream), &p, &sizeleft);
> + read_cstring(&ret.log.text, &p, &sizeleft);
> +
> + if (ret.log.text == NULL)
> + ret.type = PACKETTYPE_INVALID;
> +
> + break;
> + case PACKETTYPE_EXEC:
> + read_cstring(&ret.exec.cmdline, &p, &sizeleft);
> +
> + if (ret.exec.cmdline == NULL)
> + ret.type = PACKETTYPE_INVALID;
> +
> + break;
> + case PACKETTYPE_EXIT:
> + read_integer(&ret.exit.exitcode, sizeof(ret.exit.exitcode), &p, &sizeleft);
> + read_cstring(&ret.exit.timeused, &p, &sizeleft);
> +
> + break;
> + case PACKETTYPE_SUBTEST_START:
> + read_cstring(&ret.subteststart.name, &p, &sizeleft);
> +
> + if (ret.subteststart.name == NULL)
> + ret.type = PACKETTYPE_INVALID;
> +
> + break;
> + case PACKETTYPE_SUBTEST_RESULT:
> + read_cstring(&ret.subtestresult.name, &p, &sizeleft);
> + read_cstring(&ret.subtestresult.result, &p, &sizeleft);
> + read_cstring(&ret.subtestresult.timeused, &p, &sizeleft);
> + read_cstring(&ret.subtestresult.reason, &p, &sizeleft);
> +
> + if (ret.subtestresult.name == NULL ||
> + ret.subtestresult.result == NULL)
> + ret.type = PACKETTYPE_INVALID;
> +
> + break;
> + case PACKETTYPE_DYNAMIC_SUBTEST_START:
> + read_cstring(&ret.dynamicsubteststart.name, &p, &sizeleft);
> +
> + if (ret.dynamicsubteststart.name == NULL)
> + ret.type = PACKETTYPE_INVALID;
> +
> + break;
> + case PACKETTYPE_DYNAMIC_SUBTEST_RESULT:
> + read_cstring(&ret.dynamicsubtestresult.name, &p, &sizeleft);
> + read_cstring(&ret.dynamicsubtestresult.result, &p, &sizeleft);
> + read_cstring(&ret.dynamicsubtestresult.timeused, &p, &sizeleft);
> + read_cstring(&ret.dynamicsubtestresult.reason, &p, &sizeleft);
> +
> + if (ret.dynamicsubtestresult.name == NULL ||
> + ret.dynamicsubtestresult.result == NULL)
> + ret.type = PACKETTYPE_INVALID;
> +
> + break;
> + case PACKETTYPE_VERSIONSTRING:
> + read_cstring(&ret.versionstring.text, &p, &sizeleft);
> +
> + if (ret.versionstring.text == NULL)
> + ret.type = PACKETTYPE_INVALID;
> +
> + break;
> + case PACKETTYPE_RESULT_OVERRIDE:
> + read_cstring(&ret.resultoverride.result, &p, &sizeleft);
> +
> + if (ret.resultoverride.result == NULL)
> + ret.type = PACKETTYPE_INVALID;
> +
> + break;
> + default:
> + ret.type = PACKETTYPE_INVALID;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +struct runnerpacket *runnerpacket_log(uint8_t stream, const char *text)
> +{
> + struct runnerpacket *packet;
> + uint32_t size;
> + char *p;
> +
> + size = sizeof(struct runnerpacket) + sizeof(stream) + strlen(text) + 1;
> + packet = malloc(size);
> +
> + packet->size = size;
> + packet->type = PACKETTYPE_LOG;
> + packet->senderpid = getpid();
> + packet->sendertid = gettid();
> +
> + p = packet->data;
> +
> + memcpy(p, &stream, sizeof(stream));
> + p += sizeof(stream);
> +
> + strcpy(p, text);
> + p += strlen(text) + 1;
> +
> + return packet;
> +}
> +
> +struct runnerpacket *runnerpacket_exec(char **argv)
> +{
> + struct runnerpacket *packet;
> + uint32_t size;
> + char *p;
> + int i;
> +
> + size = sizeof(struct runnerpacket);
> +
> + for (i = 0; argv[i] != NULL; i++)
> + size += strlen(argv[i]) + 1; // followed by a space of \0 so +1 either way for each
> +
> + packet = malloc(size);
> +
> + packet->size = size;
> + packet->type = PACKETTYPE_EXEC;
> + packet->senderpid = getpid();
> + packet->sendertid = gettid();
> +
> + p = packet->data;
> +
> + for (i = 0; argv[i] != NULL; i++) {
> + if (i != 0)
> + *p++ = ' ';
> +
> + strcpy(p, argv[i]);
> + p += strlen(argv[i]);
> + }
> + p[0] = '\0';
> +
> + return packet;
> +}
> +
> +struct runnerpacket *runnerpacket_exit(int32_t exitcode, const char *timeused)
> +{
> + struct runnerpacket *packet;
> + uint32_t size;
> + char *p;
> +
> + size = sizeof(struct runnerpacket) + sizeof(exitcode) + strlen(timeused) + 1;
> + packet = malloc(size);
> +
> + packet->size = size;
> + packet->type = PACKETTYPE_EXIT;
> + packet->senderpid = getpid();
> + packet->sendertid = gettid();
> +
> + p = packet->data;
> +
> + memcpy(p, &exitcode, sizeof(exitcode));
> + p += sizeof(exitcode);
> +
> + strcpy(p, timeused);
> + p += strlen(timeused) + 1;
> +
> + return packet;
> +}
> +
> +struct runnerpacket *runnerpacket_subtest_start(const char *name)
> +{
> + struct runnerpacket *packet;
> + uint32_t size;
> + char *p;
> +
> + size = sizeof(struct runnerpacket) + strlen(name) + 1;
> + packet = malloc(size);
> +
> + packet->size = size;
> + packet->type = PACKETTYPE_SUBTEST_START;
> + packet->senderpid = getpid();
> + packet->sendertid = gettid();
> +
> + p = packet->data;
> +
> + strcpy(p, name);
> + p += strlen(name) + 1;
> +
> + return packet;
> +}
> +
> +struct runnerpacket *runnerpacket_subtest_result(const char *name, const char *result,
> + const char *timeused, const char *reason)
> +{
> + struct runnerpacket *packet;
> + uint32_t size;
> + char *p;
> +
> + if (reason == NULL)
> + reason = "";
> +
> + size = sizeof(struct runnerpacket) + strlen(name) + strlen(result) + strlen(timeused) + strlen(reason) + 4;
> + packet = malloc(size);
> +
> + packet->size = size;
> + packet->type = PACKETTYPE_SUBTEST_RESULT;
> + packet->senderpid = getpid();
> + packet->sendertid = gettid();
> +
> + p = packet->data;
> +
> + strcpy(p, name);
> + p += strlen(name) + 1;
> +
> + strcpy(p, result);
> + p += strlen(result) + 1;
> +
> + strcpy(p, timeused);
> + p += strlen(timeused) + 1;
> +
> + strcpy(p, reason);
> + p += strlen(reason) + 1;
> +
> + return packet;
> +}
> +
> +struct runnerpacket *runnerpacket_dynamic_subtest_start(const char *name)
> +{
> + struct runnerpacket *packet;
> + uint32_t size;
> + char *p;
> +
> + size = sizeof(struct runnerpacket) + strlen(name) + 1;
> + packet = malloc(size);
> +
> + packet->size = size;
> + packet->type = PACKETTYPE_DYNAMIC_SUBTEST_START;
> + packet->senderpid = getpid();
> + packet->sendertid = gettid();
> +
> + p = packet->data;
> +
> + strcpy(p, name);
> + p += strlen(name) + 1;
> +
> + return packet;
> +}
> +
> +struct runnerpacket *runnerpacket_dynamic_subtest_result(const char *name, const char *result,
> + const char *timeused, const char *reason)
> +{
> + struct runnerpacket *packet;
> + uint32_t size;
> + char *p;
> +
> + if (reason == NULL)
> + reason = "";
> +
> + size = sizeof(struct runnerpacket) + strlen(name) + strlen(result) + strlen(timeused) + strlen(reason) + 4;
> + packet = malloc(size);
> +
> + packet->size = size;
> + packet->type = PACKETTYPE_DYNAMIC_SUBTEST_RESULT;
> + packet->senderpid = getpid();
> + packet->sendertid = gettid();
> +
> + p = packet->data;
> +
> + strcpy(p, name);
> + p += strlen(name) + 1;
> +
> + strcpy(p, result);
> + p += strlen(result) + 1;
> +
> + strcpy(p, timeused);
> + p += strlen(timeused) + 1;
> +
> + strcpy(p, reason);
> + p += strlen(reason) + 1;
> +
> + return packet;
> +}
> +
> +struct runnerpacket *runnerpacket_versionstring(const char *text)
> +{
> + struct runnerpacket *packet;
> + uint32_t size;
> + char *p;
> +
> + size = sizeof(struct runnerpacket) + strlen(text) + 1;
> + packet = malloc(size);
> +
> + packet->size = size;
> + packet->type = PACKETTYPE_VERSIONSTRING;
> + packet->senderpid = getpid();
> + packet->sendertid = gettid();
> +
> + p = packet->data;
> +
> + strcpy(p, text);
> + p += strlen(text) + 1;
> +
> + return packet;
> +}
> +
> +struct runnerpacket *runnerpacket_resultoverride(const char *result)
> +{
> + struct runnerpacket *packet;
> + uint32_t size;
> + char *p;
> +
> + size = sizeof(struct runnerpacket) + strlen(result) + 1;
> + packet = malloc(size);
> +
> + packet->size = size;
> + packet->type = PACKETTYPE_RESULT_OVERRIDE;
> + packet->senderpid = getpid();
> + packet->sendertid = gettid();
> +
> + p = packet->data;
> +
> + strcpy(p, result);
> + p += strlen(result) + 1;
> +
> + return packet;
> +}
> +
> +uint32_t socket_dump_canary(void)
> +{
> + return 'I' << 24 | 'G' << 16 | 'T' << 8 | '1';
> +}
> +
> +void log_to_runner_sig_safe(const char *str, size_t len)
> +{
> + size_t prlen = len;
> +
> + struct runnerpacket_log_sig_safe p = {
> + .size = sizeof(struct runnerpacket) + sizeof(uint8_t),
> + .type = PACKETTYPE_LOG,
> + .senderpid = getpid(),
> + .sendertid = 0, /* gettid() not signal safe */
> + .stream = STDERR_FILENO,
> + };
> +
> + if (len > sizeof(p.data) - 1)
> + prlen = sizeof(p.data) - 1;
> + memcpy(p.data, str, prlen);
> + p.size += prlen + 1;
> +
> + write(runner_socket_fd, &p, p.size);
> +
> + len -= prlen;
> + if (len)
> + log_to_runner_sig_safe(str + prlen, len);
> +}
> +
> +/**
> + * comms_read_dump:
> + * @fd: Open fd to a comms dump file
> + * @visitor: Collection of packet handlers
> + *
> + * Reads a comms dump file, calling specified handler functions for
> + * individual packets.
> + *
> + * Returns: #COMMSPARSE_ERROR for failures reading or parsing the
> + * dump, #COMMSPARSE_EMPTY for empty dumps (no comms used),
> + * #COMMSPARSE_SUCCESS for successful read.
> + */
> +int comms_read_dump(int fd, struct comms_visitor *visitor)
> +{
> + struct stat statbuf;
> + char *buf, *bufend, *p;
> + int ret = COMMSPARSE_EMPTY;
> + bool cont = true;
> +
> + if (fd < 0)
> + return COMMSPARSE_EMPTY;
> +
> + if (fstat(fd, &statbuf))
> + return COMMSPARSE_ERROR;
> +
> + if (statbuf.st_size == 0)
> + return COMMSPARSE_ERROR;
> +
> + buf = mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
> + if (buf == MAP_FAILED)
> + return COMMSPARSE_ERROR;
> +
> + bufend = buf + statbuf.st_size;
> + p = buf;
> +
> + while (p != NULL && p != bufend && cont) {
> + const struct runnerpacket *packet;
> + runnerpacket_read_helper helper;
> +
> + if (bufend - p >= sizeof(uint32_t)) {
> + uint32_t canary;
> +
> + memcpy(&canary, p, sizeof(canary));
> + if (canary != socket_dump_canary()) {
> + fprintf(stderr,
> + "Invalid canary while parsing comms: %"PRIu32", expected %"PRIu32"\n",
> + canary, socket_dump_canary());
> + munmap(buf, statbuf.st_size);
> + return COMMSPARSE_ERROR;
> + }
> + }
> + p += sizeof(uint32_t);
> +
> + if (bufend -p < sizeof(struct runnerpacket)) {
> + fprintf(stderr,
> + "Error parsing comms: Expected runnerpacket after canary, truncated file?\n");
> + munmap(buf, statbuf.st_size);
> + return COMMSPARSE_ERROR;
> + }
> +
> + packet = (struct runnerpacket *)p;
> + if (bufend -p < packet->size) {
> + fprintf(stderr,
> + "Error parsing comms: Unexpected end of file, truncated file?\n");
> + munmap(buf, statbuf.st_size);
> + return COMMSPARSE_ERROR;
> + }
> + p += packet->size;
> +
> + /*
> + * Runner sends EXEC itself before executing the test.
> + * If we get other types, it indicates the test really
> + * uses socket comms.
> + */
> + if (packet->type != PACKETTYPE_EXEC)
> + ret = COMMSPARSE_SUCCESS;
> +
> + switch (packet->type) {
> + case PACKETTYPE_INVALID:
> + printf("Warning: Unknown packet type %"PRIu32", skipping\n", packet->type);
> + break;
> + case PACKETTYPE_LOG:
> + if (visitor->log) {
> + helper = read_runnerpacket(packet);
> + cont = visitor->log(packet, helper, visitor->userdata);
> + }
> + break;
> + case PACKETTYPE_EXEC:
> + if (visitor->exec) {
> + helper = read_runnerpacket(packet);
> + cont = visitor->exec(packet, helper, visitor->userdata);
> + }
> + break;
> + case PACKETTYPE_EXIT:
> + if (visitor->exit) {
> + helper = read_runnerpacket(packet);
> + cont = visitor->exit(packet, helper, visitor->userdata);
> + }
> + break;
> + case PACKETTYPE_SUBTEST_START:
> + if (visitor->subtest_start) {
> + helper = read_runnerpacket(packet);
> + cont = visitor->subtest_start(packet, helper, visitor->userdata);
> + }
> + break;
> + case PACKETTYPE_SUBTEST_RESULT:
> + if (visitor->subtest_result) {
> + helper = read_runnerpacket(packet);
> + cont = visitor->subtest_result(packet, helper, visitor->userdata);
> + }
> + break;
> + case PACKETTYPE_DYNAMIC_SUBTEST_START:
> + if (visitor->dynamic_subtest_start) {
> + helper = read_runnerpacket(packet);
> + cont = visitor->dynamic_subtest_start(packet, helper, visitor->userdata);
> + }
> + break;
> + case PACKETTYPE_DYNAMIC_SUBTEST_RESULT:
> + if (visitor->dynamic_subtest_result) {
> + helper = read_runnerpacket(packet);
> + cont = visitor->dynamic_subtest_result(packet, helper, visitor->userdata);
> + }
> + break;
> + case PACKETTYPE_VERSIONSTRING:
> + if (visitor->versionstring) {
> + helper = read_runnerpacket(packet);
> + cont = visitor->versionstring(packet, helper, visitor->userdata);
> + }
> + break;
> + case PACKETTYPE_RESULT_OVERRIDE:
> + if (visitor->result_override) {
> + helper = read_runnerpacket(packet);
> + cont = visitor->result_override(packet, helper, visitor->userdata);
> + }
> + break;
> + default:
> + printf("Warning: Unknown packet type %"PRIu32"\n", helper.type);
> + break;
> + }
> + }
> +
> + munmap(buf, statbuf.st_size);
> + return cont ? ret : COMMSPARSE_ERROR;
> +}
> diff --git a/lib/runnercomms.h b/lib/runnercomms.h
> new file mode 100644
> index 00000000..0bc30818
> --- /dev/null
> +++ b/lib/runnercomms.h
> @@ -0,0 +1,259 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2022 Intel Corporation
> + */
> +
> +#ifndef IGT_RUNNERCOMMS_H
> +#define IGT_RUNNERCOMMS_H
> +
> +#include <stdbool.h>
> +#include <stddef.h>
> +#include <stdint.h>
> +
> +/*
> + * A flat struct that can and will be directly dumped to
> + * disk. Constructed with runnerpacket_<type>() helper functions.
> + */
> +struct runnerpacket {
> + uint32_t size; /* Full size of the packet in octets */
> + uint32_t type; /* runnerpacket_type, but fixed width */
> + int32_t senderpid;
> + int32_t sendertid;
> +
> + char data[];
> +} __attribute__((packed));
> +
> +_Static_assert(sizeof(struct runnerpacket) == 4 * 4, "runnerpacket structure must not change");
> +_Static_assert(offsetof(struct runnerpacket, data) == 4 * 4, "runnerpacket structure must not change");
> +
> +/*
> + * A helper for reading and parsing runnerpacket structs. Fields will
> + * point directly into the data field of an existing runnerpacket
> + * object. Constructed with read_runnerpacket().
> + *
> + * Some fields can be left as 0 / NULL / some other applicable invalid
> + * value in the case of having older dumps read with binaries that
> + * have extended the data formats.
> + */
> +typedef union runnerpacket_read_helper {
> + /*
> + * All other fields must begin with "uint32_t type" so it's a
> + * common initial sequence, safe to read no matter what union
> + * field is active.
> + */
> + uint32_t type;
> +
> + struct {
> + uint32_t type;
> +
> + uint8_t stream;
> + const char *text;
> + } log;
> +
> + struct {
> + uint32_t type;
> +
> + const char *cmdline;
> + } exec;
> +
> + struct {
> + uint32_t type;
> +
> + int32_t exitcode;
> + const char *timeused;
> + } exit;
> +
> + struct {
> + uint32_t type;
> +
> + const char *name;
> + } subteststart;
> +
> + struct {
> + uint32_t type;
> +
> + const char *name;
> + const char *result;
> + const char *timeused;
> + const char *reason;
> + } subtestresult;
> +
> + struct {
> + uint32_t type;
> +
> + const char *name;
> + } dynamicsubteststart;
> +
> + struct {
> + uint32_t type;
> +
> + const char *name;
> + const char *result;
> + const char *timeused;
> + const char *reason;
> + } dynamicsubtestresult;
> +
> + struct {
> + uint32_t type;
> +
> + const char *text;
> + } versionstring;
> +
> + struct {
> + uint32_t type;
> +
> + const char *result;
> + } resultoverride;
> +} runnerpacket_read_helper;
> +
> +void set_runner_socket(int fd);
> +bool runner_connected(void);
> +void send_to_runner(struct runnerpacket *packet);
> +
> +runnerpacket_read_helper read_runnerpacket(const struct runnerpacket *packet);
> +
> +/*
> + * All packet types must document the format of the data[] array. The
> + * notation used is
> + *
> + * Explanation of the packet
> + * type: explanation of values
> + * type2: explanation of values
> + * (etc)
> + *
> + * The type "cstring" can be used to denote that the content is a
> + * nul-terminated string.
> + */
> +enum runnerpacket_type {
> + PACKETTYPE_INVALID,
> + /* No data. This type is only used on parse failures and such. */
> +
> + PACKETTYPE_LOG,
> + /*
> + * Normal log message.
> + * uint8_t: 1 = stdout, 2 = stderr
> + * cstring: Log text
> + */
> +
> + PACKETTYPE_EXEC,
> + /*
> + * Command line executed. Sent by runner before calling exec().
> + * cstring: command line as one string, argv[0] included, space separated
> + */
> +
> + PACKETTYPE_EXIT,
> + /*
> + * Process exit. Written by runner.
> + * int32_t: exitcode
> + * cstring: Time taken by the process from exec to exit, as a floating point value in seconds, as text
> + */
> +
> + PACKETTYPE_SUBTEST_START,
> + /*
> + * Subtest begins.
> + * cstring: Name of the subtest
> + */
> +
> + PACKETTYPE_SUBTEST_RESULT,
> + /*
> + * Subtest ends. Can appear without a corresponding SUBTEST_START packet.
> + * cstring: Name of the subtest
> + * cstring: Result of the subtest
> + * cstring: Time taken by the subtest, as a floating point value in seconds, as text
> + * cstring: If len > 0, the reason for the subtest result (fail/skip)
> + */
> +
> + PACKETTYPE_DYNAMIC_SUBTEST_START,
> + /*
> + * Dynamic subtest begins.
> + * cstring: Name of the dynamic subtest
> + */
> +
> + PACKETTYPE_DYNAMIC_SUBTEST_RESULT,
> + /*
> + * Dynamic subtest ends.
> + * cstring: Name of the dynamic subtest
> + * cstring: Result of the dynamic subtest
> + * cstring: Time taken by the dynamic subtest, as a floating point value in seconds, as text
> + * cstring: If len > 0, the reason for the dynamic subtest result (fail/skip)
> + */
> +
> + PACKETTYPE_VERSIONSTRING,
> + /*
> + * Version of the running test
> + * cstring: Version string
> + */
> +
> + PACKETTYPE_RESULT_OVERRIDE,
> + /*
> + * Override the result of the most recently started test/subtest/dynamic subtest. Used for timeout and abort etc.
> + * cstring: The result to use, as text. All lowercase.
> + */
> +
> +
> + PACKETTYPE_NUM_TYPES /* must be last */
> +};
> +
> +struct runnerpacket *runnerpacket_log(uint8_t stream, const char *text);
> +struct runnerpacket *runnerpacket_exec(char **argv);
> +struct runnerpacket *runnerpacket_exit(int32_t exitcode, const char *timeused);
> +struct runnerpacket *runnerpacket_subtest_start(const char *name);
> +struct runnerpacket *runnerpacket_subtest_result(const char *name, const char *result,
> + const char *timeused, const char *reason);
> +struct runnerpacket *runnerpacket_dynamic_subtest_start(const char *name);
> +struct runnerpacket *runnerpacket_dynamic_subtest_result(const char *name, const char *result,
> + const char *timeused, const char *reason);
> +struct runnerpacket *runnerpacket_versionstring(const char *text);
> +struct runnerpacket *runnerpacket_resultoverride(const char *result);
> +
> +uint32_t socket_dump_canary(void);
> +
> +struct runnerpacket_log_sig_safe {
> + uint32_t size;
> + uint32_t type;
> + int32_t senderpid;
> + int32_t sendertid;
> +
> + uint8_t stream;
> + char data[128];
> +} __attribute__((packed));
> +
> +_Static_assert(offsetof(struct runnerpacket_log_sig_safe, stream) == 4 * 4, "signal-safe log runnerpacket must be compatible");
> +_Static_assert(offsetof(struct runnerpacket_log_sig_safe, data) == 4 * 4 + 1, "signal-safe log runnerpacket must be compatible");
> +
> +void log_to_runner_sig_safe(const char *str, size_t len);
> +
> +/*
> + * Comms dump reader
> + *
> + * A visitor for reading comms dump files. Calls handlers if
> + * corresponding handler is set. Reading stops if a handler returns
> + * false.
> + *
> + * The passed arguments are the packet itself, the already-constructed
> + * read helper, and the userdata pointer from the visitor.
> + */
> +typedef bool (*handler_t)(const struct runnerpacket *, runnerpacket_read_helper, void *userdata);
> +
> +struct comms_visitor {
> + handler_t log;
> + handler_t exec;
> + handler_t exit;
> + handler_t subtest_start;
> + handler_t subtest_result;
> + handler_t dynamic_subtest_start;
> + handler_t dynamic_subtest_result;
> + handler_t versionstring;
> + handler_t result_override;
> +
> + void* userdata;
> +};
> +
> +enum {
> + COMMSPARSE_ERROR,
> + COMMSPARSE_EMPTY,
> + COMMSPARSE_SUCCESS
> +};
> +int comms_read_dump(int fd, struct comms_visitor *visitor);
> +
> +#endif
> --
> 2.30.2
>
More information about the igt-dev
mailing list