[PATCH i-g-t 01/26] runner goes brrrrr

Petri Latvala petri.latvala at intel.com
Fri Mar 12 11:03:16 UTC 2021


---
 lib/igt_core.c    |   6 +
 lib/meson.build   |   1 +
 lib/runnercomms.c | 440 ++++++++++++++++++++++++++++++++++++++++++++++
 lib/runnercomms.h | 210 ++++++++++++++++++++++
 runner/executor.c | 134 +++++++++++++-
 runner/executor.h |   1 +
 6 files changed, 785 insertions(+), 7 deletions(-)
 create mode 100644 lib/runnercomms.c
 create mode 100644 lib/runnercomms.h

diff --git a/lib/igt_core.c b/lib/igt_core.c
index f9dfaa0d..10b5be27 100644
--- a/lib/igt_core.c
+++ b/lib/igt_core.c
@@ -73,6 +73,7 @@
 #include "igt_list.h"
 #include "igt_device_scan.h"
 #include "igt_thread.h"
+#include "runnercomms.h"
 
 #define UNW_LOCAL_ONLY
 #include <libunwind.h>
@@ -860,6 +861,11 @@ static void common_init_env(void)
 	if (env) {
 		igt_rc_device = strdup(env);
 	}
+
+	env = getenv("IGT_RUNNER_SOCKET_FD");
+	if (env) {
+		set_runner_socket(atoi(env));
+	}
 }
 
 static int common_init(int *argc, char **argv,
diff --git a/lib/meson.build b/lib/meson.build
index 672b4206..b0cd14b2 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -52,6 +52,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..c0716fc7
--- /dev/null
+++ b/lib/runnercomms.c
@@ -0,0 +1,440 @@
+/*
+ * Copyright © 2021 Intel Corporation
+ *
+ * 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 <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "runnercomms.h"
+
+static int 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:
+ * @fd: Unix socket connected to igt_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.
+ */
+union runnerpacket_read_helper read_runnerpacket(const struct runnerpacket *packet)
+{
+	union 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_integer(&ret.log.level, sizeof(ret.log.level), &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;
+	default:
+		ret.type = PACKETTYPE_INVALID;
+		break;
+	}
+
+	return ret;
+}
+
+struct runnerpacket *runnerpacket_log(uint8_t stream, uint8_t level, const char *text)
+{
+	struct runnerpacket *packet;
+	uint32_t size;
+	char *p;
+
+	size = sizeof(struct runnerpacket) + sizeof(stream) + sizeof(level) + 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);
+
+	memcpy(p, &level, sizeof(level));
+	p += sizeof(level);
+
+	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 = 0;
+	for (i = 0; argv[i] != NULL; i++)
+		size += strlen(argv[i]) + 1; // followed by a space of \0 so +1 either way for each
+
+	size += sizeof(struct runnerpacket);
+	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++) {
+		strcpy(p, argv[i]);
+		p += strlen(argv[i]);
+		p[0] = ' ';
+		p++;
+	}
+	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;
+}
diff --git a/lib/runnercomms.h b/lib/runnercomms.h
new file mode 100644
index 00000000..87d8c771
--- /dev/null
+++ b/lib/runnercomms.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright © 2021 Intel Corporation
+ *
+ * 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.
+ */
+
+#ifndef IGT_RUNNERCOMMS_H
+#define IGT_RUNNERCOMMS_H
+
+#include <stdint.h>
+#include <stdbool.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_t, but fixed width */
+	int32_t senderpid;
+	int32_t sendertid;
+
+	char data[];
+};
+
+/*
+ * 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.
+ */
+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;
+		uint8_t level;
+		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;
+};
+
+void set_runner_socket(int fd);
+bool runner_connected(void);
+void send_to_runner(struct runnerpacket *packet);
+
+union 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.
+ */
+typedef 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
+       * uint8_t: Log level
+       * 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
+       */
+
+} runnerpacket_type_t;
+
+struct runnerpacket *runnerpacket_log(uint8_t stream, uint8_t level, 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);
+
+#endif
diff --git a/runner/executor.c b/runner/executor.c
index 9b582179..a2f29d32 100644
--- a/runner/executor.c
+++ b/runner/executor.c
@@ -27,6 +27,7 @@
 #include "igt_taints.h"
 #include "executor.h"
 #include "output_strings.h"
+#include "runnercomms.h"
 
 #define KMSG_HEADER "[IGT] "
 #define KMSG_WARN 4
@@ -445,6 +446,7 @@ static const char *filenames[_F_LAST] = {
 	[_F_OUT] = "out.txt",
 	[_F_ERR] = "err.txt",
 	[_F_DMESG] = "dmesg.txt",
+	[_F_SOCKET] = "comms.txt",
 };
 
 static int open_at_end(int dirfd, const char *name)
