diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.sources | 2 | ||||
-rw-r--r-- | lib/drmtest.c | 830 | ||||
-rw-r--r-- | lib/drmtest.h | 269 | ||||
-rw-r--r-- | lib/igt_core.c | 901 | ||||
-rw-r--r-- | lib/igt_core.h | 305 |
5 files changed, 1212 insertions, 1095 deletions
diff --git a/lib/Makefile.sources b/lib/Makefile.sources index ba4e8282..a3a679c1 100644 --- a/lib/Makefile.sources +++ b/lib/Makefile.sources @@ -41,5 +41,7 @@ libintel_tools_la_SOURCES = \ intel_iosf.c \ igt_kms.c \ igt_kms.h \ + igt_core.c \ + igt_core.h \ $(NULL) diff --git a/lib/drmtest.c b/lib/drmtest.c index b518b811..a5aac4dc 100644 --- a/lib/drmtest.c +++ b/lib/drmtest.c @@ -167,16 +167,6 @@ int drm_get_card(void) return -1; } -static void oom_adjust_for_doom(void) -{ - int fd; - const char always_kill[] = "1000"; - - fd = open("/proc/self/oom_score_adj", O_WRONLY); - igt_assert(fd != -1); - igt_assert(write(fd, always_kill, sizeof(always_kill)) == sizeof(always_kill)); -} - /** Open the first DRM device we can find, searching up to 16 device nodes */ static int __drm_open_any(void) { @@ -195,8 +185,6 @@ static int __drm_open_any(void) fd = -1; } - oom_adjust_for_doom(); - return fd; } @@ -226,8 +214,6 @@ static int __drm_open_any_render(void) return fd; } - oom_adjust_for_doom(); - return fd; } @@ -291,10 +277,6 @@ int drm_open_any_render(void) } /* signal interrupt helpers */ -static bool igt_only_list_subtests(void); - -static unsigned int exit_handler_count; - static struct igt_helper_process signal_helper; long long int sig_stat; static void __attribute__((noreturn)) signal_helper_process(pid_t pid) @@ -334,609 +316,7 @@ void igt_stop_signal_helper(void) sig_stat = 0; } -/* subtests helpers */ -static bool list_subtests = false; -static char *run_single_subtest = NULL; -static const char *in_subtest = NULL; -static bool in_fixture = false; -static bool test_with_subtests = false; -static enum { - CONT = 0, SKIP, FAIL -} skip_subtests_henceforth = CONT; - -/* fork support state */ -pid_t *test_children; -int num_test_children; -int test_children_sz; -bool test_child; - -bool __igt_fixture(void) -{ - assert(!in_fixture); - - if (igt_only_list_subtests()) - return false; - - if (skip_subtests_henceforth) - return false; - - in_fixture = true; - return true; -} - -void __igt_fixture_complete(void) -{ - assert(in_fixture); - - in_fixture = false; -} - -void __igt_fixture_end(void) -{ - assert(in_fixture); - - in_fixture = false; - longjmp(igt_subtest_jmpbuf, 1); -} - -bool igt_exit_called; -static void check_igt_exit(int sig) -{ - /* When not killed by a signal check that igt_exit() has been properly - * called. */ - assert(sig != 0 || igt_exit_called); -} - - -static void print_version(void) -{ - struct utsname uts; - - if (list_subtests) - return; - - uname(&uts); - - fprintf(stdout, "IGT-Version: %s-%s (%s) (%s: %s %s)\n", PACKAGE_VERSION, - IGT_GIT_SHA1, TARGET_CPU_PLATFORM, - uts.sysname, uts.release, uts.machine); -} - -static void print_usage(const char *command_str, const char *help_str, - bool output_on_stderr) -{ - FILE *f = output_on_stderr ? stderr : stdout; - - fprintf(f, "Usage: %s [OPTIONS]\n" - " --list-subtests\n" - " --run-subtest <pattern>\n", command_str); - if (help_str) - fprintf(f, "%s\n", help_str); -} - -int igt_subtest_init_parse_opts(int argc, char **argv, - const char *extra_short_opts, - struct option *extra_long_opts, - const char *help_str, - igt_opt_handler_t extra_opt_handler) -{ - int c, option_index = 0; - static struct option long_options[] = { - {"list-subtests", 0, 0, 'l'}, - {"run-subtest", 1, 0, 'r'}, - {"help", 0, 0, 'h'}, - }; - const char *command_str; - char *short_opts; - struct option *combined_opts; - int extra_opt_count; - int all_opt_count; - int ret = 0; - - test_with_subtests = true; - - command_str = argv[0]; - if (strrchr(command_str, '/')) - command_str = strrchr(command_str, '/') + 1; - - /* First calculate space for all passed-in extra long options */ - all_opt_count = 0; - while (extra_long_opts && extra_long_opts[all_opt_count].name) - all_opt_count++; - extra_opt_count = all_opt_count; - - all_opt_count += ARRAY_SIZE(long_options); - - combined_opts = malloc(all_opt_count * sizeof(*combined_opts)); - memcpy(combined_opts, extra_long_opts, - extra_opt_count * sizeof(*combined_opts)); - - /* Copy the subtest long options (and the final NULL entry) */ - memcpy(&combined_opts[extra_opt_count], long_options, - ARRAY_SIZE(long_options) * sizeof(*combined_opts)); - - ret = asprintf(&short_opts, "%sh", - extra_short_opts ? extra_short_opts : ""); - assert(ret >= 0); - - while ((c = getopt_long(argc, argv, short_opts, combined_opts, - &option_index)) != -1) { - switch(c) { - case 'l': - if (!run_single_subtest) - list_subtests = true; - break; - case 'r': - if (!list_subtests) - run_single_subtest = strdup(optarg); - break; - case 'h': - print_usage(command_str, help_str, false); - ret = -1; - goto out; - case '?': - if (opterr) { - print_usage(command_str, help_str, true); - ret = -2; - goto out; - } - /* - * Just ignore the error, since the unknown argument - * can be something the caller understands and will - * parse by doing a second getopt scanning. - */ - break; - default: - ret = extra_opt_handler(c, option_index); - if (ret) - goto out; - } - } - - igt_install_exit_handler(check_igt_exit); - oom_adjust_for_doom(); - -out: - free(short_opts); - free(combined_opts); - print_version(); - - return ret; -} - -enum igt_log_level igt_log_level = IGT_LOG_INFO; - -static void common_init(void) -{ - char *env = getenv("IGT_LOG_LEVEL"); - - if (!env) - return; - - if (strcmp(env, "debug") == 0) - igt_log_level = IGT_LOG_DEBUG; - else if (strcmp(env, "info") == 0) - igt_log_level = IGT_LOG_INFO; - else if (strcmp(env, "warn") == 0) - igt_log_level = IGT_LOG_WARN; - else if (strcmp(env, "none") == 0) - igt_log_level = IGT_LOG_NONE; -} - -void igt_subtest_init(int argc, char **argv) -{ - int ret; - - /* supress getopt errors about unknown options */ - opterr = 0; - - ret = igt_subtest_init_parse_opts(argc, argv, NULL, NULL, NULL, NULL); - if (ret < 0) - /* exit with no error for -h/--help */ - exit(ret == -1 ? 0 : ret); - - /* reset opt parsing */ - optind = 1; - - common_init(); -} - -void igt_simple_init(void) -{ - print_version(); - - common_init(); -} - -/* - * Note: Testcases which use these helpers MUST NOT output anything to stdout - * outside of places protected by igt_run_subtest checks - the piglit - * runner adds every line to the subtest list. - */ -bool __igt_run_subtest(const char *subtest_name) -{ - assert(!in_subtest); - assert(!in_fixture); - - if (list_subtests) { - printf("%s\n", subtest_name); - return false; - } - - if (run_single_subtest && - strcmp(subtest_name, run_single_subtest) != 0) - return false; - - if (skip_subtests_henceforth) { - printf("Subtest %s: %s\n", subtest_name, - skip_subtests_henceforth == SKIP ? - "SKIP" : "FAIL"); - return false; - } - - return (in_subtest = subtest_name); -} - -const char *igt_subtest_name(void) -{ - return in_subtest; -} - -static bool igt_only_list_subtests(void) -{ - return list_subtests; -} - -static bool skipped_one = false; -static bool succeeded_one = false; -static bool failed_one = false; -static int igt_exitcode; - -static void exit_subtest(const char *) __attribute__((noreturn)); -static void exit_subtest(const char *result) -{ - printf("Subtest %s: %s\n", in_subtest, result); - in_subtest = NULL; - longjmp(igt_subtest_jmpbuf, 1); -} - -void igt_skip(const char *f, ...) -{ - va_list args; - skipped_one = true; - - assert(!test_child); - - if (!igt_only_list_subtests()) { - va_start(args, f); - vprintf(f, args); - va_end(args); - } - - if (in_subtest) { - exit_subtest("SKIP"); - } else if (test_with_subtests) { - skip_subtests_henceforth = SKIP; - assert(in_fixture); - __igt_fixture_end(); - } else { - exit(77); - } -} - -void __igt_skip_check(const char *file, const int line, - const char *func, const char *check, - const char *f, ...) -{ - va_list args; - int err = errno; - - if (f) { - static char *buf; - - /* igt_skip never returns, so try to not leak too badly. */ - if (buf) - free(buf); - - va_start(args, f); - vasprintf(&buf, f, args); - va_end(args); - - igt_skip("Test requirement not met in function %s, file %s:%i:\n" - "Last errno: %i, %s\n" - "Test requirement: (%s)\n%s", - func, file, line, err, strerror(err), check, buf); - } else { - igt_skip("Test requirement not met in function %s, file %s:%i:\n" - "Last errno: %i, %s\n" - "Test requirement: (%s)\n", - func, file, line, err, strerror(err), check); - } -} - -void igt_success(void) -{ - succeeded_one = true; - if (in_subtest) - exit_subtest("SUCCESS"); -} - -void igt_fail(int exitcode) -{ - assert(exitcode != 0 && exitcode != 77); - - if (!failed_one) - igt_exitcode = exitcode; - - failed_one = true; - - /* Silent exit, parent will do the yelling. */ - if (test_child) - exit(exitcode); - - if (in_subtest) - exit_subtest("FAIL"); - else { - assert(!test_with_subtests || in_fixture); - - if (in_fixture) { - skip_subtests_henceforth = FAIL; - __igt_fixture_end(); - } - - exit(exitcode); - } -} - -static bool run_under_gdb(void) -{ - char buf[1024]; - - sprintf(buf, "/proc/%d/exe", getppid()); - return (readlink (buf, buf, sizeof (buf)) != -1 && - strncmp(basename(buf), "gdb", 3) == 0); -} - -void __igt_fail_assert(int exitcode, const char *file, - const int line, const char *func, const char *assertion, - const char *f, ...) -{ - va_list args; - int err = errno; - - printf("Test assertion failure function %s, file %s:%i:\n" - "Last errno: %i, %s\n" - "Failed assertion: %s\n", - func, file, line, err, strerror(err), assertion); - - if (f) { - va_start(args, f); - vprintf(f, args); - va_end(args); - } - - if (run_under_gdb()) - abort(); - igt_fail(exitcode); -} - -void igt_exit(void) -{ - igt_exit_called = true; - - if (igt_only_list_subtests()) - exit(0); - - if (!test_with_subtests) - exit(0); - - /* Calling this without calling one of the above is a failure */ - assert(skipped_one || succeeded_one || failed_one); - - if (failed_one) - exit(igt_exitcode); - else if (succeeded_one) - exit(0); - else - exit(77); -} - -static int helper_process_count; -static pid_t helper_process_pids[] = -{ -1, -1, -1, -1}; - -static void reset_helper_process_list(void) -{ - for (int i = 0; i < ARRAY_SIZE(helper_process_pids); i++) - helper_process_pids[i] = -1; - helper_process_count = 0; -} - -static void fork_helper_exit_handler(int sig) -{ - for (int i = 0; i < ARRAY_SIZE(helper_process_pids); i++) { - pid_t pid = helper_process_pids[i]; - int status, ret; - - if (pid != -1) { - /* Someone forgot to fill up the array? */ - assert(pid != 0); - - ret = kill(pid, SIGQUIT); - assert(ret == 0); - while (waitpid(pid, &status, 0) == -1 && - errno == EINTR) - ; - helper_process_count--; - } - } - - assert(helper_process_count == 0); -} - -bool __igt_fork_helper(struct igt_helper_process *proc) -{ - pid_t pid; - int id; - - assert(!proc->running); - assert(helper_process_count < ARRAY_SIZE(helper_process_pids)); - - for (id = 0; helper_process_pids[id] != -1; id++) - ; - - igt_install_exit_handler(fork_helper_exit_handler); - - switch (pid = fork()) { - case -1: - igt_assert(0); - case 0: - exit_handler_count = 0; - reset_helper_process_list(); - oom_adjust_for_doom(); - - return true; - default: - proc->running = true; - proc->pid = pid; - proc->id = id; - helper_process_pids[id] = pid; - helper_process_count++; - - return false; - } - -} - -void igt_stop_helper(struct igt_helper_process *proc) -{ - int status, ret; - - assert(proc->running); - - ret = kill(proc->pid, - proc->use_SIGKILL ? SIGKILL : SIGQUIT); - assert(ret == 0); - while (waitpid(proc->pid, &status, 0) == -1 && - errno == EINTR) - ; - igt_assert(WIFSIGNALED(status) && - WTERMSIG(status) == (proc->use_SIGKILL ? SIGKILL : SIGQUIT)); - - proc->running = false; - - helper_process_pids[proc->id] = -1; - helper_process_count--; -} - -void igt_wait_helper(struct igt_helper_process *proc) -{ - int status; - - assert(proc->running); - - while (waitpid(proc->pid, &status, 0) == -1 && - errno == EINTR) - ; - igt_assert(WIFEXITED(status) && WEXITSTATUS(status) == 0); - - proc->running = false; - - helper_process_pids[proc->id] = -1; - helper_process_count--; -} - -static void children_exit_handler(int sig) -{ - int ret; - - assert(!test_child); - - for (int nc = 0; nc < num_test_children; nc++) { - int status = -1; - ret = kill(test_children[nc], SIGQUIT); - assert(ret == 0); - - while (waitpid(test_children[nc], &status, 0) == -1 && - errno == EINTR) - ; - } - - num_test_children = 0; -} - -bool __igt_fork(void) -{ - assert(!test_with_subtests || in_subtest); - assert(!test_child); - - igt_install_exit_handler(children_exit_handler); - - if (num_test_children >= test_children_sz) { - if (!test_children_sz) - test_children_sz = 4; - else - test_children_sz *= 2; - - test_children = realloc(test_children, - sizeof(pid_t)*test_children_sz); - igt_assert(test_children); - } - - switch (test_children[num_test_children++] = fork()) { - case -1: - igt_assert(0); - case 0: - test_child = true; - exit_handler_count = 0; - reset_helper_process_list(); - oom_adjust_for_doom(); - - return true; - default: - return false; - } - -} - -/** - * igt_waitchildren: - * - * Wait for all children forked with igt_fork - * - * The magic here is that exit codes from children will be correctly propagated - */ -void igt_waitchildren(void) -{ - assert(!test_child); - - for (int nc = 0; nc < num_test_children; nc++) { - int status = -1; - while (waitpid(test_children[nc], &status, 0) == -1 && - errno == EINTR) - ; - - if (status != 0) { - if (WIFEXITED(status)) { - printf("child %i failed with exit status %i\n", - nc, WEXITSTATUS(status)); - igt_fail(WEXITSTATUS(status)); - } else if (WIFSIGNALED(status)) { - printf("child %i died with signal %i, %s\n", - nc, WTERMSIG(status), - strsignal(WTERMSIG(status))); - igt_fail(99); - } else { - printf("Unhandled failure in child %i\n", nc); - abort(); - } - } - } - - num_test_children = 0; -} - -static bool env_set(const char *env_var, bool default_value) +bool igt_env_set(const char *env_var, bool default_value) { char *val; @@ -947,59 +327,12 @@ static bool env_set(const char *env_var, bool default_value) return atoi(val) != 0; } -bool igt_run_in_simulation(void) -{ - static int simulation = -1; - - if (simulation == -1) - simulation = env_set("INTEL_SIMULATION", false); - - return simulation; -} - -/** - * igt_skip_on_simulation: - * - * Skip tests when INTEL_SIMULATION env war is set - * - * Skip the test when running on simulation (and that's relevant only when - * we're not in the mode where we list the subtests). - * - * This function is subtest aware (since it uses igt_skip) and so can be used to - * skip specific subtests or all subsequent subtests. - */ -void igt_skip_on_simulation(void) -{ - if (igt_only_list_subtests()) - return; - - igt_require(!igt_run_in_simulation()); -} - -void igt_log(enum igt_log_level level, const char *format, ...) -{ - va_list args; - - assert(format); - - if (igt_log_level > level) - return; - - va_start(args, format); - if (level == IGT_LOG_WARN) { - fflush(stdout); - vfprintf(stderr, format, args); - } else - vprintf(format, args); - va_end(args); -} - bool drmtest_dump_aub(void) { static int dump_aub = -1; if (dump_aub == -1) - dump_aub = env_set("IGT_DUMP_AUB", false); + dump_aub = igt_env_set("IGT_DUMP_AUB", false); return dump_aub; } @@ -1092,165 +425,6 @@ void igt_cleanup_aperture_trashers(void) free(trash_bos); } -#define MAX_SIGNALS 32 -#define MAX_EXIT_HANDLERS 5 - -static struct { - sighandler_t handler; - bool installed; -} orig_sig[MAX_SIGNALS]; - -static igt_exit_handler_t exit_handler_fn[MAX_EXIT_HANDLERS]; -static bool exit_handler_disabled; -static sigset_t saved_sig_mask; -static const int handled_signals[] = - { SIGINT, SIGHUP, SIGTERM, SIGQUIT, SIGPIPE, SIGABRT, SIGSEGV, SIGBUS }; - -static int install_sig_handler(int sig_num, sighandler_t handler) -{ - orig_sig[sig_num].handler = signal(sig_num, handler); - - if (orig_sig[sig_num].handler == SIG_ERR) - return -1; - - orig_sig[sig_num].installed = true; - - return 0; -} - -static void restore_sig_handler(int sig_num) -{ - /* Just restore the default so that we properly fall over. */ - signal(sig_num, SIG_DFL); -} - -static void restore_all_sig_handler(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(orig_sig); i++) - restore_sig_handler(i); -} - -static void call_exit_handlers(int sig) -{ - int i; - - if (!exit_handler_count) { - return; - } - - for (i = exit_handler_count - 1; i >= 0; i--) - exit_handler_fn[i](sig); - - /* ensure we don't get called twice */ - exit_handler_count = 0; -} - -static void igt_atexit_handler(void) -{ - restore_all_sig_handler(); - - if (!exit_handler_disabled) - call_exit_handlers(0); -} - -static void fatal_sig_handler(int sig) -{ - pid_t pid, tid; - - restore_all_sig_handler(); - - /* - * exit_handler_disabled is always false here, since when we set it - * we also block signals. - */ - call_exit_handlers(sig); - - /* Workaround cached PID and TID races on glibc and Bionic libc. */ - pid = syscall(SYS_getpid); - tid = syscall(SYS_gettid); - - syscall(SYS_tgkill, pid, tid, sig); -} - -/* - * Set a handler that will be called either when the process calls exit() or - * returns from the main function, or one of the signals in 'handled_signals' - * is raised. MAX_EXIT_HANDLERS handlers can be installed, each of which will - * be called only once, even if a subsequent signal is raised. If the exit - * handlers are called due to a signal, the signal will be re-raised with the - * original signal disposition after all handlers returned. - * - * The handler will be passed the signal number if called due to a signal, or - * 0 otherwise. - */ -void igt_install_exit_handler(igt_exit_handler_t fn) -{ - int i; - - for (i = 0; i < exit_handler_count; i++) - if (exit_handler_fn[i] == fn) - return; - - igt_assert(exit_handler_count < MAX_EXIT_HANDLERS); - - exit_handler_fn[exit_handler_count] = fn; - exit_handler_count++; - - if (exit_handler_count > 1) - return; - - for (i = 0; i < ARRAY_SIZE(handled_signals); i++) { - if (install_sig_handler(handled_signals[i], - fatal_sig_handler)) - goto err; - } - - if (atexit(igt_atexit_handler)) - goto err; - - return; -err: - restore_all_sig_handler(); - exit_handler_count--; - - igt_assert_f(0, "failed to install the signal handler\n"); -} - -void igt_disable_exit_handler(void) -{ - sigset_t set; - int i; - - if (exit_handler_disabled) - return; - - sigemptyset(&set); - for (i = 0; i < ARRAY_SIZE(handled_signals); i++) - sigaddset(&set, handled_signals[i]); - - if (sigprocmask(SIG_BLOCK, &set, &saved_sig_mask)) { - perror("sigprocmask"); - return; - } - - exit_handler_disabled = true; -} - -void igt_enable_exit_handler(void) -{ - if (!exit_handler_disabled) - return; - - if (sigprocmask(SIG_SETMASK, &saved_sig_mask, NULL)) { - perror("sigprocmask"); - return; - } - - exit_handler_disabled = false; -} - #define PREFAULT_DEBUGFS "/sys/module/i915/parameters/prefault_disable" static void igt_prefault_control(bool enable) { diff --git a/lib/drmtest.h b/lib/drmtest.h index a0b6e9fc..f9f21d39 100644 --- a/lib/drmtest.h +++ b/lib/drmtest.h @@ -45,6 +45,7 @@ #include "intel_gpu_tools.h" #include "ioctl_wrappers.h" +#include "igt_core.h" int drm_get_card(void); int drm_open_any(void); @@ -61,266 +62,7 @@ void igt_permute_array(void *array, unsigned size, unsigned i, unsigned j)); void igt_progress(const char *header, uint64_t i, uint64_t total); - -/** - * igt_simple_init: - * - * Init for simple tests without subtests - */ -void igt_simple_init(void); -#define igt_simple_main \ - static void igt_tokencat(__real_main, __LINE__)(void); \ - int main(int argc, char **argv) { \ - igt_simple_init(); \ - igt_tokencat(__real_main, __LINE__)(); \ - exit(0); \ - } \ - static void igt_tokencat(__real_main, __LINE__)(void) \ - -/* subtest infrastructure */ -jmp_buf igt_subtest_jmpbuf; -void igt_subtest_init(int argc, char **argv); -typedef int (*igt_opt_handler_t)(int opt, int opt_index); -struct option; -int igt_subtest_init_parse_opts(int argc, char **argv, - const char *extra_short_opts, - struct option *extra_long_opts, - const char *help_str, - igt_opt_handler_t opt_handler); -bool __igt_run_subtest(const char *subtest_name); -/** - * igt_subtest: - * - * Denote a subtest code block - * - * Magic control flow which denotes a subtest code block. Within that codeblock - * igt_skip|success will only bail out of the subtest. The _f variant accepts a - * printf format string, which is useful for constructing combinatorial tests. - */ -#define igt_tokencat2(x, y) x ## y -#define igt_tokencat(x, y) igt_tokencat2(x, y) -#define __igt_subtest_f(tmp, format...) \ - for (char tmp [256]; \ - snprintf( tmp , sizeof( tmp ), \ - format), \ - __igt_run_subtest( tmp ) && \ - (setjmp(igt_subtest_jmpbuf) == 0); \ - igt_success()) - -/** - * igt_subtest_f: - * @...: format string - * - * Denote a subtest code block - * - * Like #igt_subtest, but also accepts a printf format string - */ -#define igt_subtest_f(f...) \ - __igt_subtest_f(igt_tokencat(__tmpchar, __LINE__), f) -#define igt_subtest(name) for (; __igt_run_subtest((name)) && \ - (setjmp(igt_subtest_jmpbuf) == 0); \ - igt_success()) -const char *igt_subtest_name(void); -#define igt_main \ - static void igt_tokencat(__real_main, __LINE__)(void); \ - int main(int argc, char **argv) { \ - igt_subtest_init(argc, argv); \ - igt_tokencat(__real_main, __LINE__)(); \ - igt_exit(); \ - } \ - static void igt_tokencat(__real_main, __LINE__)(void) \ - - -/** - * igt_skip: - * - * Subtest aware test skipping - * - * For tests with subtests this will either bail out of the current subtest or - * mark all subsequent subtests as SKIP (in case some global setup code failed). - * - * For normal tests without subtest it will directly exit. - */ -__attribute__((format(printf, 1, 2))) -void igt_skip(const char *f, ...) __attribute__((noreturn)); -__attribute__((format(printf, 5, 6))) -void __igt_skip_check(const char *file, const int line, - const char *func, const char *check, - const char *format, ...) __attribute__((noreturn)); -/** - * igt_success: - * - * Complete a (subtest) as successfull - * - * This bails out of a subtests and marks it as successful. For global tests it - * it won't bail out of anything. - */ -void igt_success(void); - -/** - * igt_fail: - * - * Fail a testcase - * - * For subtest it just bails out of the subtest, when run in global context it - * will exit. Note that it won't attempt to keep on running further tests, - * presuming that some mandatory setup failed. - */ -void igt_fail(int exitcode) __attribute__((noreturn)); -__attribute__((format(printf, 6, 7))) -void __igt_fail_assert(int exitcode, const char *file, - const int line, const char *func, const char *assertion, - const char *format, ...) - __attribute__((noreturn)); -/** - * igt_exit: - * - * exit() for igts - * - * This will exit the test with the right exit code when subtests have been - * skipped. For normal tests it exits with a successful exit code, presuming - * everything has worked out. For subtests it also checks that at least one - * subtest has been run (save when only listing subtests. - */ -void igt_exit(void) __attribute__((noreturn)); -/** - * igt_assert: - * - * Fails (sub-)test if a condition is not met - * - * Should be used everywhere where a test checks results. - */ -#define igt_assert(expr) \ - do { if (!(expr)) \ - __igt_fail_assert(99, __FILE__, __LINE__, __func__, #expr , NULL); \ - } while (0) -#define igt_assert_f(expr, f...) \ - do { if (!(expr)) \ - __igt_fail_assert(99, __FILE__, __LINE__, __func__, #expr , f); \ - } while (0) -/** - * igt_assert_cmptint: - * - * Like #igt_assert, but displays the values being compared on failure. - */ -#define igt_assert_cmpint(n1, cmp, n2) \ - do { \ - int __n1 = (n1), __n2 = (n2); \ - if (__n1 cmp __n2) ; else \ - __igt_fail_assert(99, __FILE__, __LINE__, __func__, \ - #n1 " " #cmp " " #n2, \ - "error: %d %s %d\n", __n1, #cmp, __n2); \ - } while (0) - -/** - * igt_require: - * - * Skip a (sub-)test if a condition is not met - * - * This is useful to streamline the skip logic since it allows for a more flat - * code control flow. - */ -#define igt_require(expr) igt_skip_on(!(expr)) -#define igt_skip_on(expr) \ - do { if ((expr)) \ - __igt_skip_check(__FILE__, __LINE__, __func__, #expr , NULL); \ - } while (0) -#define igt_require_f(expr, f...) igt_skip_on_f(!(expr), f) -#define igt_skip_on_f(expr, f...) \ - do { if ((expr)) \ - __igt_skip_check(__FILE__, __LINE__, __func__, #expr , f); \ - } while (0) - -bool __igt_fixture(void); -void __igt_fixture_complete(void); -void __igt_fixture_end(void) __attribute__((noreturn)); -/** - * igt_fixture: - * - * Annotate global test fixture code - * - * Testcase with subtests often need to set up a bunch of global state as the - * common test fixture. To avoid such code interferring with the subtest - * enumeration (e.g. when enumerating on systemes without an intel gpu) such - * blocks should be annotated with igt_fixture. - */ -#define igt_fixture for (int igt_tokencat(__tmpint,__LINE__) = 0; \ - igt_tokencat(__tmpint,__LINE__) < 1 && \ - __igt_fixture() && \ - (setjmp(igt_subtest_jmpbuf) == 0); \ - igt_tokencat(__tmpint,__LINE__) ++, \ - __igt_fixture_complete()) - -bool __igt_fork(void); -/** - * igt_fork: - * @child: name of the int variable with the child number - * @num_children: number of children to fork - * - * Fork parallel test threads with fork() - * - * Joining all test threads should be done with igt_waitchildren to ensure that - * the exit codes of all children are properly reflected in the test status. - */ -#define igt_fork(child, num_children) \ - for (int child = 0; child < (num_children); child++) \ - for (; __igt_fork(); exit(0)) -void igt_waitchildren(void); - -struct igt_helper_process { - bool running; - bool use_SIGKILL; - pid_t pid; - int id; -}; -bool __igt_fork_helper(struct igt_helper_process *proc); -void igt_stop_helper(struct igt_helper_process *proc); -void igt_wait_helper(struct igt_helper_process *proc); -#define igt_fork_helper(proc) \ - for (; __igt_fork_helper(proc); exit(0)) - -/* logging support */ -enum igt_log_level { - IGT_LOG_DEBUG, - IGT_LOG_INFO, - IGT_LOG_WARN, - IGT_LOG_NONE, -}; -__attribute__((format(printf, 2, 3))) -void igt_log(enum igt_log_level level, const char *format, ...); -#define igt_debug(f...) igt_log(IGT_LOG_DEBUG, f) -#define igt_info(f...) igt_log(IGT_LOG_INFO, f) -#define igt_warn(f...) igt_log(IGT_LOG_WARN, f) -extern enum igt_log_level igt_log_level; - -#define igt_warn_on(condition) do {\ - if (condition) \ - igt_warn("Warning on condition %s in fucntion %s, file %s:%i\n", \ - #condition, __func__, __FILE__, __LINE__); \ - } while (0) -#define igt_warn_on_f(condition, f...) do {\ - if (condition) {\ - igt_warn("Warning on condition %s in fucntion %s, file %s:%i\n", \ - #condition, __func__, __FILE__, __LINE__); \ - igt_warn(f); \ - } \ - } while (0) - -/* helpers to automatically reduce test runtime in simulation */ -bool igt_run_in_simulation(void); -#define SLOW_QUICK(slow,quick) (igt_run_in_simulation() ? (quick) : (slow)) -/** - * igt_skip_on_simulation: - * - * Skip tests when INTEL_SIMULATION env war is set - * - * Skip the test when running on simulation (and that's relevant only when - * we're not in the mode where we list the subtests). - * - * This function is subtest aware (since it uses igt_skip) and so can be used to - * skip specific subtests or all subsequent subtests. - */ -void igt_skip_on_simulation(void); +bool igt_env_set(const char *env_var, bool default_value); bool drmtest_dump_aub(void); @@ -332,13 +74,6 @@ void igt_cleanup_aperture_trashers(void); #define do_or_die(x) igt_assert((x) == 0) #define do_ioctl(fd, ptr, sz) igt_assert(drmIoctl((fd), (ptr), (sz)) == 0) -typedef void (*igt_exit_handler_t)(int sig); - -/* reliable atexit helpers, also work when killed by a signal (if possible) */ -void igt_install_exit_handler(igt_exit_handler_t fn); -void igt_enable_exit_handler(void); -void igt_disable_exit_handler(void); - /* set vt into graphics mode, required to prevent fbcon from interfering */ void igt_set_vt_graphics_mode(void); diff --git a/lib/igt_core.c b/lib/igt_core.c new file mode 100644 index 00000000..27c159c6 --- /dev/null +++ b/lib/igt_core.c @@ -0,0 +1,901 @@ +/* + * Copyright © 2007, 2011, 2013, 2014 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. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * Daniel Vetter <daniel.vetter@ffwll.ch> + * + */ + +#ifndef ANDROID +#define _GNU_SOURCE +#else +#include <libgen.h> +#endif +#include <stdio.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <string.h> +#include <sys/mman.h> +#include <signal.h> +#include <pciaccess.h> +#include <getopt.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/wait.h> +#include <sys/types.h> +#include <sys/syscall.h> +#include <sys/utsname.h> +#include <termios.h> + +#include "drmtest.h" +#include "i915_drm.h" +#include "intel_chipset.h" +#include "intel_gpu_tools.h" +#include "igt_debugfs.h" +#include "../version.h" +#include "config.h" + +#include "igt_core.h" + +/** + * SECTION:igt_core + * @short_description: Core i-g-t testing support + * @title: i-g-t core + * + * This libary implements the core of the i-g-t test support infrastructure. + * Main features are the subtest enumeration, cmdline option parsing helpers for + * subtest handling and various helpers to structure testcases with subtests and + * handle subtest test results. + * + * Auxiliary code provides exit handlers, support for forked processes with test + * result propagation. Other generally useful functionality includes optional + * structure logging infrastructure and some support code for running reduced + * test set on in simulated hardware environments. + */ + +static unsigned int exit_handler_count; + +/* subtests helpers */ +static bool list_subtests = false; +static char *run_single_subtest = NULL; +static const char *in_subtest = NULL; +static bool in_fixture = false; +static bool test_with_subtests = false; +static enum { + CONT = 0, SKIP, FAIL +} skip_subtests_henceforth = CONT; + +/* fork support state */ +pid_t *test_children; +int num_test_children; +int test_children_sz; +bool test_child; + +bool __igt_fixture(void) +{ + assert(!in_fixture); + + if (igt_only_list_subtests()) + return false; + + if (skip_subtests_henceforth) + return false; + + in_fixture = true; + return true; +} + +void __igt_fixture_complete(void) +{ + assert(in_fixture); + + in_fixture = false; +} + +void __igt_fixture_end(void) +{ + assert(in_fixture); + + in_fixture = false; + longjmp(igt_subtest_jmpbuf, 1); +} + +bool igt_exit_called; +static void check_igt_exit(int sig) +{ + /* When not killed by a signal check that igt_exit() has been properly + * called. */ + assert(sig != 0 || igt_exit_called); +} + +static void print_version(void) +{ + struct utsname uts; + + if (list_subtests) + return; + + uname(&uts); + + fprintf(stdout, "IGT-Version: %s-%s (%s) (%s: %s %s)\n", PACKAGE_VERSION, + IGT_GIT_SHA1, TARGET_CPU_PLATFORM, + uts.sysname, uts.release, uts.machine); +} + +static void print_usage(const char *command_str, const char *help_str, + bool output_on_stderr) +{ + FILE *f = output_on_stderr ? stderr : stdout; + + fprintf(f, "Usage: %s [OPTIONS]\n" + " --list-subtests\n" + " --run-subtest <pattern>\n", command_str); + if (help_str) + fprintf(f, "%s\n", help_str); +} + +static void oom_adjust_for_doom(void) +{ + int fd; + const char always_kill[] = "1000"; + + fd = open("/proc/self/oom_score_adj", O_WRONLY); + igt_assert(fd != -1); + igt_assert(write(fd, always_kill, sizeof(always_kill)) == sizeof(always_kill)); +} + +int igt_subtest_init_parse_opts(int argc, char **argv, + const char *extra_short_opts, + struct option *extra_long_opts, + const char *help_str, + igt_opt_handler_t extra_opt_handler) +{ + int c, option_index = 0; + static struct option long_options[] = { + {"list-subtests", 0, 0, 'l'}, + {"run-subtest", 1, 0, 'r'}, + {"help", 0, 0, 'h'}, + }; + const char *command_str; + char *short_opts; + struct option *combined_opts; + int extra_opt_count; + int all_opt_count; + int ret = 0; + + test_with_subtests = true; + + command_str = argv[0]; + if (strrchr(command_str, '/')) + command_str = strrchr(command_str, '/') + 1; + + /* First calculate space for all passed-in extra long options */ + all_opt_count = 0; + while (extra_long_opts && extra_long_opts[all_opt_count].name) + all_opt_count++; + extra_opt_count = all_opt_count; + + all_opt_count += ARRAY_SIZE(long_options); + + combined_opts = malloc(all_opt_count * sizeof(*combined_opts)); + memcpy(combined_opts, extra_long_opts, + extra_opt_count * sizeof(*combined_opts)); + + /* Copy the subtest long options (and the final NULL entry) */ + memcpy(&combined_opts[extra_opt_count], long_options, + ARRAY_SIZE(long_options) * sizeof(*combined_opts)); + + ret = asprintf(&short_opts, "%sh", + extra_short_opts ? extra_short_opts : ""); + assert(ret >= 0); + + while ((c = getopt_long(argc, argv, short_opts, combined_opts, + &option_index)) != -1) { + switch(c) { + case 'l': + if (!run_single_subtest) + list_subtests = true; + break; + case 'r': + if (!list_subtests) + run_single_subtest = strdup(optarg); + break; + case 'h': + print_usage(command_str, help_str, false); + ret = -1; + goto out; + case '?': + if (opterr) { + print_usage(command_str, help_str, true); + ret = -2; + goto out; + } + /* + * Just ignore the error, since the unknown argument + * can be something the caller understands and will + * parse by doing a second getopt scanning. + */ + break; + default: + ret = extra_opt_handler(c, option_index); + if (ret) + goto out; + } + } + + igt_install_exit_handler(check_igt_exit); + oom_adjust_for_doom(); + +out: + free(short_opts); + free(combined_opts); + print_version(); + + return ret; +} + +enum igt_log_level igt_log_level = IGT_LOG_INFO; + +static void common_init(void) +{ + char *env = getenv("IGT_LOG_LEVEL"); + + if (!env) + return; + + if (strcmp(env, "debug") == 0) + igt_log_level = IGT_LOG_DEBUG; + else if (strcmp(env, "info") == 0) + igt_log_level = IGT_LOG_INFO; + else if (strcmp(env, "warn") == 0) + igt_log_level = IGT_LOG_WARN; + else if (strcmp(env, "none") == 0) + igt_log_level = IGT_LOG_NONE; +} + +void igt_subtest_init(int argc, char **argv) +{ + int ret; + + /* supress getopt errors about unknown options */ + opterr = 0; + + ret = igt_subtest_init_parse_opts(argc, argv, NULL, NULL, NULL, NULL); + if (ret < 0) + /* exit with no error for -h/--help */ + exit(ret == -1 ? 0 : ret); + + /* reset opt parsing */ + optind = 1; + + common_init(); +} + +void igt_simple_init(void) +{ + print_version(); + + oom_adjust_for_doom(); + + common_init(); +} + +/* + * Note: Testcases which use these helpers MUST NOT output anything to stdout + * outside of places protected by igt_run_subtest checks - the piglit + * runner adds every line to the subtest list. + */ +bool __igt_run_subtest(const char *subtest_name) +{ + assert(!in_subtest); + assert(!in_fixture); + + if (list_subtests) { + printf("%s\n", subtest_name); + return false; + } + + if (run_single_subtest && + strcmp(subtest_name, run_single_subtest) != 0) + return false; + + if (skip_subtests_henceforth) { + printf("Subtest %s: %s\n", subtest_name, + skip_subtests_henceforth == SKIP ? + "SKIP" : "FAIL"); + return false; + } + + return (in_subtest = subtest_name); +} + +const char *igt_subtest_name(void) +{ + return in_subtest; +} + +bool igt_only_list_subtests(void) +{ + return list_subtests; +} + +static bool skipped_one = false; +static bool succeeded_one = false; +static bool failed_one = false; +static int igt_exitcode; + +static void exit_subtest(const char *) __attribute__((noreturn)); +static void exit_subtest(const char *result) +{ + printf("Subtest %s: %s\n", in_subtest, result); + in_subtest = NULL; + longjmp(igt_subtest_jmpbuf, 1); +} + +void igt_skip(const char *f, ...) +{ + va_list args; + skipped_one = true; + + assert(!test_child); + + if (!igt_only_list_subtests()) { + va_start(args, f); + vprintf(f, args); + va_end(args); + } + + if (in_subtest) { + exit_subtest("SKIP"); + } else if (test_with_subtests) { + skip_subtests_henceforth = SKIP; + assert(in_fixture); + __igt_fixture_end(); + } else { + exit(77); + } +} + +void __igt_skip_check(const char *file, const int line, + const char *func, const char *check, + const char *f, ...) +{ + va_list args; + int err = errno; + + if (f) { + static char *buf; + + /* igt_skip never returns, so try to not leak too badly. */ + if (buf) + free(buf); + + va_start(args, f); + vasprintf(&buf, f, args); + va_end(args); + + igt_skip("Test requirement not met in function %s, file %s:%i:\n" + "Last errno: %i, %s\n" + "Test requirement: (%s)\n%s", + func, file, line, err, strerror(err), check, buf); + } else { + igt_skip("Test requirement not met in function %s, file %s:%i:\n" + "Last errno: %i, %s\n" + "Test requirement: (%s)\n", + func, file, line, err, strerror(err), check); + } +} + +void igt_success(void) +{ + succeeded_one = true; + if (in_subtest) + exit_subtest("SUCCESS"); +} + +void igt_fail(int exitcode) +{ + assert(exitcode != 0 && exitcode != 77); + + if (!failed_one) + igt_exitcode = exitcode; + + failed_one = true; + + /* Silent exit, parent will do the yelling. */ + if (test_child) + exit(exitcode); + + if (in_subtest) + exit_subtest("FAIL"); + else { + assert(!test_with_subtests || in_fixture); + + if (in_fixture) { + skip_subtests_henceforth = FAIL; + __igt_fixture_end(); + } + + exit(exitcode); + } +} + +static bool run_under_gdb(void) +{ + char buf[1024]; + + sprintf(buf, "/proc/%d/exe", getppid()); + return (readlink (buf, buf, sizeof (buf)) != -1 && + strncmp(basename(buf), "gdb", 3) == 0); +} + +void __igt_fail_assert(int exitcode, const char *file, + const int line, const char *func, const char *assertion, + const char *f, ...) +{ + va_list args; + int err = errno; + + printf("Test assertion failure function %s, file %s:%i:\n" + "Last errno: %i, %s\n" + "Failed assertion: %s\n", + func, file, line, err, strerror(err), assertion); + + if (f) { + va_start(args, f); + vprintf(f, args); + va_end(args); + } + + if (run_under_gdb()) + abort(); + igt_fail(exitcode); +} + +void igt_exit(void) +{ + igt_exit_called = true; + + if (igt_only_list_subtests()) + exit(0); + + if (!test_with_subtests) + exit(0); + + /* Calling this without calling one of the above is a failure */ + assert(skipped_one || succeeded_one || failed_one); + + if (failed_one) + exit(igt_exitcode); + else if (succeeded_one) + exit(0); + else + exit(77); +} + +/* fork support code */ +static int helper_process_count; +static pid_t helper_process_pids[] = +{ -1, -1, -1, -1}; + +static void reset_helper_process_list(void) +{ + for (int i = 0; i < ARRAY_SIZE(helper_process_pids); i++) + helper_process_pids[i] = -1; + helper_process_count = 0; +} + +static void fork_helper_exit_handler(int sig) +{ + for (int i = 0; i < ARRAY_SIZE(helper_process_pids); i++) { + pid_t pid = helper_process_pids[i]; + int status, ret; + + if (pid != -1) { + /* Someone forgot to fill up the array? */ + assert(pid != 0); + + ret = kill(pid, SIGQUIT); + assert(ret == 0); + while (waitpid(pid, &status, 0) == -1 && + errno == EINTR) + ; + helper_process_count--; + } + } + + assert(helper_process_count == 0); +} + +bool __igt_fork_helper(struct igt_helper_process *proc) +{ + pid_t pid; + int id; + + assert(!proc->running); + assert(helper_process_count < ARRAY_SIZE(helper_process_pids)); + + for (id = 0; helper_process_pids[id] != -1; id++) + ; + + igt_install_exit_handler(fork_helper_exit_handler); + + switch (pid = fork()) { + case -1: + igt_assert(0); + case 0: + exit_handler_count = 0; + reset_helper_process_list(); + oom_adjust_for_doom(); + + return true; + default: + proc->running = true; + proc->pid = pid; + proc->id = id; + helper_process_pids[id] = pid; + helper_process_count++; + + return false; + } + +} + +void igt_stop_helper(struct igt_helper_process *proc) +{ + int status, ret; + + assert(proc->running); + + ret = kill(proc->pid, + proc->use_SIGKILL ? SIGKILL : SIGQUIT); + assert(ret == 0); + while (waitpid(proc->pid, &status, 0) == -1 && + errno == EINTR) + ; + igt_assert(WIFSIGNALED(status) && + WTERMSIG(status) == (proc->use_SIGKILL ? SIGKILL : SIGQUIT)); + + proc->running = false; + + helper_process_pids[proc->id] = -1; + helper_process_count--; +} + +void igt_wait_helper(struct igt_helper_process *proc) +{ + int status; + + assert(proc->running); + + while (waitpid(proc->pid, &status, 0) == -1 && + errno == EINTR) + ; + igt_assert(WIFEXITED(status) && WEXITSTATUS(status) == 0); + + proc->running = false; + + helper_process_pids[proc->id] = -1; + helper_process_count--; +} + +static void children_exit_handler(int sig) +{ + int ret; + + assert(!test_child); + + for (int nc = 0; nc < num_test_children; nc++) { + int status = -1; + ret = kill(test_children[nc], SIGQUIT); + assert(ret == 0); + + while (waitpid(test_children[nc], &status, 0) == -1 && + errno == EINTR) + ; + } + + num_test_children = 0; +} + +bool __igt_fork(void) +{ + assert(!test_with_subtests || in_subtest); + assert(!test_child); + + igt_install_exit_handler(children_exit_handler); + + if (num_test_children >= test_children_sz) { + if (!test_children_sz) + test_children_sz = 4; + else + test_children_sz *= 2; + + test_children = realloc(test_children, + sizeof(pid_t)*test_children_sz); + igt_assert(test_children); + } + + switch (test_children[num_test_children++] = fork()) { + case -1: + igt_assert(0); + case 0: + test_child = true; + exit_handler_count = 0; + reset_helper_process_list(); + oom_adjust_for_doom(); + + return true; + default: + return false; + } + +} + +/** + * igt_waitchildren: + * + * Wait for all children forked with igt_fork + * + * The magic here is that exit codes from children will be correctly propagated + */ +void igt_waitchildren(void) +{ + assert(!test_child); + + for (int nc = 0; nc < num_test_children; nc++) { + int status = -1; + while (waitpid(test_children[nc], &status, 0) == -1 && + errno == EINTR) + ; + + if (status != 0) { + if (WIFEXITED(status)) { + printf("child %i failed with exit status %i\n", + nc, WEXITSTATUS(status)); + igt_fail(WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + printf("child %i died with signal %i, %s\n", + nc, WTERMSIG(status), + strsignal(WTERMSIG(status))); + igt_fail(99); + } else { + printf("Unhandled failure in child %i\n", nc); + abort(); + } + } + } + + num_test_children = 0; +} + +/* exit handler code */ +#define MAX_SIGNALS 32 +#define MAX_EXIT_HANDLERS 5 + +static struct { + sighandler_t handler; + bool installed; +} orig_sig[MAX_SIGNALS]; + +static igt_exit_handler_t exit_handler_fn[MAX_EXIT_HANDLERS]; +static bool exit_handler_disabled; +static sigset_t saved_sig_mask; +static const int handled_signals[] = + { SIGINT, SIGHUP, SIGTERM, SIGQUIT, SIGPIPE, SIGABRT, SIGSEGV, SIGBUS }; + +static int install_sig_handler(int sig_num, sighandler_t handler) +{ + orig_sig[sig_num].handler = signal(sig_num, handler); + + if (orig_sig[sig_num].handler == SIG_ERR) + return -1; + + orig_sig[sig_num].installed = true; + + return 0; +} + +static void restore_sig_handler(int sig_num) +{ + /* Just restore the default so that we properly fall over. */ + signal(sig_num, SIG_DFL); +} + +static void restore_all_sig_handler(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(orig_sig); i++) + restore_sig_handler(i); +} + +static void call_exit_handlers(int sig) +{ + int i; + + if (!exit_handler_count) { + return; + } + + for (i = exit_handler_count - 1; i >= 0; i--) + exit_handler_fn[i](sig); + + /* ensure we don't get called twice */ + exit_handler_count = 0; +} + +static void igt_atexit_handler(void) +{ + restore_all_sig_handler(); + + if (!exit_handler_disabled) + call_exit_handlers(0); +} + +static void fatal_sig_handler(int sig) +{ + pid_t pid, tid; + + restore_all_sig_handler(); + + /* + * exit_handler_disabled is always false here, since when we set it + * we also block signals. + */ + call_exit_handlers(sig); + + /* Workaround cached PID and TID races on glibc and Bionic libc. */ + pid = syscall(SYS_getpid); + tid = syscall(SYS_gettid); + + syscall(SYS_tgkill, pid, tid, sig); +} + +/* + * Set a handler that will be called either when the process calls exit() or + * returns from the main function, or one of the signals in 'handled_signals' + * is raised. MAX_EXIT_HANDLERS handlers can be installed, each of which will + * be called only once, even if a subsequent signal is raised. If the exit + * handlers are called due to a signal, the signal will be re-raised with the + * original signal disposition after all handlers returned. + * + * The handler will be passed the signal number if called due to a signal, or + * 0 otherwise. + */ +void igt_install_exit_handler(igt_exit_handler_t fn) +{ + int i; + + for (i = 0; i < exit_handler_count; i++) + if (exit_handler_fn[i] == fn) + return; + + igt_assert(exit_handler_count < MAX_EXIT_HANDLERS); + + exit_handler_fn[exit_handler_count] = fn; + exit_handler_count++; + + if (exit_handler_count > 1) + return; + + for (i = 0; i < ARRAY_SIZE(handled_signals); i++) { + if (install_sig_handler(handled_signals[i], + fatal_sig_handler)) + goto err; + } + + if (atexit(igt_atexit_handler)) + goto err; + + return; +err: + restore_all_sig_handler(); + exit_handler_count--; + + igt_assert_f(0, "failed to install the signal handler\n"); +} + +void igt_disable_exit_handler(void) +{ + sigset_t set; + int i; + + if (exit_handler_disabled) + return; + + sigemptyset(&set); + for (i = 0; i < ARRAY_SIZE(handled_signals); i++) + sigaddset(&set, handled_signals[i]); + + if (sigprocmask(SIG_BLOCK, &set, &saved_sig_mask)) { + perror("sigprocmask"); + return; + } + + exit_handler_disabled = true; +} + +void igt_enable_exit_handler(void) +{ + if (!exit_handler_disabled) + return; + + if (sigprocmask(SIG_SETMASK, &saved_sig_mask, NULL)) { + perror("sigprocmask"); + return; + } + + exit_handler_disabled = false; +} + +/* simulation enviroment support */ +bool igt_run_in_simulation(void) +{ + static int simulation = -1; + + if (simulation == -1) + simulation = igt_env_set("INTEL_SIMULATION", false); + + return simulation; +} + +/** + * igt_skip_on_simulation: + * + * Skip tests when INTEL_SIMULATION env war is set + * + * Skip the test when running on simulation (and that's relevant only when + * we're not in the mode where we list the subtests). + * + * This function is subtest aware (since it uses igt_skip) and so can be used to + * skip specific subtests or all subsequent subtests. + */ +void igt_skip_on_simulation(void) +{ + if (igt_only_list_subtests()) + return; + + igt_require(!igt_run_in_simulation()); +} + +/* structured logging */ +void igt_log(enum igt_log_level level, const char *format, ...) +{ + va_list args; + + assert(format); + + if (igt_log_level > level) + return; + + va_start(args, format); + if (level == IGT_LOG_WARN) { + fflush(stdout); + vfprintf(stderr, format, args); + } else + vprintf(format, args); + va_end(args); +} + diff --git a/lib/igt_core.h b/lib/igt_core.h new file mode 100644 index 00000000..1ec0bfcb --- /dev/null +++ b/lib/igt_core.h @@ -0,0 +1,305 @@ +/* + * Copyright © 2007,2014 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. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * Daniel Vetter <daniel.vetter@ffwll.ch> + * + */ + + +#ifndef IGT_CORE_H +#define IGT_CORE_H + +bool igt_only_list_subtests(void); + +bool __igt_fixture(void); +void __igt_fixture_complete(void); +void __igt_fixture_end(void) __attribute__((noreturn)); +/** + * igt_fixture: + * + * Annotate global test fixture code + * + * Testcase with subtests often need to set up a bunch of global state as the + * common test fixture. To avoid such code interferring with the subtest + * enumeration (e.g. when enumerating on systemes without an intel gpu) such + * blocks should be annotated with igt_fixture. + */ +#define igt_fixture for (int igt_tokencat(__tmpint,__LINE__) = 0; \ + igt_tokencat(__tmpint,__LINE__) < 1 && \ + __igt_fixture() && \ + (setjmp(igt_subtest_jmpbuf) == 0); \ + igt_tokencat(__tmpint,__LINE__) ++, \ + __igt_fixture_complete()) + +/* subtest infrastructure */ +jmp_buf igt_subtest_jmpbuf; +void igt_subtest_init(int argc, char **argv); +typedef int (*igt_opt_handler_t)(int opt, int opt_index); +struct option; +int igt_subtest_init_parse_opts(int argc, char **argv, + const char *extra_short_opts, + struct option *extra_long_opts, + const char *help_str, + igt_opt_handler_t opt_handler); + +bool __igt_run_subtest(const char *subtest_name); +/** + * igt_subtest: + * + * Denote a subtest code block + * + * Magic control flow which denotes a subtest code block. Within that codeblock + * igt_skip|success will only bail out of the subtest. The _f variant accepts a + * printf format string, which is useful for constructing combinatorial tests. + */ +#define igt_tokencat2(x, y) x ## y +#define igt_tokencat(x, y) igt_tokencat2(x, y) +#define __igt_subtest_f(tmp, format...) \ + for (char tmp [256]; \ + snprintf( tmp , sizeof( tmp ), \ + format), \ + __igt_run_subtest( tmp ) && \ + (setjmp(igt_subtest_jmpbuf) == 0); \ + igt_success()) + +/** + * igt_subtest_f: + * @...: format string + * + * Denote a subtest code block + * + * Like #igt_subtest, but also accepts a printf format string + */ +#define igt_subtest_f(f...) \ + __igt_subtest_f(igt_tokencat(__tmpchar, __LINE__), f) +#define igt_subtest(name) for (; __igt_run_subtest((name)) && \ + (setjmp(igt_subtest_jmpbuf) == 0); \ + igt_success()) +const char *igt_subtest_name(void); +#define igt_main \ + static void igt_tokencat(__real_main, __LINE__)(void); \ + int main(int argc, char **argv) { \ + igt_subtest_init(argc, argv); \ + igt_tokencat(__real_main, __LINE__)(); \ + igt_exit(); \ + } \ + static void igt_tokencat(__real_main, __LINE__)(void) \ + +/** + * igt_simple_init: + * + * Init for simple tests without subtests + */ +void igt_simple_init(void); +#define igt_simple_main \ + static void igt_tokencat(__real_main, __LINE__)(void); \ + int main(int argc, char **argv) { \ + igt_simple_init(); \ + igt_tokencat(__real_main, __LINE__)(); \ + exit(0); \ + } \ + static void igt_tokencat(__real_main, __LINE__)(void) \ + +/** + * igt_skip: + * + * Subtest aware test skipping + * + * For tests with subtests this will either bail out of the current subtest or + * mark all subsequent subtests as SKIP (in case some global setup code failed). + * + * For normal tests without subtest it will directly exit. + */ +__attribute__((format(printf, 1, 2))) +void igt_skip(const char *f, ...) __attribute__((noreturn)); +__attribute__((format(printf, 5, 6))) +void __igt_skip_check(const char *file, const int line, + const char *func, const char *check, + const char *format, ...) __attribute__((noreturn)); +/** + * igt_success: + * + * Complete a (subtest) as successfull + * + * This bails out of a subtests and marks it as successful. For global tests it + * it won't bail out of anything. + */ +void igt_success(void); + +/** + * igt_fail: + * + * Fail a testcase + * + * For subtest it just bails out of the subtest, when run in global context it + * will exit. Note that it won't attempt to keep on running further tests, + * presuming that some mandatory setup failed. + */ +void igt_fail(int exitcode) __attribute__((noreturn)); +__attribute__((format(printf, 6, 7))) +void __igt_fail_assert(int exitcode, const char *file, + const int line, const char *func, const char *assertion, + const char *format, ...) + __attribute__((noreturn)); +/** + * igt_exit: + * + * exit() for igts + * + * This will exit the test with the right exit code when subtests have been + * skipped. For normal tests it exits with a successful exit code, presuming + * everything has worked out. For subtests it also checks that at least one + * subtest has been run (save when only listing subtests. + */ +void igt_exit(void) __attribute__((noreturn)); +/** + * igt_assert: + * + * Fails (sub-)test if a condition is not met + * + * Should be used everywhere where a test checks results. + */ +#define igt_assert(expr) \ + do { if (!(expr)) \ + __igt_fail_assert(99, __FILE__, __LINE__, __func__, #expr , NULL); \ + } while (0) +#define igt_assert_f(expr, f...) \ + do { if (!(expr)) \ + __igt_fail_assert(99, __FILE__, __LINE__, __func__, #expr , f); \ + } while (0) +/** + * igt_assert_cmptint: + * + * Like #igt_assert, but displays the values being compared on failure. + */ +#define igt_assert_cmpint(n1, cmp, n2) \ + do { \ + int __n1 = (n1), __n2 = (n2); \ + if (__n1 cmp __n2) ; else \ + __igt_fail_assert(99, __FILE__, __LINE__, __func__, \ + #n1 " " #cmp " " #n2, \ + "error: %d %s %d\n", __n1, #cmp, __n2); \ + } while (0) + +/** + * igt_require: + * + * Skip a (sub-)test if a condition is not met + * + * This is useful to streamline the skip logic since it allows for a more flat + * code control flow. + */ +#define igt_require(expr) igt_skip_on(!(expr)) +#define igt_skip_on(expr) \ + do { if ((expr)) \ + __igt_skip_check(__FILE__, __LINE__, __func__, #expr , NULL); \ + } while (0) +#define igt_require_f(expr, f...) igt_skip_on_f(!(expr), f) +#define igt_skip_on_f(expr, f...) \ + do { if ((expr)) \ + __igt_skip_check(__FILE__, __LINE__, __func__, #expr , f); \ + } while (0) + +/* fork support code */ +bool __igt_fork(void); +/** + * igt_fork: + * @child: name of the int variable with the child number + * @num_children: number of children to fork + * + * Fork parallel test threads with fork() + * + * Joining all test threads should be done with igt_waitchildren to ensure that + * the exit codes of all children are properly reflected in the test status. + */ +#define igt_fork(child, num_children) \ + for (int child = 0; child < (num_children); child++) \ + for (; __igt_fork(); exit(0)) +void igt_waitchildren(void); + +struct igt_helper_process { + bool running; + bool use_SIGKILL; + pid_t pid; + int id; +}; +bool __igt_fork_helper(struct igt_helper_process *proc); +void igt_stop_helper(struct igt_helper_process *proc); +void igt_wait_helper(struct igt_helper_process *proc); +#define igt_fork_helper(proc) \ + for (; __igt_fork_helper(proc); exit(0)) + +/* exit handler code */ +typedef void (*igt_exit_handler_t)(int sig); + +/* reliable atexit helpers, also work when killed by a signal (if possible) */ +void igt_install_exit_handler(igt_exit_handler_t fn); +void igt_enable_exit_handler(void); +void igt_disable_exit_handler(void); + +/* helpers to automatically reduce test runtime in simulation */ +bool igt_run_in_simulation(void); +#define SLOW_QUICK(slow,quick) (igt_run_in_simulation() ? (quick) : (slow)) +/** + * igt_skip_on_simulation: + * + * Skip tests when INTEL_SIMULATION env war is set + * + * Skip the test when running on simulation (and that's relevant only when + * we're not in the mode where we list the subtests). + * + * This function is subtest aware (since it uses igt_skip) and so can be used to + * skip specific subtests or all subsequent subtests. + */ +void igt_skip_on_simulation(void); + +/* structured logging */ +enum igt_log_level { + IGT_LOG_DEBUG, + IGT_LOG_INFO, + IGT_LOG_WARN, + IGT_LOG_NONE, +}; +__attribute__((format(printf, 2, 3))) +void igt_log(enum igt_log_level level, const char *format, ...); +#define igt_debug(f...) igt_log(IGT_LOG_DEBUG, f) +#define igt_info(f...) igt_log(IGT_LOG_INFO, f) +#define igt_warn(f...) igt_log(IGT_LOG_WARN, f) +extern enum igt_log_level igt_log_level; + +#define igt_warn_on(condition) do {\ + if (condition) \ + igt_warn("Warning on condition %s in fucntion %s, file %s:%i\n", \ + #condition, __func__, __FILE__, __LINE__); \ + } while (0) +#define igt_warn_on_f(condition, f...) do {\ + if (condition) {\ + igt_warn("Warning on condition %s in fucntion %s, file %s:%i\n", \ + #condition, __func__, __FILE__, __LINE__); \ + igt_warn(f); \ + } \ + } while (0) + + +#endif /* IGT_CORE_H */ |