framework: call destroy directly instead of using atexit
[piglit.git] / tests / util / piglit-util.c
blobe91982756af80766b71dbe95b138f918dd850837
1 /*
2 * Copyright (c) The Piglit project 2007
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:
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.
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.
24 #if defined(_WIN32)
25 #ifndef WIN32_LEAN_AND_MEAN
26 #define WIN32_LEAN_AND_MEAN
27 #endif
28 #include <windows.h>
29 #endif
31 #ifdef __linux__
32 #include <sys/types.h>
33 #include <sys/syscall.h>
34 #endif
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>
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
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
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
66 #if defined(HAVE_UNISTD_H)
67 #include <unistd.h> // for usleep
68 #endif
70 #ifdef _MSC_VER
71 #define strcasecmp _stricmp
72 #endif
74 #include "piglit-util.h"
77 #ifndef HAVE_ASPRINTF
79 /* Some versions of MinGW are missing _vscprintf's declaration, although they
80 * still provide the symbol in the import library.
82 #ifdef __MINGW32__
83 _CRTIMP int _vscprintf(const char *format, va_list argptr);
84 #endif
86 int asprintf(char **strp, const char *fmt, ...)
88 va_list args;
89 va_list args_copy;
90 int length;
91 size_t size;
93 va_start(args, fmt);
95 va_copy(args_copy, args);
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.
101 length = _vscprintf(fmt, args_copy);
102 #else
103 char dummy;
104 length = vsnprintf(&dummy, sizeof dummy, fmt, args_copy);
105 #endif
107 va_end(args_copy);
109 assert(length >= 0);
110 size = length + 1;
112 *strp = malloc(size);
113 if (!*strp) {
114 return -1;
117 va_start(args, fmt);
118 vsnprintf(*strp, size, fmt, args);
119 va_end(args);
121 return length;
124 #endif /* HAVE_ASPRINTF */
127 * \brief Split \a string into an array of strings.
129 * The null-terminated string \a separators is a list of characters at
130 * which to perform the splits. For example, if separators is " ,", then
131 * the function will split the string at each occurrence of ' ' and ','.
133 const char**
134 piglit_split_string_to_array(const char *string, const char *separators)
136 char **strings, *string_copy;
137 int i, length, max_words;
139 length = strlen(string);
140 max_words = length / 2;
141 strings = malloc((sizeof(char*) * (max_words + 1)) +
142 (sizeof(char) * (length + 1)));
143 assert(strings != NULL);
145 string_copy = (char*) &strings[max_words + 1];
146 strcpy(string_copy, string);
148 strings[0] = strtok(string_copy, separators);
149 for (i = 0; strings[i] != NULL; ++i) {
150 strings[i + 1] = strtok(NULL, separators);
153 return (const char**) strings;
156 bool piglit_is_extension_in_array(const char **haystack, const char *needle)
158 if (needle[0] == 0)
159 return false;
161 while (*haystack != NULL) {
162 if (strcmp(*haystack, needle) == 0) {
163 return true;
165 haystack++;
168 return false;
171 bool piglit_is_extension_in_string(const char *haystack, const char *needle)
173 const unsigned needle_len = strlen(needle);
175 if (needle_len == 0)
176 return false;
178 while (true) {
179 const char *const s = strstr(haystack, needle);
181 if (s == NULL)
182 return false;
184 if (s[needle_len] == ' ' || s[needle_len] == '\0') {
185 return true;
188 /* strstr found an extension whose name begins with
189 * needle, but whose name is not equal to needle.
190 * Restart the search at s + needle_len so that we
191 * don't just find the same extension again and go
192 * into an infinite loop.
194 haystack = s + needle_len;
197 return false;
200 /** Returns the line in the program string given the character position. */
201 int piglit_find_line(const char *program, int position)
203 int i, line = 1;
204 for (i = 0; i < position; i++) {
205 if (program[i] == '0')
206 return -1; /* unknown line */
207 if (program[i] == '\n')
208 line++;
210 return line;
213 const char *
214 piglit_result_to_string(enum piglit_result result)
216 switch (result) {
217 case PIGLIT_FAIL: return "fail";
218 case PIGLIT_SKIP: return "skip";
219 case PIGLIT_WARN: return "warn";
220 case PIGLIT_PASS: return "pass";
222 return "Unknown result";
225 static void (*destroy_func)(void*);
226 static void *destroy_data;
228 void
229 piglit_report_result(enum piglit_result result)
231 const char *result_str = piglit_result_to_string(result);
233 #ifdef PIGLIT_HAS_POSIX_TIMER_NOTIFY_THREAD
234 /* Ensure we only report one result in case we race with timeout */
235 static pthread_mutex_t result_lock = PTHREAD_MUTEX_INITIALIZER;
236 pthread_mutex_lock(&result_lock);
237 #endif
239 fflush(stderr);
241 printf("PIGLIT: {\"result\": \"%s\" }\n", result_str);
242 fflush(stdout);
244 if (destroy_func)
245 destroy_func(destroy_data);
246 switch(result) {
247 case PIGLIT_PASS:
248 case PIGLIT_SKIP:
249 case PIGLIT_WARN:
250 exit(0);
251 default:
252 exit(1);
256 #ifdef PIGLIT_HAS_POSIX_TIMER_NOTIFY_THREAD
257 static void
258 timeout_expired(union sigval val)
260 piglit_loge("Test timed out.");
261 piglit_report_result(val.sival_int);
263 #endif
265 void
266 piglit_set_timeout(double seconds, enum piglit_result timeout_result)
268 #ifdef PIGLIT_HAS_POSIX_TIMER_NOTIFY_THREAD
269 struct sigevent sev = {
270 .sigev_notify = SIGEV_THREAD,
271 .sigev_notify_function = timeout_expired,
272 .sigev_value = { .sival_int = timeout_result },
274 time_t sec = seconds;
275 struct itimerspec spec = {
276 .it_value = { .tv_sec = sec, .tv_nsec = (seconds - sec) * 1e9 },
278 timer_t timerid;
279 timer_create(CLOCK_MONOTONIC, &sev, &timerid);
280 timer_settime(timerid, 0, &spec, NULL);
281 #else
282 piglit_logi("Cannot abort this test for timeout on this platform");
283 #endif
286 void
287 piglit_report_subtest_result(enum piglit_result result, const char *format, ...)
289 const char *result_str = piglit_result_to_string(result);
290 va_list ap;
292 va_start(ap, format);
294 printf("PIGLIT: {\"subtest\": {\"");
295 vprintf(format, ap);
296 printf("\" : \"%s\"}}\n", result_str);
297 fflush(stdout);
299 va_end(ap);
303 static void
304 piglit_disable_error_message_boxes(void)
306 /* When Windows' error message boxes are disabled for this process (as
307 * is always the case when running through `piglit run`) we disable CRT
308 * message boxes too.
310 * This will disable the CRT message boxes for the main executable, but
311 * it will not disable message boxes for assertion failures inside
312 * OpenGL ICD, unless this test's executable and the OpenGL ICD DLL are
313 * both dynamically linked to the same CRT DLL. If the OpenGL ICD is
314 * statically linked to the CRT then it must do these calls itself.
316 #ifdef _WIN32
317 UINT uMode;
318 #if _WIN32_WINNT >= 0x0600
319 uMode = GetErrorMode();
320 #else
321 uMode = SetErrorMode(0);
322 SetErrorMode(uMode);
323 #endif
324 if (uMode & SEM_FAILCRITICALERRORS) {
325 /* Disable assertion failure message box.
326 * http://msdn.microsoft.com/en-us/library/sas1dkb2.aspx
328 _set_error_mode(_OUT_TO_STDERR);
330 #endif /* _WIN32 */
334 static void
335 piglit_set_line_buffering(void)
337 /* Windows doesn't immediately flush stdout/stderr after printf
338 * calls as we see on Linux. To get immediate flushing, we disable
339 * buffering here.
341 #ifdef _WIN32
342 setbuf(stdout, NULL);
343 setbuf(stderr, NULL);
344 #endif
348 void
349 piglit_general_init(void)
351 piglit_disable_error_message_boxes();
352 piglit_set_line_buffering();
356 void
357 piglit_set_rlimit(unsigned long lim)
359 #if defined(USE_SETRLIMIT) && defined(RLIMIT_AS)
360 struct rlimit rl;
361 if (getrlimit(RLIMIT_AS, &rl) != -1) {
362 piglit_logi("Address space limit = %lu, max = %lu",
363 (unsigned long) rl.rlim_cur,
364 (unsigned long) rl.rlim_max);
366 if (rl.rlim_max > lim) {
367 piglit_logi("Resetting limit to %lu", lim);
369 rl.rlim_cur = lim;
370 rl.rlim_max = lim;
371 if (setrlimit(RLIMIT_AS, &rl) == -1) {
372 piglit_loge("Could not set rlimit "
373 "due to: %s (%d)",
374 strerror(errno), errno);
378 #else
379 piglit_loge("Cannot reset rlimit on this platform");
380 #endif
383 /* Merges the PASS/FAIL/SKIP for @subtest into the overall result
384 * @all.
386 * The @all should start out initialized to PIGLIT_SKIP.
388 void
389 piglit_merge_result(enum piglit_result *all, enum piglit_result subtest)
391 switch (subtest) {
392 case PIGLIT_FAIL:
393 *all = PIGLIT_FAIL;
394 break;
395 case PIGLIT_WARN:
396 if (*all == PIGLIT_SKIP || *all == PIGLIT_PASS)
397 *all = PIGLIT_WARN;
398 break;
399 case PIGLIT_PASS:
400 if (*all == PIGLIT_SKIP)
401 *all = PIGLIT_PASS;
402 break;
403 case PIGLIT_SKIP:
404 break;
408 char *piglit_load_text_file(const char *file_name, unsigned *size)
410 char *text = NULL;
412 #if defined(USE_STDIO)
413 FILE *fp;
415 # ifdef HAVE_FOPEN_S
416 errno_t err;
418 if (file_name == NULL) {
419 return NULL;
422 err = fopen_s(&fp, file_name, "r");
424 if (err || (fp == NULL)) {
425 return NULL;
427 # else
428 fp = fopen(file_name, "r");
429 if (fp == NULL) {
430 return NULL;
432 # endif
434 if (fseek(fp, 0, SEEK_END) == 0) {
435 size_t len = (size_t) ftell(fp);
436 rewind(fp);
438 text = malloc(len + 1);
439 if (text != NULL) {
440 size_t total_read = 0;
442 do {
443 size_t bytes = fread(text + total_read, 1,
444 len - total_read, fp);
446 total_read += bytes;
447 if (feof(fp)) {
448 break;
451 if (ferror(fp)) {
452 free(text);
453 text = NULL;
454 break;
456 } while (total_read < len);
458 if (text != NULL) {
459 text[total_read] = '\0';
462 if (size != NULL) {
463 *size = total_read;
468 fclose(fp);
469 return text;
470 #else
471 struct stat st;
472 int fd = open(file_name, O_RDONLY);
474 if (fd < 0) {
475 return NULL;
478 if (fstat(fd, & st) == 0) {
479 ssize_t total_read = 0;
481 if (!S_ISREG(st.st_mode) &&
482 !S_ISLNK(st.st_mode)) {
483 /* not a regular file or symlink */
484 close(fd);
485 return NULL;
488 text = malloc(st.st_size + 1);
489 if (text != NULL) {
490 do {
491 ssize_t bytes = read(fd, text + total_read,
492 st.st_size - total_read);
493 if (bytes < 0) {
494 free(text);
495 text = NULL;
496 break;
499 if (bytes == 0) {
500 break;
503 total_read += bytes;
504 } while (total_read < st.st_size);
506 text[total_read] = '\0';
507 if (size != NULL) {
508 *size = total_read;
513 close(fd);
515 return text;
516 #endif
519 const char*
520 piglit_source_dir(void)
523 const char *s = getenv("PIGLIT_SOURCE_DIR");
525 if (s == NULL) {
526 piglit_loge("env var PIGLIT_SOURCE_DIR is undefined");
527 piglit_report_result(PIGLIT_FAIL);
530 return s;
533 size_t
534 piglit_join_paths(char buf[], size_t buf_size, int n, ...)
536 char *dest = buf;
537 size_t size_written = 0;
539 int i;
540 va_list va;
542 if (buf_size == 0 || n < 1)
543 return 0;
545 va_start(va, n);
547 i = 0;
548 while (true) {
549 const char *p = va_arg(va, const char*);
551 while (*p != 0) {
552 if (size_written == buf_size - 1)
553 goto write_null;
555 *dest = *p;
556 ++dest;
557 ++p;
558 ++size_written;
561 ++i;
562 if (i == n)
563 break;
565 *dest = PIGLIT_PATH_SEP;
566 ++dest;
567 ++size_written;
570 write_null:
571 *dest = '\0';
572 ++size_written;
574 va_end(va);
575 return size_written;
578 bool
579 piglit_time_is_monotonic(void)
581 #ifdef PIGLIT_HAS_POSIX_CLOCK_MONOTONIC
582 struct timespec t;
583 int r = clock_gettime(CLOCK_MONOTONIC, &t);
585 return r == 0 || (r == -1 && errno != EINVAL);
586 #else
587 return false;
588 #endif
591 int64_t
592 piglit_time_get_nano(void)
594 #if !defined(_WIN32)
595 struct timeval tv;
597 #ifdef PIGLIT_HAS_POSIX_CLOCK_MONOTONIC
598 struct timespec t;
599 int r = clock_gettime(CLOCK_MONOTONIC, &t);
601 if (r == 0 || (r == -1 && errno != EINVAL))
602 return (t.tv_sec * INT64_C(1000000000)) + t.tv_nsec;
603 #endif
605 gettimeofday(&tv, NULL);
606 return tv.tv_usec * INT64_C(1000) + tv.tv_sec * INT64_C(1000000000);
607 #else
608 static LARGE_INTEGER frequency;
609 LARGE_INTEGER counter;
611 if (!frequency.QuadPart)
612 QueryPerformanceFrequency(&frequency);
613 QueryPerformanceCounter(&counter);
614 return counter.QuadPart * INT64_C(1000000000)/frequency.QuadPart;
615 #endif
618 int64_t
619 piglit_delay_ns(int64_t time_ns)
621 int64_t start = piglit_time_get_nano();
622 int64_t end;
624 #ifdef __linux__
625 struct timespec ts;
627 ts.tv_sec = time_ns / 1000000000LL;
628 ts.tv_nsec = time_ns - ts.tv_sec * 1000000000LL;
630 while (nanosleep(&ts, &ts) == -1 && errno == EINTR)
632 #elif defined(_MSC_VER)
633 Sleep(time_ns / 1000000);
634 #else
635 usleep(time_ns / 1000);
636 #endif
638 end = piglit_time_get_nano();
640 return end - start;
644 * Search for an argument with the given name in the argument list.
645 * If it is found, remove it and return true.
647 bool
648 piglit_strip_arg(int *argc, char *argv[], const char *arg)
650 int i;
651 for (i = 1; i < *argc; i++) {
652 if (strcmp(argv[i], arg) != 0)
653 continue;
655 for (i += 1; i < *argc; ++i)
656 argv[i-1] = argv[i];
658 *argc -= 1;
659 return true;
662 return false;
665 void
666 piglit_parse_subtest_args(int *argc, char *argv[],
667 const struct piglit_subtest *subtests,
668 const char ***out_selected_subtests,
669 size_t *out_num_selected_subtests)
671 int j;
672 const char **selected_subtests = NULL;
673 size_t num_selected_subtests = 0;
675 const char *usage =
676 "usage:\n"
677 " %1$s\n"
678 " Run all subtests.\n"
679 "\n"
680 " %1$s -list-subtests\n"
681 " List all subtests.\n"
682 "\n"
683 " %1$s -subtest SUBTEST [-subtest SUBTEST [...]]\n"
684 " Run only the given subtests.\n"
685 "\n"
686 " %1$s -h|--help\n"
687 " Print this help message.\n"
690 for (j = 1; j < *argc; j++) {
691 if (streq(argv[j], "-h") || streq(argv[j], "--help")) {
692 printf(usage, basename(argv[0]));
693 exit(EXIT_SUCCESS);
694 } else if (streq(argv[j], "-subtest")) {
695 int i;
697 ++j;
698 if (j >= *argc) {
699 piglit_loge("-subtest requires an argument");
700 piglit_report_result(PIGLIT_FAIL);
703 if (!piglit_find_subtest(subtests, argv[j])) {
704 piglit_loge("Test defines no subtest with "
705 "name '%s'", argv[j]);
706 piglit_report_result(PIGLIT_FAIL);
709 selected_subtests =
710 realloc(selected_subtests,
711 (num_selected_subtests + 1)
712 * sizeof(char*));
713 selected_subtests[num_selected_subtests] = argv[j];
714 ++num_selected_subtests;
716 /* Remove 2 arguments from the command line. */
717 for (i = j + 1; i < *argc; i++) {
718 argv[i - 2] = argv[i];
720 *argc -= 2;
721 j -= 2;
722 } else if (streq(argv[j], "-list-subtests")) {
723 int i;
725 if (subtests == NULL) {
726 piglit_loge("Test defines no subtests!");
727 exit(EXIT_FAILURE);
730 for (i = 0; !PIGLIT_SUBTEST_END(&subtests[i]); ++i) {
731 printf("%s: %s\n",
732 subtests[i].option,
733 subtests[i].name);
736 exit(EXIT_SUCCESS);
740 *out_selected_subtests = selected_subtests;
741 *out_num_selected_subtests = num_selected_subtests;
745 const struct piglit_subtest *
746 piglit_find_subtest(const struct piglit_subtest *subtests, const char *name)
748 unsigned i;
749 if (!subtests)
750 return NULL;
751 for (i = 0; !PIGLIT_SUBTEST_END(&subtests[i]); i++) {
752 if (strcmp(subtests[i].option, name) == 0)
753 return &subtests[i];
756 return NULL;
759 enum piglit_result
760 piglit_run_selected_subtests(const struct piglit_subtest *all_subtests,
761 const char **selected_subtests,
762 size_t num_selected_subtests,
763 enum piglit_result previous_result)
765 enum piglit_result result = previous_result;
767 /* print JSON list of subtests */
768 printf("PIGLIT: {\"enumerate subtests\": [");
769 if (num_selected_subtests) {
770 const char *prefix = "";
771 for (int i = 0; i < num_selected_subtests; i++) {
772 const char *const name = selected_subtests[i];
773 const struct piglit_subtest *subtest =
774 piglit_find_subtest(all_subtests, name);
776 if (subtest == NULL) {
777 printf("]}\n");
778 fflush(stdout);
779 piglit_loge("Unknown subtest \"%s\"", name);
780 piglit_report_result(PIGLIT_FAIL);
782 printf("%s\"%s\"", prefix, name);
783 prefix = ", ";
786 } else {
787 const char *prefix = "";
788 for (int i = 0; !PIGLIT_SUBTEST_END(&all_subtests[i]); i++) {
789 printf("%s\"%s\"", prefix, all_subtests[i].name);
790 prefix = ", ";
793 printf("]}\n");
794 fflush(stdout);
796 if (num_selected_subtests) {
797 for (int i = 0; i < num_selected_subtests; i++) {
798 enum piglit_result subtest_result;
799 const char *const name = selected_subtests[i];
800 const struct piglit_subtest *subtest =
801 piglit_find_subtest(all_subtests, name);
803 subtest_result = subtest->subtest_func(subtest->data);
804 piglit_report_subtest_result(subtest_result, "%s",
805 subtest->name);
807 piglit_merge_result(&result, subtest_result);
809 } else {
810 for (int i = 0; !PIGLIT_SUBTEST_END(&all_subtests[i]); i++) {
811 const enum piglit_result subtest_result =
812 all_subtests[i].subtest_func(all_subtests[i].data);
813 piglit_report_subtest_result(subtest_result, "%s",
814 all_subtests[i].name);
816 piglit_merge_result(&result, subtest_result);
820 return result;
823 void
824 piglit_register_subtests(const char *names[])
826 printf("PIGLIT: {\"enumerate subtests\": [\"%s\"", names[0]);
827 for (int i = 1; names[i]; i++) {
828 printf(", \"%s\"", names[i]);
830 printf("]}\n");
831 fflush(stdout);
834 uint64_t
835 piglit_gettid(void)
837 #ifdef __linux__
838 return syscall(SYS_gettid);
839 #else
840 return 0;
841 #endif
845 size_t
846 piglit_get_page_size(void)
848 #if defined(_WIN32)
849 SYSTEM_INFO system_info;
850 GetSystemInfo(&system_info);
851 return system_info.dwPageSize;
852 #else
853 return sysconf(_SC_PAGESIZE);
854 #endif
858 void *
859 piglit_alloc_aligned(size_t alignment, size_t size)
861 #if defined(_WIN32)
862 return _aligned_malloc(size, alignment);
863 #else
864 void *p;
865 if (posix_memalign(&p, alignment, size) != 0) {
866 return NULL;
868 return p;
869 #endif
873 void
874 piglit_free_aligned(void *p)
876 #if defined(_WIN32)
877 _aligned_free(p);
878 #else
879 free(p);
880 #endif
885 * \brief Reads an environment variable and interprets its value as a boolean.
887 * Recognizes 0/false/no and 1/true/yes. Other values result in the
888 * \a default_value.
890 bool
891 piglit_env_var_as_boolean(const char *var_name, bool default_value)
893 const char *str = getenv(var_name);
894 if (str == NULL)
895 return default_value;
897 if (strcmp(str, "1") == 0 ||
898 strcasecmp(str, "true") == 0 ||
899 strcasecmp(str, "yes") == 0) {
900 return true;
901 } else if (strcmp(str, "0") == 0 ||
902 strcasecmp(str, "false") == 0 ||
903 strcasecmp(str, "no") == 0) {
904 return false;
905 } else {
906 return default_value;
910 void
911 piglit_set_destroy_func(void (*destroy)(void*), void *data)
913 destroy_func = destroy;
914 destroy_data = data;