• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <ctype.h>
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <linux/limits.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <unistd.h>
11 
12 #include "job_list.h"
13 #include "igt_core.h"
14 
matches_any(const char * str,struct regex_list * list)15 static bool matches_any(const char *str, struct regex_list *list)
16 {
17 	size_t i;
18 
19 	for (i = 0; i < list->size; i++) {
20 		if (g_regex_match(list->regexes[i], str, 0, NULL))
21 			return true;
22 	}
23 
24 	return false;
25 }
26 
add_job_list_entry(struct job_list * job_list,char * binary,char ** subtests,size_t subtest_count)27 static void add_job_list_entry(struct job_list *job_list,
28 			       char *binary,
29 			       char **subtests,
30 			       size_t subtest_count)
31 {
32 	struct job_list_entry *entry;
33 
34 	job_list->size++;
35 	job_list->entries = realloc(job_list->entries, job_list->size * sizeof(*job_list->entries));
36 	entry = &job_list->entries[job_list->size - 1];
37 
38 	entry->binary = binary;
39 	entry->subtests = subtests;
40 	entry->subtest_count = subtest_count;
41 }
42 
add_subtests(struct job_list * job_list,struct settings * settings,char * binary,struct regex_list * include,struct regex_list * exclude)43 static void add_subtests(struct job_list *job_list, struct settings *settings,
44 			 char *binary,
45 			 struct regex_list *include, struct regex_list *exclude)
46 {
47 	FILE *p;
48 	char cmd[256] = {};
49 	char *subtestname;
50 	char **subtests = NULL;
51 	size_t num_subtests = 0;
52 	int s;
53 
54 	s = snprintf(cmd, sizeof(cmd), "%s/%s --list-subtests",
55 		     settings->test_root, binary);
56 	if (s < 0) {
57 		fprintf(stderr, "Failure generating command string, this shouldn't happen.\n");
58 		return;
59 	}
60 
61 	if (s >= sizeof(cmd)) {
62 		fprintf(stderr, "Path to binary too long, ignoring: %s/%s\n",
63 			settings->test_root, binary);
64 		return;
65 	}
66 
67 	p = popen(cmd, "r");
68 	if (!p) {
69 		fprintf(stderr, "popen failed when executing %s: %s\n",
70 			cmd,
71 			strerror(errno));
72 		return;
73 	}
74 
75 	while (fscanf(p, "%ms", &subtestname) == 1) {
76 		char piglitname[256];
77 
78 		generate_piglit_name(binary, subtestname, piglitname, sizeof(piglitname));
79 
80 		if (exclude && exclude->size && matches_any(piglitname, exclude)) {
81 			free(subtestname);
82 			continue;
83 		}
84 
85 		if (include && include->size && !matches_any(piglitname, include)) {
86 			free(subtestname);
87 			continue;
88 		}
89 
90 		if (settings->multiple_mode) {
91 			num_subtests++;
92 			subtests = realloc(subtests, num_subtests * sizeof(*subtests));
93 			subtests[num_subtests - 1] = strdup(subtestname);
94 		} else {
95 			subtests = malloc(sizeof(*subtests));
96 			*subtests = strdup(subtestname);
97 			add_job_list_entry(job_list, strdup(binary), subtests, 1);
98 			subtests = NULL;
99 		}
100 
101 		free(subtestname);
102 	}
103 
104 	if (num_subtests)
105 		add_job_list_entry(job_list, strdup(binary), subtests, num_subtests);
106 
107 	s = pclose(p);
108 	if (s == 0) {
109 		return;
110 	} else if (s == -1) {
111 		fprintf(stderr, "popen error when executing %s: %s\n", binary, strerror(errno));
112 	} else if (WIFEXITED(s)) {
113 		if (WEXITSTATUS(s) == IGT_EXIT_INVALID) {
114 			char piglitname[256];
115 
116 			generate_piglit_name(binary, NULL,
117 					     piglitname, sizeof(piglitname));
118 			/* No subtests on this one */
119 			if (exclude && exclude->size &&
120 			    matches_any(piglitname, exclude)) {
121 				return;
122 			}
123 			if (!include || !include->size ||
124 			    matches_any(piglitname, include)) {
125 				add_job_list_entry(job_list, strdup(binary), NULL, 0);
126 				return;
127 			}
128 		}
129 	} else {
130 		fprintf(stderr, "Test binary %s died unexpectedly\n", binary);
131 	}
132 }
133 
filtered_job_list(struct job_list * job_list,struct settings * settings,int fd)134 static bool filtered_job_list(struct job_list *job_list,
135 			      struct settings *settings,
136 			      int fd)
137 {
138 	FILE *f;
139 	char buf[128];
140 	bool ok;
141 
142 	if (job_list->entries != NULL) {
143 		fprintf(stderr, "Caller didn't clear the job list, this shouldn't happen\n");
144 		exit(1);
145 	}
146 
147 	f = fdopen(fd, "r");
148 
149 	while (fscanf(f, "%127s", buf) == 1) {
150 		if (!strcmp(buf, "TESTLIST") || !(strcmp(buf, "END")))
151 			continue;
152 
153 		/*
154 		 * If the binary name matches exclude filters, no
155 		 * subtests are added.
156 		 */
157 		if (settings->exclude_regexes.size && matches_any(buf, &settings->exclude_regexes))
158 			continue;
159 
160 		/*
161 		 * If the binary name matches include filters (or include filters not present),
162 		 * all subtests except those matching exclude filters are added.
163 		 */
164 		if (!settings->include_regexes.size || matches_any(buf, &settings->include_regexes)) {
165 			if (settings->multiple_mode && !settings->exclude_regexes.size)
166 				/*
167 				 * Optimization; we know that all
168 				 * subtests will be included, so we
169 				 * get to omit executing
170 				 * --list-subtests.
171 				 */
172 				add_job_list_entry(job_list, strdup(buf), NULL, 0);
173 			else
174 				add_subtests(job_list, settings, buf,
175 					     NULL, &settings->exclude_regexes);
176 			continue;
177 		}
178 
179 		/*
180 		 * Binary name doesn't match exclude or include filters.
181 		 */
182 		add_subtests(job_list, settings, buf,
183 			     &settings->include_regexes,
184 			     &settings->exclude_regexes);
185 	}
186 
187 	ok = job_list->size != 0;
188 	if (!ok)
189 		fprintf(stderr, "Filter didn't match any job name\n");
190 
191 	return ok;
192 }
193 
job_list_from_test_list(struct job_list * job_list,struct settings * settings)194 static bool job_list_from_test_list(struct job_list *job_list,
195 				    struct settings *settings)
196 {
197 	FILE *f;
198 	char *line = NULL;
199 	size_t line_len = 0;
200 	struct job_list_entry entry = {};
201 	bool any = false;
202 
203 	if ((f = fopen(settings->test_list, "r")) == NULL) {
204 		fprintf(stderr, "Cannot open test list file %s\n", settings->test_list);
205 		return false;
206 	}
207 
208 	while (1) {
209 		char *binary;
210 		char *delim;
211 
212 		if (getline(&line, &line_len, f) == -1) {
213 			if (errno == EINTR)
214 				continue;
215 			else
216 				break;
217 		}
218 
219 		/* # starts a comment */
220 		if ((delim = strchr(line, '#')) != NULL)
221 			*delim = '\0';
222 
223 		if (settings->exclude_regexes.size && matches_any(line, &settings->exclude_regexes))
224 			continue;
225 
226 		if (settings->include_regexes.size && !matches_any(line, &settings->include_regexes))
227 			continue;
228 
229 		if (sscanf(line, "igt@%ms", &binary) == 1) {
230 			if ((delim = strchr(binary, '@')) != NULL)
231 				*delim++ = '\0';
232 
233 			if (!settings->multiple_mode) {
234 				char **subtests = NULL;
235 				if (delim) {
236 					subtests = malloc(sizeof(char*));
237 					subtests[0] = strdup(delim);
238 				}
239 				add_job_list_entry(job_list, strdup(binary),
240 						   subtests, (size_t)(subtests != NULL));
241 				any = true;
242 				free(binary);
243 				binary = NULL;
244 				continue;
245 			}
246 
247 			/*
248 			 * If the currently built entry has the same
249 			 * binary, add a subtest. Otherwise submit
250 			 * what's already built and start a new one.
251 			 */
252 			if (entry.binary && !strcmp(entry.binary, binary)) {
253 				if (!delim) {
254 					/* ... except we didn't get a subtest */
255 					fprintf(stderr,
256 						"Error: Unexpected test without subtests "
257 						"after same test had subtests\n");
258 					free(binary);
259 					fclose(f);
260 					return false;
261 				}
262 				entry.subtest_count++;
263 				entry.subtests = realloc(entry.subtests,
264 							 entry.subtest_count *
265 							 sizeof(*entry.subtests));
266 				entry.subtests[entry.subtest_count - 1] = strdup(delim);
267 				free(binary);
268 				binary = NULL;
269 				continue;
270 			}
271 
272 			if (entry.binary) {
273 				add_job_list_entry(job_list, entry.binary, entry.subtests, entry.subtest_count);
274 				any = true;
275 			}
276 
277 			memset(&entry, 0, sizeof(entry));
278 			entry.binary = strdup(binary);
279 			if (delim) {
280 				entry.subtests = malloc(sizeof(*entry.subtests));
281 				entry.subtests[0] = strdup(delim);
282 				entry.subtest_count = 1;
283 			}
284 
285 			free(binary);
286 			binary = NULL;
287 		}
288 	}
289 
290 	if (entry.binary) {
291 		add_job_list_entry(job_list, entry.binary, entry.subtests, entry.subtest_count);
292 		any = true;
293 	}
294 
295 	free(line);
296 	fclose(f);
297 	return any;
298 }
299 
list_all_tests(struct job_list * lst)300 void list_all_tests(struct job_list *lst)
301 {
302 	char piglit_name[256];
303 
304 	for (size_t test_idx = 0; test_idx < lst->size; ++test_idx) {
305 		struct job_list_entry *current_entry = lst->entries + test_idx;
306 		char *binary = current_entry->binary;
307 
308 		if (current_entry->subtest_count == 0) {
309 			generate_piglit_name(binary, NULL,
310 					     piglit_name, sizeof(piglit_name));
311 			printf("%s\n", piglit_name);
312 			continue;
313 		}
314 		for (size_t subtest_idx = 0;
315 		    subtest_idx < current_entry->subtest_count;
316 		    ++subtest_idx) {
317 			generate_piglit_name(binary, current_entry->subtests[subtest_idx],
318 					     piglit_name, sizeof(piglit_name));
319 			printf("%s\n", piglit_name);
320 		}
321 	}
322 }
323 
lowercase(const char * str)324 static char *lowercase(const char *str)
325 {
326 	char *ret = malloc(strlen(str) + 1);
327 	char *q = ret;
328 
329 	while (*str) {
330 		if (isspace(*str))
331 			break;
332 
333 		*q++ = tolower(*str++);
334 	}
335 	*q = '\0';
336 
337 	return ret;
338 }
339 
generate_piglit_name(const char * binary,const char * subtest,char * namebuf,size_t namebuf_size)340 void generate_piglit_name(const char *binary, const char *subtest,
341 			  char *namebuf, size_t namebuf_size)
342 {
343 	char *lc_binary = lowercase(binary);
344 	char *lc_subtest = NULL;
345 
346 	if (!subtest) {
347 		snprintf(namebuf, namebuf_size, "igt@%s", lc_binary);
348 		free(lc_binary);
349 		return;
350 	}
351 
352 	lc_subtest = lowercase(subtest);
353 
354 	snprintf(namebuf, namebuf_size, "igt@%s@%s", lc_binary, lc_subtest);
355 
356 	free(lc_binary);
357 	free(lc_subtest);
358 }
359 
init_job_list(struct job_list * job_list)360 void init_job_list(struct job_list *job_list)
361 {
362 	memset(job_list, 0, sizeof(*job_list));
363 }
364 
free_job_list(struct job_list * job_list)365 void free_job_list(struct job_list *job_list)
366 {
367 	int i, k;
368 
369 	for (i = 0; i < job_list->size; i++) {
370 		struct job_list_entry *entry = &job_list->entries[i];
371 
372 		free(entry->binary);
373 		for (k = 0; k < entry->subtest_count; k++) {
374 			free(entry->subtests[k]);
375 		}
376 		free(entry->subtests);
377 	}
378 	free(job_list->entries);
379 	init_job_list(job_list);
380 }
381 
create_job_list(struct job_list * job_list,struct settings * settings)382 bool create_job_list(struct job_list *job_list,
383 		     struct settings *settings)
384 {
385 	int dirfd, fd;
386 	bool result;
387 
388 	if (!settings->test_root) {
389 		fprintf(stderr, "No test root set; this shouldn't happen\n");
390 		return false;
391 	}
392 
393 	free_job_list(job_list);
394 
395 	dirfd = open(settings->test_root, O_DIRECTORY | O_RDONLY);
396 	if (dirfd < 0) {
397 		fprintf(stderr, "Test directory %s cannot be opened\n", settings->test_root);
398 		return false;
399 	}
400 
401 	fd = openat(dirfd, "test-list.txt", O_RDONLY);
402 	if (fd < 0) {
403 		fprintf(stderr, "Cannot open %s/test-list.txt\n", settings->test_root);
404 		close(dirfd);
405 		return false;
406 	}
407 
408 	/*
409 	 * If a test_list is given (not to be confused with
410 	 * test-list.txt), we use it directly without making tests
411 	 * list their subtests. If include/exclude filters are given
412 	 * we filter them directly from the test_list.
413 	 */
414 	if (settings->test_list)
415 		result = job_list_from_test_list(job_list, settings);
416 	else
417 		result = filtered_job_list(job_list, settings, fd);
418 
419 	close(fd);
420 	close(dirfd);
421 
422 	return result;
423 }
424 
425 static char joblist_filename[] = "joblist.txt";
serialize_job_list(struct job_list * job_list,struct settings * settings)426 bool serialize_job_list(struct job_list *job_list, struct settings *settings)
427 {
428 	int dirfd, fd;
429 	size_t i, k;
430 	FILE *f;
431 
432 	if (!settings->results_path) {
433 		fprintf(stderr, "No results-path set; this shouldn't happen\n");
434 		return false;
435 	}
436 
437 	if ((dirfd = open(settings->results_path, O_DIRECTORY | O_RDONLY)) < 0) {
438 		mkdir(settings->results_path, 0777);
439 		if ((dirfd = open(settings->results_path, O_DIRECTORY | O_RDONLY)) < 0) {
440 			fprintf(stderr, "Creating results-path failed\n");
441 			return false;
442 		}
443 	}
444 
445 	if (!settings->overwrite &&
446 	    faccessat(dirfd, joblist_filename, F_OK, 0) == 0) {
447 		fprintf(stderr, "Job list file already exists and not overwriting\n");
448 		close(dirfd);
449 		return false;
450 	}
451 
452 	if (settings->overwrite &&
453 	    unlinkat(dirfd, joblist_filename, 0) != 0 &&
454 	    errno != ENOENT) {
455 		fprintf(stderr, "Error removing old job list\n");
456 		close(dirfd);
457 		return false;
458 	}
459 
460 	if ((fd = openat(dirfd, joblist_filename, O_CREAT | O_EXCL | O_WRONLY, 0666)) < 0) {
461 		fprintf(stderr, "Creating job list serialization file failed: %s\n", strerror(errno));
462 		close(dirfd);
463 		return false;
464 	}
465 
466 	f = fdopen(fd, "w");
467 	if (!f) {
468 		close(fd);
469 		close(dirfd);
470 		return false;
471 	}
472 
473 	for (i = 0; i < job_list->size; i++) {
474 		struct job_list_entry *entry = &job_list->entries[i];
475 		fputs(entry->binary, f);
476 
477 		if (entry->subtest_count) {
478 			const char *delim = "";
479 
480 			fprintf(f, " ");
481 
482 			for (k = 0; k < entry->subtest_count; k++) {
483 				fprintf(f, "%s%s", delim, entry->subtests[k]);
484 				delim = ",";
485 			}
486 		}
487 
488 		fprintf(f, "\n");
489 	}
490 
491 	if (settings->sync) {
492 		fsync(fd);
493 		fsync(dirfd);
494 	}
495 
496 	fclose(f);
497 	close(dirfd);
498 	return true;
499 }
500 
read_job_list(struct job_list * job_list,int dirfd)501 bool read_job_list(struct job_list *job_list, int dirfd)
502 {
503 	int fd;
504 	FILE *f;
505 	ssize_t read;
506 	char *line = NULL;
507 	size_t line_len = 0;
508 
509 	free_job_list(job_list);
510 
511 	if ((fd = openat(dirfd, joblist_filename, O_RDONLY)) < 0)
512 		return false;
513 
514 	f = fdopen(fd, "r");
515 	if (!f) {
516 		close(fd);
517 		return false;
518 	}
519 
520 	while ((read = getline(&line, &line_len, f))) {
521 		char *binary, *sublist, *comma;
522 		char **subtests = NULL;
523 		size_t num_subtests = 0, len;
524 
525 		if (read < 0) {
526 			if (errno == EINTR)
527 				continue;
528 			else
529 				break;
530 		}
531 
532 		len = strlen(line);
533 		if (len > 0 && line[len - 1] == '\n')
534 			line[len - 1] = '\0';
535 
536 		sublist = strchr(line, ' ');
537 		if (!sublist) {
538 			add_job_list_entry(job_list, strdup(line), NULL, 0);
539 			continue;
540 		}
541 
542 		*sublist++ = '\0';
543 		binary = strdup(line);
544 
545 		do {
546 			comma = strchr(sublist, ',');
547 			if (comma) {
548 				*comma++ = '\0';
549 			}
550 
551 			++num_subtests;
552 			subtests = realloc(subtests, num_subtests * sizeof(*subtests));
553 			subtests[num_subtests - 1] = strdup(sublist);
554 			sublist = comma;
555 		} while (comma != NULL);
556 
557 		add_job_list_entry(job_list, binary, subtests, num_subtests);
558 	}
559 
560 	free(line);
561 	fclose(f);
562 
563 	return true;
564 }
565