summaryrefslogtreecommitdiff
path: root/runner/job_list.c
diff options
context:
space:
mode:
Diffstat (limited to 'runner/job_list.c')
-rw-r--r--runner/job_list.c484
1 files changed, 484 insertions, 0 deletions
diff --git a/runner/job_list.c b/runner/job_list.c
new file mode 100644
index 00000000..e3f820c3
--- /dev/null
+++ b/runner/job_list.c
@@ -0,0 +1,484 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "job_list.h"
+#include "igt_core.h"
+
+static bool matches_any(const char *str, struct regex_list *list)
+{
+ size_t i;
+
+ for (i = 0; i < list->size; i++) {
+ if (regexec(list->regexes[i], str,
+ (size_t)0, NULL, 0) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void add_job_list_entry(struct job_list *job_list,
+ char *binary,
+ char **subtests,
+ size_t subtest_count)
+{
+ struct job_list_entry *entry;
+
+ job_list->size++;
+ job_list->entries = realloc(job_list->entries, job_list->size * sizeof(*job_list->entries));
+ entry = &job_list->entries[job_list->size - 1];
+
+ entry->binary = binary;
+ entry->subtests = subtests;
+ entry->subtest_count = subtest_count;
+}
+
+static void add_subtests(struct job_list *job_list, struct settings *settings,
+ char *binary,
+ struct regex_list *include, struct regex_list *exclude)
+{
+ FILE *p;
+ char cmd[256] = {};
+ char *subtestname;
+ char **subtests = NULL;
+ size_t num_subtests = 0;
+ int s;
+
+ s = snprintf(cmd, sizeof(cmd), "%s/%s --list-subtests",
+ settings->test_root, binary);
+ if (s < 0) {
+ fprintf(stderr, "Failure generating command string, this shouldn't happen.\n");
+ return;
+ }
+
+ if (s >= sizeof(cmd)) {
+ fprintf(stderr, "Path to binary too long, ignoring: %s/%s\n",
+ settings->test_root, binary);
+ return;
+ }
+
+ p = popen(cmd, "r");
+ if (!p) {
+ fprintf(stderr, "popen failed when executing %s: %s\n",
+ cmd,
+ strerror(errno));
+ return;
+ }
+
+ while (fscanf(p, "%ms", &subtestname) == 1) {
+ if (exclude && exclude->size && matches_any(subtestname, exclude)) {
+ free(subtestname);
+ continue;
+ }
+
+ if (include && include->size && !matches_any(subtestname, include)) {
+ free(subtestname);
+ continue;
+ }
+
+ if (settings->multiple_mode) {
+ num_subtests++;
+ subtests = realloc(subtests, num_subtests * sizeof(*subtests));
+ subtests[num_subtests - 1] = strdup(subtestname);
+ } else {
+ subtests = malloc(sizeof(*subtests));
+ *subtests = strdup(subtestname);
+ add_job_list_entry(job_list, strdup(binary), subtests, 1);
+ subtests = NULL;
+ }
+
+ free(subtestname);
+ }
+
+ if (num_subtests)
+ add_job_list_entry(job_list, strdup(binary), subtests, num_subtests);
+
+ s = pclose(p);
+ if (s == 0) {
+ return;
+ } else if (s == -1) {
+ fprintf(stderr, "popen error when executing %s: %s\n", binary, strerror(errno));
+ } else if (WIFEXITED(s)) {
+ if (WEXITSTATUS(s) == IGT_EXIT_INVALID) {
+ /* No subtests on this one */
+ if (exclude && exclude->size && matches_any(binary, exclude)) {
+ return;
+ }
+ if (!include || !include->size || matches_any(binary, include)) {
+ add_job_list_entry(job_list, strdup(binary), NULL, 0);
+ return;
+ }
+ }
+ } else {
+ fprintf(stderr, "Test binary %s died unexpectedly\n", binary);
+ }
+}
+
+static bool filtered_job_list(struct job_list *job_list,
+ struct settings *settings,
+ int fd)
+{
+ FILE *f;
+ char buf[128];
+
+ if (job_list->entries != NULL) {
+ fprintf(stderr, "Caller didn't clear the job list, this shouldn't happen\n");
+ exit(1);
+ }
+
+ f = fdopen(fd, "r");
+
+ while (fscanf(f, "%127s", buf) == 1) {
+ if (!strcmp(buf, "TESTLIST") || !(strcmp(buf, "END")))
+ continue;
+
+ /*
+ * If the binary name matches exclude filters, no
+ * subtests are added.
+ */
+ if (settings->exclude_regexes.size && matches_any(buf, &settings->exclude_regexes))
+ continue;
+
+ /*
+ * If the binary name matches include filters (or include filters not present),
+ * all subtests except those matching exclude filters are added.
+ */
+ if (!settings->include_regexes.size || matches_any(buf, &settings->include_regexes)) {
+ if (settings->multiple_mode && !settings->exclude_regexes.size)
+ /*
+ * Optimization; we know that all
+ * subtests will be included, so we
+ * get to omit executing
+ * --list-subtests.
+ */
+ add_job_list_entry(job_list, strdup(buf), NULL, 0);
+ else
+ add_subtests(job_list, settings, buf,
+ NULL, &settings->exclude_regexes);
+ continue;
+ }
+
+ /*
+ * Binary name doesn't match exclude or include filters.
+ */
+ add_subtests(job_list, settings, buf,
+ &settings->include_regexes,
+ &settings->exclude_regexes);
+ }
+
+ return job_list->size != 0;
+}
+
+static bool job_list_from_test_list(struct job_list *job_list,
+ struct settings *settings)
+{
+ FILE *f;
+ char *line = NULL;
+ size_t line_len = 0;
+ struct job_list_entry entry = {};
+ bool any = false;
+
+ if ((f = fopen(settings->test_list, "r")) == NULL) {
+ fprintf(stderr, "Cannot open test list file %s\n", settings->test_list);
+ return false;
+ }
+
+ while (1) {
+ char *binary;
+ char *delim;
+
+ if (getline(&line, &line_len, f) == -1) {
+ if (errno == EINTR)
+ continue;
+ else
+ break;
+ }
+
+ /* # starts a comment */
+ if ((delim = strchr(line, '#')) != NULL)
+ *delim = '\0';
+
+ if (sscanf(line, "igt@%ms", &binary) == 1) {
+ if ((delim = strchr(binary, '@')) != NULL)
+ *delim++ = '\0';
+
+ if (!settings->multiple_mode) {
+ char **subtests = NULL;
+ if (delim) {
+ subtests = malloc(sizeof(char*));
+ subtests[0] = strdup(delim);
+ }
+ add_job_list_entry(job_list, strdup(binary),
+ subtests, (size_t)(subtests != NULL));
+ any = true;
+ free(binary);
+ binary = NULL;
+ continue;
+ }
+
+ /*
+ * If the currently built entry has the same
+ * binary, add a subtest. Otherwise submit
+ * what's already built and start a new one.
+ */
+ if (entry.binary && !strcmp(entry.binary, binary)) {
+ if (!delim) {
+ /* ... except we didn't get a subtest */
+ fprintf(stderr,
+ "Error: Unexpected test without subtests "
+ "after same test had subtests\n");
+ free(binary);
+ fclose(f);
+ return false;
+ }
+ entry.subtest_count++;
+ entry.subtests = realloc(entry.subtests,
+ entry.subtest_count *
+ sizeof(*entry.subtests));
+ entry.subtests[entry.subtest_count - 1] = strdup(delim);
+ free(binary);
+ binary = NULL;
+ continue;
+ }
+
+ if (entry.binary) {
+ add_job_list_entry(job_list, entry.binary, entry.subtests, entry.subtest_count);
+ any = true;
+ }
+
+ memset(&entry, 0, sizeof(entry));
+ entry.binary = strdup(binary);
+ if (delim) {
+ entry.subtests = malloc(sizeof(*entry.subtests));
+ entry.subtests[0] = strdup(delim);
+ entry.subtest_count = 1;
+ }
+
+ free(binary);
+ binary = NULL;
+ }
+ }
+
+ if (entry.binary) {
+ add_job_list_entry(job_list, entry.binary, entry.subtests, entry.subtest_count);
+ any = true;
+ }
+
+ free(line);
+ fclose(f);
+ return any;
+}
+
+void init_job_list(struct job_list *job_list)
+{
+ memset(job_list, 0, sizeof(*job_list));
+}
+
+void free_job_list(struct job_list *job_list)
+{
+ int i, k;
+
+ for (i = 0; i < job_list->size; i++) {
+ struct job_list_entry *entry = &job_list->entries[i];
+
+ free(entry->binary);
+ for (k = 0; k < entry->subtest_count; k++) {
+ free(entry->subtests[k]);
+ }
+ free(entry->subtests);
+ }
+ free(job_list->entries);
+ init_job_list(job_list);
+}
+
+bool create_job_list(struct job_list *job_list,
+ struct settings *settings)
+{
+ int dirfd, fd;
+ bool result;
+
+ if (!settings->test_root) {
+ fprintf(stderr, "No test root set; this shouldn't happen\n");
+ return false;
+ }
+
+ free_job_list(job_list);
+
+ dirfd = open(settings->test_root, O_DIRECTORY | O_RDONLY);
+ if (dirfd < 0) {
+ fprintf(stderr, "Test directory %s cannot be opened\n", settings->test_root);
+ return false;
+ }
+
+ fd = openat(dirfd, "test-list.txt", O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Cannot open %s/test-list.txt\n", settings->test_root);
+ close(dirfd);
+ return false;
+ }
+
+ /*
+ * If a test_list is given (not to be confused with
+ * test-list.txt), we use it directly without making tests
+ * list their subtests. If include/exclude filters are given
+ * we filter them directly from the test_list.
+ */
+ if (settings->test_list)
+ result = job_list_from_test_list(job_list, settings);
+ else
+ result = filtered_job_list(job_list, settings, fd);
+
+ close(fd);
+ close(dirfd);
+
+ return result;
+}
+
+static char joblist_filename[] = "joblist.txt";
+bool serialize_job_list(struct job_list *job_list, struct settings *settings)
+{
+ int dirfd, fd;
+ size_t i, k;
+ FILE *f;
+
+ if (!settings->results_path) {
+ fprintf(stderr, "No results-path set; this shouldn't happen\n");
+ return false;
+ }
+
+ if ((dirfd = open(settings->results_path, O_DIRECTORY | O_RDONLY)) < 0) {
+ mkdir(settings->results_path, 0777);
+ if ((dirfd = open(settings->results_path, O_DIRECTORY | O_RDONLY)) < 0) {
+ fprintf(stderr, "Creating results-path failed\n");
+ return false;
+ }
+ }
+
+ if (!settings->overwrite &&
+ faccessat(dirfd, joblist_filename, F_OK, 0) == 0) {
+ fprintf(stderr, "Job list file already exists and not overwriting\n");
+ close(dirfd);
+ return false;
+ }
+
+ if (settings->overwrite &&
+ unlinkat(dirfd, joblist_filename, 0) != 0 &&
+ errno != ENOENT) {
+ fprintf(stderr, "Error removing old job list\n");
+ close(dirfd);
+ return false;
+ }
+
+ if ((fd = openat(dirfd, joblist_filename, O_CREAT | O_EXCL | O_WRONLY, 0666)) < 0) {
+ fprintf(stderr, "Creating job list serialization file failed: %s\n", strerror(errno));
+ close(dirfd);
+ return false;
+ }
+
+ f = fdopen(fd, "w");
+ if (!f) {
+ close(fd);
+ close(dirfd);
+ return false;
+ }
+
+ for (i = 0; i < job_list->size; i++) {
+ struct job_list_entry *entry = &job_list->entries[i];
+ fputs(entry->binary, f);
+
+ if (entry->subtest_count) {
+ const char *delim = "";
+
+ fprintf(f, " ");
+
+ for (k = 0; k < entry->subtest_count; k++) {
+ fprintf(f, "%s%s", delim, entry->subtests[k]);
+ delim = ",";
+ }
+ }
+
+ fprintf(f, "\n");
+ }
+
+ if (settings->sync) {
+ fsync(fd);
+ fsync(dirfd);
+ }
+
+ fclose(f);
+ close(dirfd);
+ return true;
+}
+
+bool read_job_list(struct job_list *job_list, int dirfd)
+{
+ int fd;
+ FILE *f;
+ ssize_t read;
+ char *line = NULL;
+ size_t line_len = 0;
+
+ free_job_list(job_list);
+
+ if ((fd = openat(dirfd, joblist_filename, O_RDONLY)) < 0)
+ return false;
+
+ f = fdopen(fd, "r");
+ if (!f) {
+ close(fd);
+ return false;
+ }
+
+ while ((read = getline(&line, &line_len, f))) {
+ char *binary, *sublist, *comma;
+ char **subtests = NULL;
+ size_t num_subtests = 0, len;
+
+ if (read < 0) {
+ if (errno == EINTR)
+ continue;
+ else
+ break;
+ }
+
+ len = strlen(line);
+ if (len > 0 && line[len - 1] == '\n')
+ line[len - 1] = '\0';
+
+ sublist = strchr(line, ' ');
+ if (!sublist) {
+ add_job_list_entry(job_list, strdup(line), NULL, 0);
+ continue;
+ }
+
+ *sublist++ = '\0';
+ binary = strdup(line);
+
+ do {
+ comma = strchr(sublist, ',');
+ if (comma) {
+ *comma++ = '\0';
+ }
+
+ ++num_subtests;
+ subtests = realloc(subtests, num_subtests * sizeof(*subtests));
+ subtests[num_subtests - 1] = strdup(sublist);
+ sublist = comma;
+ } while (comma != NULL);
+
+ add_job_list_entry(job_list, binary, subtests, num_subtests);
+ }
+
+ free(line);
+ fclose(f);
+
+ return true;
+}