1 /*
2  * Copyright (c) The Piglit project 2007
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * on the rights to use, copy, modify, merge, publish, distribute, sub
8  * license, and/or sell copies of the Software, and to permit persons to whom
9  * the Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
18  * VA LINUX SYSTEM, IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21  * USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 #if defined(_WIN32)
25 #ifndef WIN32_LEAN_AND_MEAN
26 #define WIN32_LEAN_AND_MEAN
27 #endif
28 #include <windows.h>
29 #endif
30 
31 #ifdef __linux__
32 #include <sys/types.h>
33 #include <sys/syscall.h>
34 #endif
35 
36 #include <assert.h>
37 #include <math.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <errno.h>
42 #include <inttypes.h>
43 #include <time.h>
44 
45 #if defined(PIGLIT_HAS_POSIX_CLOCK_MONOTONIC) && defined(PIGLIT_HAS_POSIX_TIMER_NOTIFY_THREAD)
46 #include <pthread.h>
47 #include <signal.h>
48 #endif
49 
50 #include "config.h"
51 #if defined(HAVE_SYS_TIME_H) && defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_SETRLIMIT)
52 #include <sys/time.h>
53 #include <sys/resource.h>
54 #define USE_SETRLIMIT
55 #endif
56 
57 #if defined(HAVE_FCNTL_H) && defined(HAVE_SYS_STAT_H) && defined(HAVE_SYS_TYPES_H) && defined(HAVE_UNISTD_H) && !defined(_WIN32)
58 # include <sys/types.h>
59 # include <sys/stat.h>
60 # include <fcntl.h>
61 # include <unistd.h>
62 #else
63 # define USE_STDIO
64 #endif
65 
66 #if defined(HAVE_UNISTD_H)
67 #include <unistd.h>  // for usleep
68 #endif
69 
70 #ifdef _MSC_VER
71 #define strcasecmp _stricmp
72 #endif
73 
74 #include "piglit-util.h"
75 
76 
77 #ifndef HAVE_ASPRINTF
78 
79 /* Some versions of MinGW are missing _vscprintf's declaration, although they
80  * still provide the symbol in the import library.
81  */
82 #ifdef __MINGW32__
83 _CRTIMP int _vscprintf(const char *format, va_list argptr);
84 #endif
85 
asprintf(char ** strp,const char * fmt,...)86 int asprintf(char **strp, const char *fmt, ...)
87 {
88 	va_list args;
89 	va_list args_copy;
90 	int length;
91 	size_t size;
92 
93 	va_start(args, fmt);
94 
95 	va_copy(args_copy, args);
96 
97 #ifdef _WIN32
98 	/* We need to use _vcsprintf to calculate the length as vsnprintf returns -1
99 	 * if the number of characters to write is greater than count.
100 	 */
101 	length = _vscprintf(fmt, args_copy);
102 #else
103 	char dummy;
104 	length = vsnprintf(&dummy, sizeof dummy, fmt, args_copy);
105 #endif
106 
107 	va_end(args_copy);
108 
109 	assert(length >= 0);
110 	size = length + 1;
111 
112 	*strp = malloc(size);
113 	if (!*strp) {
114 		return -1;
115 	}
116 
117 	va_start(args, fmt);
118 	vsnprintf(*strp, size, fmt, args);
119 	va_end(args);
120 
121 	return length;
122 }
123 
124 #endif /* HAVE_ASPRINTF */
125 
126 #if defined(__FreeBSD__) || defined(__DragonFly__)
127 #include <libgen.h>	/* For basename(3) */
128 #endif
129 
130 /**
131  * \brief Split \a string into an array of strings.
132  *
133  * The null-terminated string \a separators is a list of characters at
134  * which to perform the splits. For example, if separators is " ,", then
135  * the function will split the string at each occurence of ' ' and ','.
136  */
137 const char**
piglit_split_string_to_array(const char * string,const char * separators)138 piglit_split_string_to_array(const char *string, const char *separators)
139 {
140 	char **strings, *string_copy;
141 	int i, length, max_words;
142 
143 	length = strlen(string);
144 	max_words = length / 2;
145 	strings = malloc((sizeof(char*) * (max_words + 1)) +
146 	                 (sizeof(char) * (length + 1)));
147 	assert(strings != NULL);
148 
149 	string_copy = (char*) &strings[max_words + 1];
150 	strcpy(string_copy, string);
151 
152 	strings[0] = strtok(string_copy, separators);
153 	for (i = 0; strings[i] != NULL; ++i) {
154 		strings[i + 1] = strtok(NULL, separators);
155 	}
156 
157 	return (const char**) strings;
158 }
159 
piglit_is_extension_in_array(const char ** haystack,const char * needle)160 bool piglit_is_extension_in_array(const char **haystack, const char *needle)
161 {
162 	if (needle[0] == 0)
163 		return false;
164 
165 	while (*haystack != NULL) {
166 		if (strcmp(*haystack, needle) == 0) {
167 			return true;
168 		}
169 		haystack++;
170 	}
171 
172 	return false;
173 }
174 
piglit_is_extension_in_string(const char * haystack,const char * needle)175 bool piglit_is_extension_in_string(const char *haystack, const char *needle)
176 {
177 	const unsigned needle_len = strlen(needle);
178 
179 	if (needle_len == 0)
180 		return false;
181 
182 	while (true) {
183 		const char *const s = strstr(haystack, needle);
184 
185 		if (s == NULL)
186 			return false;
187 
188 		if (s[needle_len] == ' ' || s[needle_len] == '\0') {
189 			return true;
190 		}
191 
192 		/* strstr found an extension whose name begins with
193 		 * needle, but whose name is not equal to needle.
194 		 * Restart the search at s + needle_len so that we
195 		 * don't just find the same extension again and go
196 		 * into an infinite loop.
197 		 */
198 		haystack = s + needle_len;
199 	}
200 
201 	return false;
202 }
203 
204 /** Returns the line in the program string given the character position. */
piglit_find_line(const char * program,int position)205 int piglit_find_line(const char *program, int position)
206 {
207 	int i, line = 1;
208 	for (i = 0; i < position; i++) {
209 		if (program[i] == '0')
210 			return -1; /* unknown line */
211 		if (program[i] == '\n')
212 			line++;
213 	}
214 	return line;
215 }
216 
217 const char *
piglit_result_to_string(enum piglit_result result)218 piglit_result_to_string(enum piglit_result result)
219 {
220         switch (result) {
221         case PIGLIT_FAIL: return "fail";
222         case PIGLIT_SKIP: return "skip";
223         case PIGLIT_WARN: return "warn";
224         case PIGLIT_PASS: return "pass";
225         }
226         return "Unknown result";
227 }
228 
229 void
piglit_report_result(enum piglit_result result)230 piglit_report_result(enum piglit_result result)
231 {
232 	const char *result_str = piglit_result_to_string(result);
233 
234 #ifdef PIGLIT_HAS_POSIX_TIMER_NOTIFY_THREAD
235 	/* Ensure we only report one result in case we race with timeout */
236 	static pthread_mutex_t result_lock = PTHREAD_MUTEX_INITIALIZER;
237 	pthread_mutex_lock(&result_lock);
238 #endif
239 
240 	fflush(stderr);
241 
242 	printf("PIGLIT: {\"result\": \"%s\" }\n", result_str);
243 	fflush(stdout);
244 
245 	switch(result) {
246 	case PIGLIT_PASS:
247 	case PIGLIT_SKIP:
248 	case PIGLIT_WARN:
249 		exit(0);
250 	default:
251 		exit(1);
252 	}
253 }
254 
255 #ifdef PIGLIT_HAS_POSIX_TIMER_NOTIFY_THREAD
256 static void
timeout_expired(union sigval val)257 timeout_expired(union sigval val)
258 {
259 	piglit_loge("Test timed out.");
260 	piglit_report_result(val.sival_int);
261 }
262 #endif
263 
264 void
piglit_set_timeout(double seconds,enum piglit_result timeout_result)265 piglit_set_timeout(double seconds, enum piglit_result timeout_result)
266 {
267 #ifdef PIGLIT_HAS_POSIX_TIMER_NOTIFY_THREAD
268 	struct sigevent sev = {
269 		.sigev_notify = SIGEV_THREAD,
270 		.sigev_notify_function = timeout_expired,
271 		.sigev_value = { .sival_int = timeout_result },
272 	};
273 	time_t sec = seconds;
274 	struct itimerspec spec = {
275 		.it_value = { .tv_sec = sec, .tv_nsec = (seconds - sec) * 1e9 },
276 	};
277 	timer_t timerid;
278 	timer_create(CLOCK_MONOTONIC, &sev, &timerid);
279 	timer_settime(timerid, 0, &spec, NULL);
280 #else
281 	piglit_logi("Cannot abort this test for timeout on this platform");
282 #endif
283 }
284 
285 void
piglit_report_subtest_result(enum piglit_result result,const char * format,...)286 piglit_report_subtest_result(enum piglit_result result, const char *format, ...)
287 {
288 	const char *result_str = piglit_result_to_string(result);
289 	va_list ap;
290 
291 	va_start(ap, format);
292 
293 	printf("PIGLIT: {\"subtest\": {\"");
294 	vprintf(format, ap);
295 	printf("\" : \"%s\"}}\n", result_str);
296 	fflush(stdout);
297 
298 	va_end(ap);
299 }
300 
301 
302 static void
piglit_disable_error_message_boxes(void)303 piglit_disable_error_message_boxes(void)
304 {
305 	/* When Windows' error message boxes are disabled for this process (as
306 	 * is always the case when running through `piglit run`) we disable CRT
307 	 * message boxes too.
308 	 *
309 	 * This will disable the CRT message boxes for the main executable, but
310 	 * it will not disable message boxes for assertion failures inside
311 	 * OpenGL ICD, unless this test's executable and the OpenGL ICD DLL are
312 	 * both dynamically linked to the same CRT DLL.  If the OpenGL ICD is
313 	 * statically linked to the CRT then it must do these calls itself.
314 	 */
315 #ifdef _WIN32
316 	UINT uMode;
317 #if _WIN32_WINNT >= 0x0600
318 	uMode = GetErrorMode();
319 #else
320 	uMode = SetErrorMode(0);
321 	SetErrorMode(uMode);
322 #endif
323 	if (uMode & SEM_FAILCRITICALERRORS) {
324 		/* Disable assertion failure message box.
325 		 * http://msdn.microsoft.com/en-us/library/sas1dkb2.aspx
326 		 */
327 		_set_error_mode(_OUT_TO_STDERR);
328 	}
329 #endif /* _WIN32 */
330 }
331 
332 
333 static void
piglit_set_line_buffering(void)334 piglit_set_line_buffering(void)
335 {
336 	/* Windows doesn't immediately flush stdout/stderr after printf
337 	 * calls as we see on Linux.  To get immediate flushing, we disable
338 	 * buffering here.
339 	 */
340 #ifdef _WIN32
341 	setbuf(stdout, NULL);
342 	setbuf(stderr, NULL);
343 #endif
344 }
345 
346 
347 void
piglit_general_init(void)348 piglit_general_init(void)
349 {
350 	piglit_disable_error_message_boxes();
351 	piglit_set_line_buffering();
352 }
353 
354 
355 void
piglit_set_rlimit(unsigned long lim)356 piglit_set_rlimit(unsigned long lim)
357 {
358 #if defined(USE_SETRLIMIT) && defined(RLIMIT_AS)
359 	struct rlimit rl;
360 	if (getrlimit(RLIMIT_AS, &rl) != -1) {
361 		piglit_logi("Address space limit = %lu, max = %lu",
362 		       (unsigned long) rl.rlim_cur,
363 		       (unsigned long) rl.rlim_max);
364 
365 		if (rl.rlim_max > lim) {
366 			piglit_logi("Resetting limit to %lu", lim);
367 
368 			rl.rlim_cur = lim;
369 			rl.rlim_max = lim;
370 			if (setrlimit(RLIMIT_AS, &rl) == -1) {
371 				piglit_loge("Could not set rlimit "
372 				       "due to: %s (%d)",
373 				       strerror(errno), errno);
374 			}
375 		}
376 	}
377 #else
378 	piglit_loge("Cannot reset rlimit on this platform");
379 #endif
380 }
381 
382 /* Merges the PASS/FAIL/SKIP for @subtest into the overall result
383  * @all.
384  *
385  * The @all should start out initialized to PIGLIT_SKIP.
386  */
387 void
piglit_merge_result(enum piglit_result * all,enum piglit_result subtest)388 piglit_merge_result(enum piglit_result *all, enum piglit_result subtest)
389 {
390 	switch (subtest) {
391 	case PIGLIT_FAIL:
392 		*all = PIGLIT_FAIL;
393 		break;
394 	case PIGLIT_WARN:
395 		if (*all == PIGLIT_SKIP || *all == PIGLIT_PASS)
396 			*all = PIGLIT_WARN;
397 		break;
398 	case PIGLIT_PASS:
399 		if (*all == PIGLIT_SKIP)
400 			*all = PIGLIT_PASS;
401 		break;
402 	case PIGLIT_SKIP:
403 		break;
404 	}
405 }
406 
piglit_load_text_file(const char * file_name,unsigned * size)407 char *piglit_load_text_file(const char *file_name, unsigned *size)
408 {
409 	char *text = NULL;
410 
411 #if defined(USE_STDIO)
412 	FILE *fp;
413 
414 # ifdef HAVE_FOPEN_S
415 	errno_t err;
416 
417 	if (file_name == NULL) {
418 		return NULL;
419 	}
420 
421 	err = fopen_s(&fp, file_name, "r");
422 
423 	if (err || (fp == NULL)) {
424 		return NULL;
425 	}
426 # else
427 	fp = fopen(file_name, "r");
428 	if (fp == NULL) {
429 		return NULL;
430 	}
431 # endif
432 
433 	if (fseek(fp, 0, SEEK_END) == 0) {
434 		size_t len = (size_t) ftell(fp);
435 		rewind(fp);
436 
437 		text = malloc(len + 1);
438 		if (text != NULL) {
439 			size_t total_read = 0;
440 
441 			do {
442 				size_t bytes = fread(text + total_read, 1,
443 						     len - total_read, fp);
444 
445 				total_read += bytes;
446 				if (feof(fp)) {
447 					break;
448 				}
449 
450 				if (ferror(fp)) {
451 					free(text);
452 					text = NULL;
453 					break;
454 				}
455 			} while (total_read < len);
456 
457 			if (text != NULL) {
458 				text[total_read] = '\0';
459 			}
460 
461 			if (size != NULL) {
462 				*size = total_read;
463 			}
464 		}
465 	}
466 
467 	fclose(fp);
468 	return text;
469 #else
470 	struct stat st;
471 	int fd = open(file_name, O_RDONLY);
472 
473 	if (fd < 0) {
474 		return NULL;
475 	}
476 
477 	if (fstat(fd, & st) == 0) {
478 		ssize_t total_read = 0;
479 
480                 if (!S_ISREG(st.st_mode) &&
481                     !S_ISLNK(st.st_mode)) {
482                    /* not a regular file or symlink */
483                    close(fd);
484                    return NULL;
485                 }
486 
487 		text = malloc(st.st_size + 1);
488 		if (text != NULL) {
489 			do {
490 				ssize_t bytes = read(fd, text + total_read,
491 						     st.st_size - total_read);
492 				if (bytes < 0) {
493 					free(text);
494 					text = NULL;
495 					break;
496 				}
497 
498 				if (bytes == 0) {
499 					break;
500 				}
501 
502 				total_read += bytes;
503 			} while (total_read < st.st_size);
504 
505 			text[total_read] = '\0';
506 			if (size != NULL) {
507 				*size = total_read;
508 			}
509 		}
510 	}
511 
512 	close(fd);
513 
514 	return text;
515 #endif
516 }
517 
518 const char*
piglit_source_dir(void)519 piglit_source_dir(void)
520 {
521 
522     const char *s = getenv("PIGLIT_SOURCE_DIR");
523 
524     if (s == NULL) {
525         piglit_loge("env var PIGLIT_SOURCE_DIR is undefined");
526         piglit_report_result(PIGLIT_FAIL);
527     }
528 
529     return s;
530 }
531 
532 size_t
piglit_join_paths(char buf[],size_t buf_size,int n,...)533 piglit_join_paths(char buf[], size_t buf_size, int n, ...)
534 {
535 	char *dest = buf;
536 	size_t size_written = 0;
537 
538 	int i;
539 	va_list va;
540 
541 	if (buf_size  == 0 || n < 1)
542 		return 0;
543 
544 	va_start(va, n);
545 
546 	i = 0;
547 	while (true) {
548 		const char *p = va_arg(va, const char*);
549 
550 		while (*p != 0) {
551 			if (size_written == buf_size - 1)
552 				goto write_null;
553 
554 			*dest = *p;
555 			++dest;
556 			++p;
557 			++size_written;
558 		}
559 
560 		++i;
561 		if (i == n)
562 			break;
563 
564 		*dest = PIGLIT_PATH_SEP;
565 		++dest;
566 		++size_written;
567 	}
568 
569 write_null:
570 	*dest = '\0';
571 	++size_written;
572 
573 	va_end(va);
574 	return size_written;
575 }
576 
577 bool
piglit_time_is_monotonic(void)578 piglit_time_is_monotonic(void)
579 {
580 #ifdef PIGLIT_HAS_POSIX_CLOCK_MONOTONIC
581 	struct timespec t;
582 	int r = clock_gettime(CLOCK_MONOTONIC, &t);
583 
584 	return r == 0 || (r == -1 && errno != EINVAL);
585 #else
586 	return false;
587 #endif
588 }
589 
590 int64_t
piglit_time_get_nano(void)591 piglit_time_get_nano(void)
592 {
593 #if !defined(_WIN32)
594 	struct timeval tv;
595 
596 #ifdef PIGLIT_HAS_POSIX_CLOCK_MONOTONIC
597 	struct timespec t;
598 	int r = clock_gettime(CLOCK_MONOTONIC, &t);
599 
600 	if (r == 0 || (r == -1 && errno != EINVAL))
601 		return (t.tv_sec * INT64_C(1000000000)) + t.tv_nsec;
602 #endif
603 
604 	gettimeofday(&tv, NULL);
605 	return tv.tv_usec * INT64_C(1000) + tv.tv_sec * INT64_C(1000000000);
606 #else
607 	static LARGE_INTEGER frequency;
608 	LARGE_INTEGER counter;
609 
610 	if (!frequency.QuadPart)
611 		QueryPerformanceFrequency(&frequency);
612 	QueryPerformanceCounter(&counter);
613 	return counter.QuadPart * INT64_C(1000000000)/frequency.QuadPart;
614 #endif
615 }
616 
617 int64_t
piglit_delay_ns(int64_t time_ns)618 piglit_delay_ns(int64_t time_ns)
619 {
620 	int64_t start = piglit_time_get_nano();
621 	int64_t end;
622 
623 #ifdef __linux__
624 	struct timespec ts;
625 
626 	ts.tv_sec = time_ns / 1000000000LL;
627 	ts.tv_nsec = time_ns - ts.tv_sec * 1000000000LL;
628 
629 	while (nanosleep(&ts, &ts) == -1 && errno == EINTR)
630 		;
631 #elif defined(_MSC_VER)
632 	Sleep(time_ns / 1000000);
633 #else
634 	usleep(time_ns / 1000);
635 #endif
636 
637 	end = piglit_time_get_nano();
638 
639 	return end - start;
640 }
641 
642 /**
643  * Search for an argument with the given name in the argument list.
644  * If it is found, remove it and return true.
645  */
646 bool
piglit_strip_arg(int * argc,char * argv[],const char * arg)647 piglit_strip_arg(int *argc, char *argv[], const char *arg)
648 {
649         int i;
650         for (i = 1; i < *argc; i++) {
651                 if (strcmp(argv[i], arg) != 0)
652                 	continue;
653 
654                 for (i += 1; i < *argc; ++i)
655 			argv[i-1] = argv[i];
656 
657 		*argc -= 1;
658 		return true;
659 	}
660 
661         return false;
662 }
663 
664 void
piglit_parse_subtest_args(int * argc,char * argv[],const struct piglit_subtest * subtests,const char *** out_selected_subtests,size_t * out_num_selected_subtests)665 piglit_parse_subtest_args(int *argc, char *argv[],
666 			  const struct piglit_subtest *subtests,
667 			  const char ***out_selected_subtests,
668 			  size_t *out_num_selected_subtests)
669 {
670 	int j;
671 	const char **selected_subtests = NULL;
672 	size_t num_selected_subtests = 0;
673 
674 	const char *usage =
675 		"usage:\n"
676 		"  %1$s\n"
677 		"      Run all subtests.\n"
678 		"\n"
679 		"  %1$s -list-subtests\n"
680 		"      List all subtests.\n"
681 		"\n"
682 		"  %1$s -subtest SUBTEST [-subtest SUBTEST [...]]\n"
683 		"      Run only the given subtests.\n"
684 		"\n"
685 		"  %1$s -h|--help\n"
686 		"      Print this help message.\n"
687 		;
688 
689 	for (j = 1; j < *argc; j++) {
690 		if (streq(argv[j], "-h") || streq(argv[j], "--help")) {
691 			printf(usage, basename(argv[0]));
692 			exit(EXIT_SUCCESS);
693 		} else if (streq(argv[j], "-subtest")) {
694 			int i;
695 
696 			++j;
697 			if (j >= *argc) {
698 				piglit_loge("-subtest requires an argument");
699 				piglit_report_result(PIGLIT_FAIL);
700 			}
701 
702 			if (!piglit_find_subtest(subtests, argv[j])) {
703 				piglit_loge("Test defines no subtest with "
704 					"name '%s'", argv[j]);
705 				piglit_report_result(PIGLIT_FAIL);
706 			}
707 
708 			selected_subtests =
709 				realloc(selected_subtests,
710 					(num_selected_subtests + 1)
711 					* sizeof(char*));
712 			selected_subtests[num_selected_subtests] = argv[j];
713 			++num_selected_subtests;
714 
715 			/* Remove 2 arguments from the command line. */
716 			for (i = j + 1; i < *argc; i++) {
717 				argv[i - 2] = argv[i];
718 			}
719 			*argc -= 2;
720 			j -= 2;
721 		} else if (streq(argv[j], "-list-subtests")) {
722 			int i;
723 
724 			if (subtests == NULL) {
725 				piglit_loge("Test defines no subtests!");
726 				exit(EXIT_FAILURE);
727 			}
728 
729 			for (i = 0; !PIGLIT_SUBTEST_END(&subtests[i]); ++i) {
730 				printf("%s: %s\n",
731 				       subtests[i].option,
732 				       subtests[i].name);
733 			}
734 
735 			exit(EXIT_SUCCESS);
736 		}
737 	}
738 
739 	*out_selected_subtests = selected_subtests;
740 	*out_num_selected_subtests = num_selected_subtests;
741 }
742 
743 
744 const struct piglit_subtest *
piglit_find_subtest(const struct piglit_subtest * subtests,const char * name)745 piglit_find_subtest(const struct piglit_subtest *subtests, const char *name)
746 {
747 	unsigned i;
748 
749 	for (i = 0; !PIGLIT_SUBTEST_END(&subtests[i]); i++) {
750 		if (strcmp(subtests[i].option, name) == 0)
751 			return &subtests[i];
752 	}
753 
754 	return NULL;
755 }
756 
757 enum piglit_result
piglit_run_selected_subtests(const struct piglit_subtest * all_subtests,const char ** selected_subtests,size_t num_selected_subtests,enum piglit_result previous_result)758 piglit_run_selected_subtests(const struct piglit_subtest *all_subtests,
759 			     const char **selected_subtests,
760 			     size_t num_selected_subtests,
761 			     enum piglit_result previous_result)
762 {
763 	enum piglit_result result = previous_result;
764 
765 	/* print JSON list of subtests */
766 	printf("PIGLIT: {\"enumerate subtests\": [");
767 	if (num_selected_subtests) {
768 		const char *prefix = "";
769 		for (int i = 0; i < num_selected_subtests; i++) {
770 			const char *const name = selected_subtests[i];
771 			const struct piglit_subtest *subtest =
772 				piglit_find_subtest(all_subtests, name);
773 
774 			if (subtest == NULL) {
775 				printf("]}\n");
776 				fflush(stdout);
777 				piglit_loge("Unknown subtest \"%s\"", name);
778 				piglit_report_result(PIGLIT_FAIL);
779 			}
780 			printf("%s\"%s\"", prefix, name);
781 			prefix = ", ";
782 
783 		}
784 	} else {
785 		const char *prefix = "";
786 		for (int i = 0; !PIGLIT_SUBTEST_END(&all_subtests[i]); i++) {
787 			printf("%s\"%s\"", prefix, all_subtests[i].name);
788 			prefix = ", ";
789 		}
790 	}
791 	printf("]}\n");
792 	fflush(stdout);
793 
794 	if (num_selected_subtests) {
795 		for (int i = 0; i < num_selected_subtests; i++) {
796 			enum piglit_result subtest_result;
797 			const char *const name = selected_subtests[i];
798 			const struct piglit_subtest *subtest =
799 				piglit_find_subtest(all_subtests, name);
800 
801 			subtest_result = subtest->subtest_func(subtest->data);
802 			piglit_report_subtest_result(subtest_result, "%s",
803 						     subtest->name);
804 
805 			piglit_merge_result(&result, subtest_result);
806 		}
807 	} else {
808 		for (int i = 0; !PIGLIT_SUBTEST_END(&all_subtests[i]); i++) {
809 			const enum piglit_result subtest_result =
810 				all_subtests[i].subtest_func(all_subtests[i].data);
811 			piglit_report_subtest_result(subtest_result, "%s",
812 						     all_subtests[i].name);
813 
814 			piglit_merge_result(&result, subtest_result);
815 		}
816 	}
817 
818 	return result;
819 }
820 
821 void
piglit_register_subtests(const char * names[])822 piglit_register_subtests(const char *names[])
823 {
824 	printf("PIGLIT: {\"enumerate subtests\": [\"%s\"", names[0]);
825 	for (int i = 1; names[i]; i++) {
826 		printf(", \"%s\"", names[i]);
827 	}
828 	printf("]}\n");
829 	fflush(stdout);
830 }
831 
832 uint64_t
piglit_gettid(void)833 piglit_gettid(void)
834 {
835 #ifdef __linux__
836 	return syscall(SYS_gettid);
837 #else
838 	return 0;
839 #endif
840 }
841 
842 
843 size_t
piglit_get_page_size(void)844 piglit_get_page_size(void)
845 {
846 #if defined(_WIN32)
847 	SYSTEM_INFO system_info;
848 	GetSystemInfo(&system_info);
849 	return system_info.dwPageSize;
850 #else
851 	return sysconf(_SC_PAGESIZE);
852 #endif
853 }
854 
855 
856 void *
piglit_alloc_aligned(size_t alignment,size_t size)857 piglit_alloc_aligned(size_t alignment, size_t size)
858 {
859 #if defined(_WIN32)
860 	return _aligned_malloc(size, alignment);
861 #else
862 	void *p;
863 	if (posix_memalign(&p, alignment, size) != 0) {
864 		return NULL;
865 	}
866 	return p;
867 #endif
868 }
869 
870 
871 void
piglit_free_aligned(void * p)872 piglit_free_aligned(void *p)
873 {
874 #if defined(_WIN32)
875 	_aligned_free(p);
876 #else
877 	free(p);
878 #endif
879 }
880 
881 
882 /**
883  * \brief Reads an environment variable and interprets its value as a boolean.
884  *
885  * Recognizes 0/false/no and 1/true/yes.  Other values result in the
886  * \a default_value.
887  */
888 bool
piglit_env_var_as_boolean(const char * var_name,bool default_value)889 piglit_env_var_as_boolean(const char *var_name, bool default_value)
890 {
891    const char *str = getenv(var_name);
892    if (str == NULL)
893       return default_value;
894 
895    if (strcmp(str, "1") == 0 ||
896        strcasecmp(str, "true") == 0 ||
897        strcasecmp(str, "yes") == 0) {
898       return true;
899    } else if (strcmp(str, "0") == 0 ||
900               strcasecmp(str, "false") == 0 ||
901               strcasecmp(str, "no") == 0) {
902       return false;
903    } else {
904       return default_value;
905    }
906 }
907