@@ -476,6 +478,9 @@ bool open_output_files(int dirfd, int *fds, bool write)
 
 	for (i = 0; i < _F_LAST; i++) {
 		if ((fds[i] = openfunc(dirfd, filenames[i])) < 0) {
+			/* Ignore failure to open socket comms */
+			if (i == _F_SOCKET) continue;
+
 			while (--i >= 0)
 				close(fds[i]);
 			return false;
@@ -756,6 +761,11 @@ static int next_kill_signal(int killed)
 	}
 }
 
+static uint32_t socket_dump_canary(void)
+{
+	return 'I' << 24 | 'G' << 16 | 'T' << 8 | '1';
+}
+
 /*
  * Returns:
  *  =0 - Success
@@ -763,7 +773,8 @@ static int next_kill_signal(int killed)
  *  >0 - Timeout happened, need to recreate from journal
  */
 static int monitor_output(pid_t child,
-			  int outfd, int errfd, int kmsgfd, int sigfd,
+			  int outfd, int errfd, int socketfd,
+			  int kmsgfd, int sigfd,
 			  int *outputs,
 			  double *time_spent,
 			  struct settings *settings,
@@ -791,6 +802,8 @@ static int monitor_output(pid_t child,
 
 	if (errfd > nfds)
 		nfds = errfd;
+	if (socketfd > nfds)
+		nfds = socketfd;
 	if (kmsgfd > nfds)
 		nfds = kmsgfd;
 	if (sigfd > nfds)
@@ -961,6 +974,87 @@ static int monitor_output(pid_t child,
 			}
 		}
 
+		if (socketfd >= 0 && FD_ISSET(socketfd, &set)) {
+			struct runnerpacket *packet;
+			uint32_t canary = socket_dump_canary();
+
+			time_last_activity = time_now;
+
+			/* Don't need flags, don't need srcaddr */
+			s = read(socketfd, buf, sizeof(buf));
+			if (s <= 0) {
+				if (s < 0) {
+					errf("Error reading from communication socket: %m\n");
+				}
+
+				close(socketfd);
+				socketfd = -1;
+				goto socket_end;
+			}
+
+			packet = (struct runnerpacket *)buf;
+			if (s < sizeof(*packet) || s != packet->size) {
+				errf("Socket communication error: Received %zd bytes, expected %zd\n",
+				     s, s >= sizeof(packet->size) ? packet->size : sizeof(*packet));
+				close(socketfd);
+				socketfd = -1;
+				goto socket_end;
+			}
+
+			write(outputs[_F_SOCKET], &canary, sizeof(canary));
+			write(outputs[_F_SOCKET], buf, s);
+			disk_usage += s;
+			if (settings->sync)
+				fdatasync(outputs[_F_SOCKET]);
+
+			if (settings->log_level >= LOG_LEVEL_VERBOSE) {
+				union runnerpacket_read_helper helper = {};
+				const char *time;
+
+				if (packet->type == PACKETTYPE_SUBTEST_START ||
+				    packet->type == PACKETTYPE_SUBTEST_RESULT ||
+				    packet->type == PACKETTYPE_DYNAMIC_SUBTEST_START ||
+				    packet->type == PACKETTYPE_DYNAMIC_SUBTEST_RESULT)
+					helper = read_runnerpacket(packet);
+
+				switch (helper.type) {
+				case PACKETTYPE_SUBTEST_START:
+					if (helper.subteststart.name)
+						outf("Starting subtest: %s\n", helper.subteststart.name);
+					break;
+				case PACKETTYPE_SUBTEST_RESULT:
+					if (helper.subtestresult.name && helper.subtestresult.result) {
+						time = "<unknown>";
+						if (helper.subtestresult.timeused)
+							time = helper.subtestresult.timeused;
+						outf("Subtest %s: %s (%ss)\n",
+						     helper.subtestresult.name,
+						     helper.subtestresult.result,
+						     time);
+					}
+					break;
+				case PACKETTYPE_DYNAMIC_SUBTEST_START:
+					if (helper.dynamicsubteststart.name)
+						outf("Starting dynamic subtest: %s\n", helper.dynamicsubteststart.name);
+					break;
+				case PACKETTYPE_DYNAMIC_SUBTEST_RESULT:
+					if (helper.dynamicsubtestresult.name && helper.dynamicsubtestresult.result) {
+						time = "<unknown>";
+						if (helper.dynamicsubtestresult.timeused)
+							time = helper.dynamicsubtestresult.timeused;
+						outf("Dynamic subtest %s: %s (%ss)\n",
+						     helper.dynamicsubtestresult.name,
+						     helper.dynamicsubtestresult.result,
+						     time);
+					}
+					break;
+				default:
+					break;
+				}
+			}
+		}
+	socket_end:
+
 		if (kmsgfd >= 0 && FD_ISSET(kmsgfd, &set)) {
 			long dmesgwritten;
 
@@ -1151,6 +1245,7 @@ static int monitor_output(pid_t child,
 				free(outbuf);
 				close(outfd);
 				close(errfd);
+				close(socketfd);
 				close(kmsgfd);
 				return -1;
 			}
@@ -1174,6 +1269,7 @@ static int monitor_output(pid_t child,
 	free(outbuf);
 	close(outfd);
 	close(errfd);
+	close(socketfd);
 	close(kmsgfd);
 
 	if (aborting)
@@ -1183,7 +1279,7 @@ static int monitor_output(pid_t child,
 }
 
 static void __attribute__((noreturn))
-execute_test_process(int outfd, int errfd,
+execute_test_process(int outfd, int errfd, int socketfd,
 		     struct settings *settings,
 		     struct job_list_entry *entry)
 {
@@ -1235,6 +1331,13 @@ execute_test_process(int outfd, int errfd,
 		}
 	}
 
+	if (socketfd >= 0) {
+		struct runnerpacket *packet;
+
+		packet = runnerpacket_exec(argv);
+		write(socketfd, packet, packet->size);
+	}
+
 	execv(argv[0], argv);
 	fprintf(stderr, "Cannot execute %s\n", argv[0]);
 	exit(IGT_EXIT_INVALID);
@@ -1316,7 +1419,8 @@ static int execute_next_entry(struct execute_state *state,
 	int kmsgfd;
 	int outpipe[2] = { -1, -1 };
 	int errpipe[2] = { -1, -1 };
-	int outfd, errfd;
+	int socket[2] = { -1, -1 };
+	int outfd, errfd, socketfd;
 	char name[32];
 	pid_t child;
 	int result;
@@ -1346,6 +1450,12 @@ static int execute_next_entry(struct execute_state *state,
 		goto out_pipe;
 	}
 
+	if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socket)) {
+		errf("Error creating sockets: %m\n");
+		result = -1;
+		goto out_pipe;
+	}
+
 	if ((kmsgfd = open("/dev/kmsg", O_RDONLY | O_CLOEXEC | O_NONBLOCK)) < 0) {
 		errf("Warning: Cannot open /dev/kmsg\n");
 	} else {
@@ -1386,27 +1496,37 @@ static int execute_next_entry(struct execute_state *state,
 		result = -1;
 		goto out_kmsgfd;
 	} else if (child == 0) {
+		char *envstring = NULL;
+
 		outfd = outpipe[1];
 		errfd = errpipe[1];
+		socketfd = socket[1];
 		close(outpipe[0]);
 		close(errpipe[0]);
+		close(socket[0]);
 
 		sigprocmask(SIG_UNBLOCK, sigmask, NULL);
 
+		if (socketfd >= 0 && asprintf(&envstring, "%d", socketfd) == 1)
+			setenv("IGT_RUNNER_SOCKET_FD", envstring, 1);
 		setenv("IGT_SENTINEL_ON_STDERR", "1", 1);
 
-		execute_test_process(outfd, errfd, settings, entry);
+		execute_test_process(outfd, errfd, socketfd, settings, entry);
 		/* unreachable */
 	}
 
 	outfd = outpipe[0];
 	errfd = errpipe[0];
+	socketfd = socket[0];
 	close(outpipe[1]);
 	close(errpipe[1]);
-	outpipe[1] = errpipe[1] = -1;
+	close(socket[1]);
+	outpipe[1] = errpipe[1] = socket[1] = -1;
 
-	result = monitor_output(child, outfd, errfd, kmsgfd, sigfd,
-				outputs, time_spent, settings, abortreason);
+	result = monitor_output(child, outfd, errfd, socketfd,
+				kmsgfd, sigfd,
+				outputs, time_spent, settings,
+				abortreason);
 
 out_kmsgfd:
 	close(kmsgfd);
diff --git a/runner/executor.h b/runner/executor.h
index 6c83e649..31f4ac16 100644
--- a/runner/executor.h
+++ b/runner/executor.h
@@ -22,6 +22,7 @@ enum {
 	_F_OUT,
 	_F_ERR,
 	_F_DMESG,
+	_F_SOCKET,
 	_F_LAST,
 };
 
-- 
2.29.2



More information about the Intel-gfx-trybot mailing list