2649 lines
65 KiB
C
2649 lines
65 KiB
C
/*
|
|
* 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>
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_LIBGEN_H
|
|
#include <libgen.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <assert.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>
|
|
#ifdef __linux__
|
|
#include <sys/syscall.h>
|
|
#endif
|
|
#include <pthread.h>
|
|
#include <sys/utsname.h>
|
|
#include <termios.h>
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
#include <ctype.h>
|
|
#include <limits.h>
|
|
#include <locale.h>
|
|
#include <uwildmat/uwildmat.h>
|
|
#include <glib.h>
|
|
|
|
#include "drmtest.h"
|
|
#include "intel_chipset.h"
|
|
#include "intel_io.h"
|
|
#include "igt_debugfs.h"
|
|
#include "igt_dummyload.h"
|
|
#include "version.h"
|
|
#include "config.h"
|
|
|
|
#include "igt_core.h"
|
|
#include "igt_aux.h"
|
|
#include "igt_sysfs.h"
|
|
#include "igt_sysrq.h"
|
|
#include "igt_rc.h"
|
|
#include "igt_list.h"
|
|
|
|
#ifndef ANDROID
|
|
#define UNW_LOCAL_ONLY
|
|
#include <libunwind.h>
|
|
#endif
|
|
|
|
#include <elfutils/libdwfl.h>
|
|
|
|
#ifdef HAVE_LIBGEN_H
|
|
#include <libgen.h> /* for basename() on Solaris */
|
|
#endif
|
|
|
|
/**
|
|
* SECTION:igt_core
|
|
* @short_description: Core i-g-t testing support
|
|
* @title: Core
|
|
* @include: igt.h
|
|
*
|
|
* This library 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.
|
|
*
|
|
* When writing tests with subtests it is extremely important that nothing
|
|
* interferes with the subtest enumeration. In i-g-t subtests are enumerated at
|
|
* runtime, which allows powerful testcase enumeration. But it makes subtest
|
|
* enumeration a bit more tricky since the test code needs to be careful to
|
|
* never run any code which might fail (like trying to do privileged operations
|
|
* or opening device driver nodes).
|
|
*
|
|
* To allow this i-g-t provides #igt_fixture code blocks for setup code outside
|
|
* of subtests and automatically skips the subtest code blocks themselves. For
|
|
* special cases igt_only_list_subtests() is also provided. For setup code only
|
|
* shared by a group of subtest encapsulate the #igt_fixture block and all the
|
|
* subtestest in a #igt_subtest_group block.
|
|
*
|
|
* # Magic Control Blocks
|
|
*
|
|
* i-g-t makes heavy use of C macros which serve as magic control blocks. They
|
|
* work fairly well and transparently but since C doesn't have full-blown
|
|
* closures there are caveats:
|
|
*
|
|
* - Asynchronous blocks which are used to spawn children internally use fork().
|
|
* Which means that nonsensical control flow like jumping out of the control
|
|
* block is possible, but it will badly confuse the i-g-t library code. And of
|
|
* course all caveats of a real fork() call apply, namely that file
|
|
* descriptors are copied, but still point at the original file. This will
|
|
* terminally upset the libdrm buffer manager if both parent and child keep on
|
|
* using the same open instance of the drm device. Usually everything related
|
|
* to interacting with the kernel driver must be reinitialized to avoid such
|
|
* issues.
|
|
*
|
|
* - Code blocks with magic control flow are implemented with setjmp() and
|
|
* longjmp(). This applies to #igt_fixture and #igt_subtest blocks and all the
|
|
* three variants to finish test: igt_success(), igt_skip() and igt_fail().
|
|
* Mostly this is of no concern, except when such a control block changes
|
|
* stack variables defined in the same function as the control block resides.
|
|
* Any store/load behaviour after a longjmp() is ill-defined for these
|
|
* variables. Avoid such code.
|
|
*
|
|
* Quoting the man page for longjmp():
|
|
*
|
|
* "The values of automatic variables are unspecified after a call to
|
|
* longjmp() if they meet all the following criteria:"
|
|
* - "they are local to the function that made the corresponding setjmp() call;
|
|
* - "their values are changed between the calls to setjmp() and longjmp(); and
|
|
* - "they are not declared as volatile."
|
|
*
|
|
* # Best Practices for Test Helper Libraries Design
|
|
*
|
|
* Kernel tests itself tend to have fairly complex logic already. It is
|
|
* therefore paramount that helper code, both in libraries and test-private
|
|
* functions, add as little boilerplate code to the main test logic as possible.
|
|
* But then dense code is hard to understand without constantly consulting
|
|
* the documentation and implementation of all the helper functions if it
|
|
* doesn't follow some clear patterns. Hence follow these established best
|
|
* practices:
|
|
*
|
|
* - Make extensive use of the implicit control flow afforded by igt_skip(),
|
|
* igt_fail and igt_success(). When dealing with optional kernel features
|
|
* combine igt_skip() with igt_fail() to skip when the kernel support isn't
|
|
* available but fail when anything else goes awry. void should be the most
|
|
* common return type in all your functions, except object constructors of
|
|
* course.
|
|
*
|
|
* - The main test logic should have no explicit control flow for failure
|
|
* conditions, but instead such assumptions should be written in a declarative
|
|
* style. Use one of the many macros which encapsulate i-g-t's implicit
|
|
* control flow. Pick the most suitable one to have as much debug output as
|
|
* possible without polluting the code unnecessarily. For example
|
|
* igt_assert_cmpint() for comparing integers or do_ioctl() for running ioctls
|
|
* and checking their results. Feel free to add new ones to the library or
|
|
* wrap up a set of checks into a private function to further condense your
|
|
* test logic.
|
|
*
|
|
* - When adding a new feature test function which uses igt_skip() internally,
|
|
* use the {prefix}_require_{feature_name} naming scheme. When you
|
|
* instead add a feature test function which returns a boolean, because your
|
|
* main test logic must take different actions depending upon the feature's
|
|
* availability, then instead use the {prefix}_has_{feature_name}.
|
|
*
|
|
* - As already mentioned eschew explicit error handling logic as much as
|
|
* possible. If your test absolutely has to handle the error of some function
|
|
* the customary naming pattern is to prefix those variants with __. Try to
|
|
* restrict explicit error handling to leaf functions. For the main test flow
|
|
* simply pass the expected error condition down into your helper code, which
|
|
* results in tidy and declarative test logic.
|
|
*
|
|
* - Make your library functions as simple to use as possible. Automatically
|
|
* register cleanup handlers through igt_install_exit_handler(). Reduce the
|
|
* amount of setup boilerplate needed by using implicit singletons and lazy
|
|
* structure initialization and similar design patterns.
|
|
*
|
|
* - Don't shy away from refactoring common code, even when there are just 2-3
|
|
* users and even if it's not a net reduction in code. As long as it helps to
|
|
* remove boilerplate and makes the code more declarative the resulting
|
|
* clearer test flow is worth it. All i-g-t library code has been organically
|
|
* extracted from testcases in this fashion.
|
|
*
|
|
* - For general coding style issues please follow the kernel's rules laid out
|
|
* in
|
|
* [CodingStyle](https://www.kernel.org/doc/Documentation/CodingStyle).
|
|
*
|
|
* # Interface with Testrunners
|
|
*
|
|
* i-g-t testcase are all executables which should be run as root on an
|
|
* otherwise completely idle system. The test status is reflected in the
|
|
* exitcode. #IGT_EXIT_SUCCESS means "success", #IGT_EXIT_SKIP "skip",
|
|
* #IGT_EXIT_TIMEOUT that some operation "timed out". All other exit codes
|
|
* encode a failed test result, including any abnormal termination of the test
|
|
* (e.g. by SIGKILL).
|
|
*
|
|
* On top of that tests may report unexpected results and minor issues to
|
|
* stderr. If stderr is non-empty the test result should be treated as "warn".
|
|
*
|
|
* The test lists are generated at build time. Simple testcases are listed in
|
|
* tests/single-tests.txt and tests with subtests are listed in
|
|
* tests/multi-tests.txt. When running tests with subtest from a test runner it
|
|
* is recommend to run each subtest individually, since otherwise the return
|
|
* code will only reflect the overall result.
|
|
*
|
|
* To do that obtain the lists of subtests with "--list-subtests", which can be
|
|
* run as non-root and doesn't require a DRM driver to be loaded (or any GPU to
|
|
* be present). Then individual subtests can be run with "--run-subtest". Usage
|
|
* help for tests with subtests can be obtained with the "--help" command line
|
|
* option.
|
|
*
|
|
* A wildcard expression can be given to --run-subtest to specify a subset of
|
|
* subtests to run. See https://tools.ietf.org/html/rfc3977#section-4 for a
|
|
* description of allowed wildcard expressions.
|
|
* Some examples of allowed wildcard expressions are:
|
|
*
|
|
* - '*basic*' match any subtest containing basic
|
|
* - 'basic-???' match any subtest named basic- with 3 characters after -
|
|
* - 'basic-[0-9]' match any subtest named basic- with a single number after -
|
|
* - 'basic-[^0-9]' match any subtest named basic- with a single non numerical character after -
|
|
* - 'basic*,advanced*' match any subtest starting basic or advanced
|
|
* - '*,!basic*' match any subtest not starting basic
|
|
* - 'basic*,!basic-render*' match any subtest starting basic but not starting basic-render
|
|
*
|
|
* # Configuration
|
|
*
|
|
* Some of IGT's behavior can be configured through a configuration file.
|
|
* By default, this file is expected to exist in ~/.igtrc . The directory for
|
|
* this can be overridden by setting the environment variable %IGT_CONFIG_PATH.
|
|
* An example configuration follows:
|
|
*
|
|
* |[<!-- language="plain" -->
|
|
* # The common configuration section follows.
|
|
* [Common]
|
|
* FrameDumpPath=/tmp # The path to dump frames that fail comparison checks
|
|
*
|
|
* # The following section is used for configuring the Device Under Test.
|
|
* # It is not mandatory and allows overriding default values.
|
|
* [DUT]
|
|
* SuspendResumeDelay=10
|
|
* ]|
|
|
*
|
|
* Some specific configuration options may be used by specific parts of IGT,
|
|
* such as those related to Chamelium support.
|
|
*/
|
|
|
|
static unsigned int exit_handler_count;
|
|
const char *igt_interactive_debug;
|
|
bool igt_skip_crc_compare;
|
|
|
|
/* subtests helpers */
|
|
static bool list_subtests = false;
|
|
static bool describe_subtests = false;
|
|
static char *run_single_subtest = NULL;
|
|
static bool run_single_subtest_found = false;
|
|
static const char *in_subtest = NULL;
|
|
static struct timespec subtest_time;
|
|
static clockid_t igt_clock = (clockid_t)-1;
|
|
static bool in_fixture = false;
|
|
static bool test_with_subtests = false;
|
|
static bool in_atexit_handler = false;
|
|
static enum {
|
|
CONT = 0, SKIP, FAIL
|
|
} skip_subtests_henceforth = CONT;
|
|
|
|
static char __current_description[512];
|
|
|
|
struct description_node {
|
|
char desc[sizeof(__current_description)];
|
|
struct igt_list link;
|
|
};
|
|
|
|
static struct igt_list subgroup_descriptions;
|
|
|
|
|
|
bool __igt_plain_output = false;
|
|
|
|
/* fork support state */
|
|
pid_t *test_children;
|
|
int num_test_children;
|
|
int test_children_sz;
|
|
bool test_child;
|
|
|
|
enum {
|
|
/*
|
|
* Let the first values be used by individual tests so options don't
|
|
* conflict with core ones
|
|
*/
|
|
OPT_LIST_SUBTESTS = 500,
|
|
OPT_DESCRIBE_SUBTESTS,
|
|
OPT_RUN_SUBTEST,
|
|
OPT_DESCRIPTION,
|
|
OPT_DEBUG,
|
|
OPT_INTERACTIVE_DEBUG,
|
|
OPT_SKIP_CRC,
|
|
OPT_HELP = 'h'
|
|
};
|
|
|
|
static int igt_exitcode = IGT_EXIT_SUCCESS;
|
|
static const char *command_str;
|
|
|
|
static char* igt_log_domain_filter;
|
|
static struct {
|
|
char *entries[256];
|
|
uint8_t start, end;
|
|
} log_buffer;
|
|
static pthread_mutex_t log_buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
GKeyFile *igt_key_file;
|
|
|
|
char *igt_frame_dump_path;
|
|
|
|
static bool stderr_needs_sentinel = false;
|
|
|
|
const char *igt_test_name(void)
|
|
{
|
|
return command_str;
|
|
}
|
|
|
|
static void _igt_log_buffer_append(char *line)
|
|
{
|
|
pthread_mutex_lock(&log_buffer_mutex);
|
|
|
|
free(log_buffer.entries[log_buffer.end]);
|
|
log_buffer.entries[log_buffer.end] = line;
|
|
log_buffer.end++;
|
|
if (log_buffer.end == log_buffer.start)
|
|
log_buffer.start++;
|
|
|
|
pthread_mutex_unlock(&log_buffer_mutex);
|
|
}
|
|
|
|
static void _igt_log_buffer_reset(void)
|
|
{
|
|
pthread_mutex_lock(&log_buffer_mutex);
|
|
|
|
log_buffer.start = log_buffer.end = 0;
|
|
|
|
pthread_mutex_unlock(&log_buffer_mutex);
|
|
}
|
|
|
|
static void _igt_log_buffer_dump(void)
|
|
{
|
|
uint8_t i;
|
|
|
|
if (in_subtest)
|
|
fprintf(stderr, "Subtest %s failed.\n", in_subtest);
|
|
else
|
|
fprintf(stderr, "Test %s failed.\n", command_str);
|
|
|
|
if (log_buffer.start == log_buffer.end) {
|
|
fprintf(stderr, "No log.\n");
|
|
return;
|
|
}
|
|
|
|
pthread_mutex_lock(&log_buffer_mutex);
|
|
fprintf(stderr, "**** DEBUG ****\n");
|
|
|
|
i = log_buffer.start;
|
|
do {
|
|
char *last_line = log_buffer.entries[i];
|
|
fprintf(stderr, "%s", last_line);
|
|
i++;
|
|
} while (i != log_buffer.start && i != log_buffer.end);
|
|
|
|
/* reset the buffer */
|
|
log_buffer.start = log_buffer.end = 0;
|
|
|
|
fprintf(stderr, "**** END ****\n");
|
|
pthread_mutex_unlock(&log_buffer_mutex);
|
|
}
|
|
|
|
/**
|
|
* igt_log_buffer_inspect:
|
|
*
|
|
* Provides a way to replay the internal igt log buffer for inspection.
|
|
* @check: A user-specified handler that gets invoked for each line of
|
|
* the log buffer. The handler should return true to stop
|
|
* inspecting the rest of the buffer.
|
|
* @data: passed as a user argument to the inspection function.
|
|
*/
|
|
void igt_log_buffer_inspect(igt_buffer_log_handler_t check, void *data)
|
|
{
|
|
uint8_t i;
|
|
pthread_mutex_lock(&log_buffer_mutex);
|
|
|
|
i = log_buffer.start;
|
|
do {
|
|
if (check(log_buffer.entries[i], data))
|
|
break;
|
|
i++;
|
|
} while (i != log_buffer.start && i != log_buffer.end);
|
|
|
|
pthread_mutex_unlock(&log_buffer_mutex);
|
|
}
|
|
|
|
void igt_kmsg(const char *format, ...)
|
|
{
|
|
va_list ap;
|
|
FILE *file;
|
|
|
|
file = fopen("/dev/kmsg", "w");
|
|
if (file == NULL)
|
|
return;
|
|
|
|
va_start(ap, format);
|
|
vfprintf(file, format, ap);
|
|
va_end(ap);
|
|
|
|
fclose(file);
|
|
}
|
|
|
|
#define time_valid(ts) ((ts)->tv_sec || (ts)->tv_nsec)
|
|
|
|
double igt_time_elapsed(struct timespec *then,
|
|
struct timespec *now)
|
|
{
|
|
double elapsed = -1.;
|
|
|
|
if (time_valid(then) && time_valid(now)) {
|
|
elapsed = now->tv_sec - then->tv_sec;
|
|
elapsed += (now->tv_nsec - then->tv_nsec) * 1e-9;
|
|
}
|
|
|
|
return elapsed;
|
|
}
|
|
|
|
int igt_gettime(struct timespec *ts)
|
|
{
|
|
memset(ts, 0, sizeof(*ts));
|
|
errno = 0;
|
|
|
|
/* Stay on the same clock for consistency. */
|
|
if (igt_clock != (clockid_t)-1) {
|
|
if (clock_gettime(igt_clock, ts))
|
|
goto error;
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CLOCK_MONOTONIC_RAW
|
|
if (!clock_gettime(igt_clock = CLOCK_MONOTONIC_RAW, ts))
|
|
return 0;
|
|
#endif
|
|
#ifdef CLOCK_MONOTONIC_COARSE
|
|
if (!clock_gettime(igt_clock = CLOCK_MONOTONIC_COARSE, ts))
|
|
return 0;
|
|
#endif
|
|
if (!clock_gettime(igt_clock = CLOCK_MONOTONIC, ts))
|
|
return 0;
|
|
error:
|
|
igt_warn("Could not read monotonic time: %s\n",
|
|
strerror(errno));
|
|
|
|
return -errno;
|
|
}
|
|
|
|
uint64_t igt_nsec_elapsed(struct timespec *start)
|
|
{
|
|
struct timespec now;
|
|
|
|
igt_gettime(&now);
|
|
if ((start->tv_sec | start->tv_nsec) == 0) {
|
|
*start = now;
|
|
return 0;
|
|
}
|
|
|
|
return ((now.tv_nsec - start->tv_nsec) +
|
|
(uint64_t)NSEC_PER_SEC*(now.tv_sec - start->tv_sec));
|
|
}
|
|
|
|
bool __igt_fixture(void)
|
|
{
|
|
assert(!in_fixture);
|
|
assert(test_with_subtests);
|
|
|
|
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;
|
|
siglongjmp(igt_subtest_jmpbuf, 1);
|
|
}
|
|
|
|
/*
|
|
* If the test takes out the machine, in addition to the usual dmesg
|
|
* spam, the kernel may also emit a "death rattle" -- extra debug
|
|
* information that is overkill for normal successful tests, but
|
|
* vital for post-mortem analysis.
|
|
*/
|
|
static void ftrace_dump_on_oops(bool enable)
|
|
{
|
|
int fd;
|
|
|
|
fd = open("/proc/sys/kernel/ftrace_dump_on_oops", O_WRONLY);
|
|
if (fd < 0)
|
|
return;
|
|
|
|
/*
|
|
* If we fail, we do not get the death rattle we wish, but we
|
|
* still want to run the tests anyway.
|
|
*/
|
|
igt_ignore_warn(write(fd, enable ? "1\n" : "0\n", 2));
|
|
close(fd);
|
|
}
|
|
|
|
bool igt_exit_called;
|
|
static void common_exit_handler(int sig)
|
|
{
|
|
if (!igt_only_list_subtests()) {
|
|
bind_fbcon(true);
|
|
}
|
|
|
|
/* When not killed by a signal check that igt_exit() has been properly
|
|
* called. */
|
|
assert(sig != 0 || igt_exit_called);
|
|
}
|
|
|
|
static void print_line_wrapping(const char *indent, const char *text)
|
|
{
|
|
char *copy, *curr, *next_space;
|
|
int current_line_length = 0;
|
|
bool done = false;
|
|
|
|
const int total_line_length = 80;
|
|
const int line_length = total_line_length - strlen(indent);
|
|
|
|
copy = malloc(strlen(text) + 1);
|
|
memcpy(copy, text, strlen(text) + 1);
|
|
|
|
curr = copy;
|
|
|
|
printf("%s", indent);
|
|
|
|
while (!done) {
|
|
next_space = strchr(curr, ' ');
|
|
|
|
if (!next_space) { /* no more spaces, print everything that is left */
|
|
done = true;
|
|
next_space = strchr(curr, '\0');
|
|
}
|
|
|
|
*next_space = '\0';
|
|
|
|
if ((next_space - curr) + current_line_length > line_length && curr != copy) {
|
|
printf("\n%s", indent);
|
|
current_line_length = 0;
|
|
}
|
|
|
|
if (current_line_length == 0)
|
|
printf("%s", curr); /* first word in a line, don't space out */
|
|
else
|
|
printf(" %s", curr);
|
|
|
|
current_line_length += next_space - curr;
|
|
curr = next_space + 1;
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
free(copy);
|
|
}
|
|
|
|
|
|
static void print_test_description(void)
|
|
{
|
|
if (&__igt_test_description) {
|
|
print_line_wrapping("", __igt_test_description);
|
|
if (describe_subtests)
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
static void print_version(void)
|
|
{
|
|
struct utsname uts;
|
|
|
|
if (list_subtests)
|
|
return;
|
|
|
|
uname(&uts);
|
|
|
|
igt_info("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 *help_str, bool output_on_stderr)
|
|
{
|
|
FILE *f = output_on_stderr ? stderr : stdout;
|
|
|
|
fprintf(f, "Usage: %s [OPTIONS]\n", command_str);
|
|
fprintf(f, " --list-subtests\n"
|
|
" --run-subtest <pattern>\n"
|
|
" --debug[=log-domain]\n"
|
|
" --interactive-debug[=domain]\n"
|
|
" --skip-crc-compare\n"
|
|
" --help-description\n"
|
|
" --describe\n"
|
|
" --help|-h\n");
|
|
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));
|
|
close(fd);
|
|
|
|
}
|
|
|
|
static void common_init_config(void)
|
|
{
|
|
char *key_file_env = NULL;
|
|
char *key_file_loc = NULL;
|
|
GError *error = NULL;
|
|
int ret;
|
|
|
|
/* Determine igt config path */
|
|
key_file_env = getenv("IGT_CONFIG_PATH");
|
|
if (key_file_env) {
|
|
key_file_loc = key_file_env;
|
|
} else {
|
|
key_file_loc = malloc(100);
|
|
snprintf(key_file_loc, 100, "%s/.igtrc", g_get_home_dir());
|
|
}
|
|
|
|
/* Load igt config file */
|
|
igt_key_file = g_key_file_new();
|
|
ret = g_key_file_load_from_file(igt_key_file, key_file_loc,
|
|
G_KEY_FILE_NONE, &error);
|
|
if (!ret) {
|
|
g_error_free(error);
|
|
g_key_file_free(igt_key_file);
|
|
igt_key_file = NULL;
|
|
|
|
goto out;
|
|
}
|
|
|
|
g_clear_error(&error);
|
|
|
|
if (!igt_frame_dump_path)
|
|
igt_frame_dump_path =
|
|
g_key_file_get_string(igt_key_file, "Common",
|
|
"FrameDumpPath", &error);
|
|
|
|
g_clear_error(&error);
|
|
|
|
ret = g_key_file_get_integer(igt_key_file, "DUT", "SuspendResumeDelay",
|
|
&error);
|
|
assert(!error || error->code != G_KEY_FILE_ERROR_INVALID_VALUE);
|
|
|
|
g_clear_error(&error);
|
|
|
|
if (ret != 0)
|
|
igt_set_autoresume_delay(ret);
|
|
|
|
out:
|
|
if (!key_file_env && key_file_loc)
|
|
free(key_file_loc);
|
|
}
|
|
|
|
static void common_init_env(void)
|
|
{
|
|
const char *env;
|
|
|
|
if (!isatty(STDOUT_FILENO) || getenv("IGT_PLAIN_OUTPUT"))
|
|
__igt_plain_output = true;
|
|
|
|
errno = 0; /* otherwise may be either ENOTTY or EBADF because of isatty */
|
|
|
|
if (!__igt_plain_output)
|
|
setlocale(LC_ALL, "");
|
|
|
|
env = getenv("IGT_LOG_LEVEL");
|
|
if (env) {
|
|
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;
|
|
}
|
|
|
|
igt_frame_dump_path = getenv("IGT_FRAME_DUMP_PATH");
|
|
|
|
stderr_needs_sentinel = getenv("IGT_SENTINEL_ON_STDERR") != NULL;
|
|
|
|
env = getenv("IGT_FORCE_DRIVER");
|
|
if (env) {
|
|
__set_forced_driver(env);
|
|
}
|
|
}
|
|
|
|
static int common_init(int *argc, char **argv,
|
|
const char *extra_short_opts,
|
|
const struct option *extra_long_opts,
|
|
const char *help_str,
|
|
igt_opt_handler_t extra_opt_handler,
|
|
void *handler_data)
|
|
{
|
|
int c, option_index = 0, i, x;
|
|
static struct option long_options[] = {
|
|
{"list-subtests", no_argument, NULL, OPT_LIST_SUBTESTS},
|
|
{"describe", optional_argument, NULL, OPT_DESCRIBE_SUBTESTS},
|
|
{"run-subtest", required_argument, NULL, OPT_RUN_SUBTEST},
|
|
{"help-description", no_argument, NULL, OPT_DESCRIPTION},
|
|
{"debug", optional_argument, NULL, OPT_DEBUG},
|
|
{"interactive-debug", optional_argument, NULL, OPT_INTERACTIVE_DEBUG},
|
|
{"skip-crc-compare", no_argument, NULL, OPT_SKIP_CRC},
|
|
{"help", no_argument, NULL, OPT_HELP},
|
|
{0, 0, 0, 0}
|
|
};
|
|
char *short_opts;
|
|
const char *std_short_opts = "h";
|
|
size_t std_short_opts_len = strlen(std_short_opts);
|
|
struct option *combined_opts;
|
|
int extra_opt_count;
|
|
int all_opt_count;
|
|
int ret = 0;
|
|
|
|
common_init_env();
|
|
igt_list_init(&subgroup_descriptions);
|
|
|
|
command_str = argv[0];
|
|
if (strrchr(command_str, '/'))
|
|
command_str = strrchr(command_str, '/') + 1;
|
|
|
|
/* Check for conflicts and calculate space for passed-in extra long options */
|
|
for (extra_opt_count = 0; extra_long_opts && extra_long_opts[extra_opt_count].name; extra_opt_count++) {
|
|
char *conflicting_char;
|
|
|
|
/* check for conflicts with standard long option values */
|
|
for (i = 0; long_options[i].name; i++) {
|
|
if (0 == strcmp(extra_long_opts[extra_opt_count].name, long_options[i].name)) {
|
|
igt_critical("Conflicting extra long option defined --%s\n", long_options[i].name);
|
|
assert(0);
|
|
|
|
}
|
|
|
|
if (extra_long_opts[extra_opt_count].val == long_options[i].val) {
|
|
igt_critical("Conflicting long option 'val' representation between --%s and --%s\n",
|
|
extra_long_opts[extra_opt_count].name,
|
|
long_options[i].name);
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
/* check for conflicts with standard short options */
|
|
if (extra_long_opts[extra_opt_count].val != ':'
|
|
&& (conflicting_char = memchr(std_short_opts, extra_long_opts[extra_opt_count].val, std_short_opts_len))) {
|
|
igt_critical("Conflicting long and short option 'val' representation between --%s and -%c\n",
|
|
extra_long_opts[extra_opt_count].name,
|
|
*conflicting_char);
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
/* check for conflicts in extra short options*/
|
|
for (i = 0; extra_short_opts && extra_short_opts[i]; i++) {
|
|
if (extra_short_opts[i] == ':')
|
|
continue;
|
|
|
|
/* check for conflicts with standard short options */
|
|
if (memchr(std_short_opts, extra_short_opts[i], std_short_opts_len)) {
|
|
igt_critical("Conflicting short option: -%c\n", std_short_opts[i]);
|
|
assert(0);
|
|
}
|
|
|
|
/* check for conflicts with standard long option values */
|
|
for (x = 0; long_options[x].name; x++) {
|
|
if (long_options[x].val == extra_short_opts[i]) {
|
|
igt_critical("Conflicting short option and long option 'val' representation: --%s and -%c\n",
|
|
long_options[x].name, extra_short_opts[i]);
|
|
assert(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
all_opt_count = extra_opt_count + ARRAY_SIZE(long_options);
|
|
|
|
combined_opts = malloc(all_opt_count * sizeof(*combined_opts));
|
|
if (extra_opt_count > 0)
|
|
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, "%s%s",
|
|
extra_short_opts ? extra_short_opts : "",
|
|
std_short_opts);
|
|
assert(ret >= 0);
|
|
|
|
while ((c = getopt_long(*argc, argv, short_opts, combined_opts,
|
|
&option_index)) != -1) {
|
|
switch(c) {
|
|
case OPT_INTERACTIVE_DEBUG:
|
|
if (optarg && strlen(optarg) > 0)
|
|
igt_interactive_debug = strdup(optarg);
|
|
else
|
|
igt_interactive_debug = "all";
|
|
break;
|
|
case OPT_DEBUG:
|
|
igt_log_level = IGT_LOG_DEBUG;
|
|
if (optarg && strlen(optarg) > 0)
|
|
igt_log_domain_filter = strdup(optarg);
|
|
break;
|
|
case OPT_LIST_SUBTESTS:
|
|
if (!run_single_subtest)
|
|
list_subtests = true;
|
|
break;
|
|
case OPT_DESCRIBE_SUBTESTS:
|
|
if (optarg)
|
|
run_single_subtest = strdup(optarg);
|
|
list_subtests = true;
|
|
describe_subtests = true;
|
|
print_test_description();
|
|
break;
|
|
case OPT_RUN_SUBTEST:
|
|
assert(optarg);
|
|
if (!list_subtests)
|
|
run_single_subtest = strdup(optarg);
|
|
break;
|
|
case OPT_DESCRIPTION:
|
|
print_test_description();
|
|
ret = -1;
|
|
goto out;
|
|
case OPT_SKIP_CRC:
|
|
igt_skip_crc_compare = true;
|
|
goto out;
|
|
case OPT_HELP:
|
|
print_usage(help_str, false);
|
|
ret = -1;
|
|
goto out;
|
|
case '?':
|
|
print_usage(help_str, true);
|
|
ret = -2;
|
|
goto out;
|
|
default:
|
|
ret = extra_opt_handler(c, option_index, handler_data);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
common_init_config();
|
|
|
|
out:
|
|
free(short_opts);
|
|
free(combined_opts);
|
|
|
|
/* exit immediately if this test has no subtests and a subtest or the
|
|
* list of subtests has been requested */
|
|
if (!test_with_subtests) {
|
|
if (run_single_subtest) {
|
|
igt_warn("Unknown subtest: %s\n", run_single_subtest);
|
|
exit(IGT_EXIT_INVALID);
|
|
}
|
|
if (list_subtests)
|
|
exit(IGT_EXIT_INVALID);
|
|
}
|
|
|
|
if (ret < 0)
|
|
/* exit with no error for -h/--help */
|
|
exit(ret == -1 ? 0 : IGT_EXIT_INVALID);
|
|
|
|
if (!list_subtests) {
|
|
bind_fbcon(false);
|
|
igt_kmsg(KMSG_INFO "%s: executing\n", command_str);
|
|
print_version();
|
|
|
|
sync();
|
|
oom_adjust_for_doom();
|
|
ftrace_dump_on_oops(true);
|
|
}
|
|
|
|
/* install exit handler, to ensure we clean up */
|
|
igt_install_exit_handler(common_exit_handler);
|
|
|
|
if (!test_with_subtests)
|
|
igt_gettime(&subtest_time);
|
|
|
|
for (i = 0; (optind + i) < *argc; i++)
|
|
argv[i + 1] = argv[optind + i];
|
|
|
|
*argc = *argc - optind + 1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* igt_subtest_init_parse_opts:
|
|
* @argc: argc from the test's main()
|
|
* @argv: argv from the test's main()
|
|
* @extra_short_opts: getopt_long() compliant list with additional short options
|
|
* @extra_long_opts: getopt_long() compliant list with additional long options
|
|
* @help_str: help string for the additional options
|
|
* @extra_opt_handler: handler for the additional options
|
|
* @handler_data: user data given to @extra_opt_handler when invoked
|
|
*
|
|
* This function handles the subtest related command line options and allows an
|
|
* arbitrary set of additional options. This is useful for tests which have
|
|
* additional knobs to tune when run manually like the number of rounds execute
|
|
* or the size of the allocated buffer objects.
|
|
*
|
|
* Tests should use #igt_main_args instead of their own main()
|
|
* function and calling this function.
|
|
*
|
|
* The @help_str parameter is printed directly after the help text of
|
|
* standard arguments. The formatting of the string should be:
|
|
* - One line per option
|
|
* - Two spaces, option flag, tab character, help text, newline character
|
|
*
|
|
* Example: " -s\tBuffer size\n"
|
|
*
|
|
* The opt handler function must return #IGT_OPT_HANDLER_SUCCESS on
|
|
* successful handling, #IGT_OPT_HANDLER_ERROR on errors.
|
|
*
|
|
* Returns: Forwards any option parsing errors from getopt_long.
|
|
*/
|
|
int igt_subtest_init_parse_opts(int *argc, char **argv,
|
|
const char *extra_short_opts,
|
|
const struct option *extra_long_opts,
|
|
const char *help_str,
|
|
igt_opt_handler_t extra_opt_handler,
|
|
void *handler_data)
|
|
{
|
|
int ret;
|
|
|
|
test_with_subtests = true;
|
|
ret = common_init(argc, argv, extra_short_opts, extra_long_opts,
|
|
help_str, extra_opt_handler, handler_data);
|
|
|
|
return ret;
|
|
}
|
|
|
|
enum igt_log_level igt_log_level = IGT_LOG_INFO;
|
|
|
|
/**
|
|
* igt_simple_init_parse_opts:
|
|
* @argc: argc from the test's main()
|
|
* @argv: argv from the test's main()
|
|
* @extra_short_opts: getopt_long() compliant list with additional short options
|
|
* @extra_long_opts: getopt_long() compliant list with additional long options
|
|
* @help_str: help string for the additional options
|
|
* @extra_opt_handler: handler for the additional options
|
|
* @handler_data: user data given to @extra_opt_handler when invoked
|
|
*
|
|
* This initializes a simple test without any support for subtests and allows
|
|
* an arbitrary set of additional options. This is useful for tests which have
|
|
* additional knobs to tune when run manually like the number of rounds execute
|
|
* or the size of the allocated buffer objects.
|
|
*
|
|
* Tests should use #igt_simple_main_args instead of their own main()
|
|
* function and calling this function.
|
|
*
|
|
* The @help_str parameter is printed directly after the help text of
|
|
* standard arguments. The formatting of the string should be:
|
|
* - One line per option
|
|
* - Two spaces, option flag, tab character, help text, newline character
|
|
*
|
|
* Example: " -s\tBuffer size\n"
|
|
*
|
|
* The opt handler function must return #IGT_OPT_HANDLER_SUCCESS on
|
|
* successful handling, #IGT_OPT_HANDLER_ERROR on errors.
|
|
*/
|
|
void igt_simple_init_parse_opts(int *argc, char **argv,
|
|
const char *extra_short_opts,
|
|
const struct option *extra_long_opts,
|
|
const char *help_str,
|
|
igt_opt_handler_t extra_opt_handler,
|
|
void *handler_data)
|
|
{
|
|
common_init(argc, argv, extra_short_opts, extra_long_opts, help_str,
|
|
extra_opt_handler, handler_data);
|
|
}
|
|
|
|
static void _clear_current_description(void) {
|
|
__current_description[0] = '\0';
|
|
}
|
|
|
|
static void __igt_print_description(const char *subtest_name, const char *file, int line)
|
|
{
|
|
struct description_node *desc;
|
|
const char indent[] = " ";
|
|
bool has_doc = false;
|
|
|
|
|
|
printf("SUB %s %s:%d:\n", subtest_name, file, line);
|
|
|
|
igt_list_for_each(desc, &subgroup_descriptions, link) {
|
|
print_line_wrapping(indent, desc->desc);
|
|
printf("\n");
|
|
has_doc = true;
|
|
}
|
|
|
|
if (__current_description[0] != '\0') {
|
|
print_line_wrapping(indent, __current_description);
|
|
printf("\n");
|
|
has_doc = true;
|
|
}
|
|
|
|
if (!has_doc)
|
|
printf("%sNO DOCUMENTATION!\n\n", indent);
|
|
}
|
|
|
|
/*
|
|
* 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, const char *file, const int line)
|
|
{
|
|
int i;
|
|
|
|
assert(!igt_can_fail());
|
|
|
|
/* check the subtest name only contains a-z, A-Z, 0-9, '-' and '_' */
|
|
for (i = 0; subtest_name[i] != '\0'; i++)
|
|
if (subtest_name[i] != '_' && subtest_name[i] != '-'
|
|
&& !isalnum(subtest_name[i])) {
|
|
igt_critical("Invalid subtest name \"%s\".\n",
|
|
subtest_name);
|
|
igt_exit();
|
|
}
|
|
|
|
if (run_single_subtest) {
|
|
if (uwildmat(subtest_name, run_single_subtest) == 0) {
|
|
_clear_current_description();
|
|
return false;
|
|
} else {
|
|
run_single_subtest_found = true;
|
|
}
|
|
}
|
|
|
|
if (describe_subtests) {
|
|
__igt_print_description(subtest_name, file, line);
|
|
_clear_current_description();
|
|
return false;
|
|
} else if (list_subtests) {
|
|
printf("%s\n", subtest_name);
|
|
return false;
|
|
}
|
|
|
|
|
|
if (skip_subtests_henceforth) {
|
|
printf("%sSubtest %s: %s%s\n",
|
|
(!__igt_plain_output) ? "\x1b[1m" : "", subtest_name,
|
|
skip_subtests_henceforth == SKIP ?
|
|
"SKIP" : "FAIL", (!__igt_plain_output) ? "\x1b[0m" : "");
|
|
fflush(stdout);
|
|
if (stderr_needs_sentinel)
|
|
fprintf(stderr, "Subtest %s: %s\n", subtest_name,
|
|
skip_subtests_henceforth == SKIP ?
|
|
"SKIP" : "FAIL");
|
|
return false;
|
|
}
|
|
|
|
igt_kmsg(KMSG_INFO "%s: starting subtest %s\n",
|
|
command_str, subtest_name);
|
|
igt_info("Starting subtest: %s\n", subtest_name);
|
|
fflush(stdout);
|
|
if (stderr_needs_sentinel)
|
|
fprintf(stderr, "Starting subtest: %s\n", subtest_name);
|
|
|
|
_igt_log_buffer_reset();
|
|
|
|
igt_gettime(&subtest_time);
|
|
return (in_subtest = subtest_name);
|
|
}
|
|
|
|
/**
|
|
* igt_subtest_name:
|
|
*
|
|
* Returns: The name of the currently executed subtest or NULL if called from
|
|
* outside a subtest block.
|
|
*/
|
|
const char *igt_subtest_name(void)
|
|
{
|
|
return in_subtest;
|
|
}
|
|
|
|
/**
|
|
* igt_only_list_subtests:
|
|
*
|
|
* Returns: Returns true if only subtest should be listed and any setup code
|
|
* must be skipped, false otherwise.
|
|
*/
|
|
bool igt_only_list_subtests(void)
|
|
{
|
|
return list_subtests;
|
|
}
|
|
|
|
|
|
|
|
void __igt_subtest_group_save(int *save, int *desc)
|
|
{
|
|
assert(test_with_subtests);
|
|
|
|
if (__current_description[0] != '\0') {
|
|
struct description_node *new = calloc(1, sizeof(*new));
|
|
memcpy(new->desc, __current_description, sizeof(__current_description));
|
|
igt_list_add_tail(&new->link, &subgroup_descriptions);
|
|
_clear_current_description();
|
|
*desc = true;
|
|
}
|
|
|
|
*save = skip_subtests_henceforth;
|
|
}
|
|
|
|
void __igt_subtest_group_restore(int save, int desc)
|
|
{
|
|
if (desc) {
|
|
struct description_node *last =
|
|
igt_list_last_entry(&subgroup_descriptions, last, link);
|
|
igt_list_del(&last->link);
|
|
free(last);
|
|
}
|
|
|
|
skip_subtests_henceforth = save;
|
|
}
|
|
|
|
static bool skipped_one = false;
|
|
static bool succeeded_one = false;
|
|
static bool failed_one = false;
|
|
|
|
static void exit_subtest(const char *) __attribute__((noreturn));
|
|
static void exit_subtest(const char *result)
|
|
{
|
|
struct timespec now;
|
|
|
|
igt_gettime(&now);
|
|
igt_info("%sSubtest %s: %s (%.3fs)%s\n",
|
|
(!__igt_plain_output) ? "\x1b[1m" : "",
|
|
in_subtest, result, igt_time_elapsed(&subtest_time, &now),
|
|
(!__igt_plain_output) ? "\x1b[0m" : "");
|
|
fflush(stdout);
|
|
if (stderr_needs_sentinel)
|
|
fprintf(stderr, "Subtest %s: %s (%.3fs)\n",
|
|
in_subtest, result, igt_time_elapsed(&subtest_time, &now));
|
|
|
|
igt_terminate_spins();
|
|
|
|
in_subtest = NULL;
|
|
siglongjmp(igt_subtest_jmpbuf, 1);
|
|
}
|
|
|
|
/**
|
|
* igt_skip:
|
|
* @f: format string
|
|
* @...: optional arguments used in the format string
|
|
*
|
|
* Subtest aware test skipping. The format string is printed to stderr as the
|
|
* reason why the test skipped.
|
|
*
|
|
* For tests with subtests this will either bail out of the current subtest or
|
|
* mark all subsequent subtests as SKIP (presuming some global setup code
|
|
* failed).
|
|
*
|
|
* For normal tests without subtest it will directly exit.
|
|
*/
|
|
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 {
|
|
igt_exitcode = IGT_EXIT_SKIP;
|
|
igt_exit();
|
|
}
|
|
}
|
|
|
|
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;
|
|
char *err_str = NULL;
|
|
|
|
if (err)
|
|
igt_assert_neq(asprintf(&err_str, "Last errno: %i, %s\n", err, strerror(err)),
|
|
-1);
|
|
|
|
if (f) {
|
|
static char *buf;
|
|
|
|
/* igt_skip never returns, so try to not leak too badly. */
|
|
if (buf)
|
|
free(buf);
|
|
|
|
va_start(args, f);
|
|
igt_assert_neq(vasprintf(&buf, f, args), -1);
|
|
va_end(args);
|
|
|
|
igt_skip("Test requirement not met in function %s, file %s:%i:\n"
|
|
"Test requirement: %s\n%s"
|
|
"%s",
|
|
func, file, line, check, buf, err_str ?: "");
|
|
} else {
|
|
igt_skip("Test requirement not met in function %s, file %s:%i:\n"
|
|
"Test requirement: %s\n"
|
|
"%s",
|
|
func, file, line, check, err_str ?: "");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* igt_success:
|
|
*
|
|
* Complete a (subtest) as successful
|
|
*
|
|
* 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)
|
|
{
|
|
succeeded_one = true;
|
|
if (in_subtest)
|
|
exit_subtest("SUCCESS");
|
|
}
|
|
|
|
/**
|
|
* igt_fail:
|
|
* @exitcode: exitcode
|
|
*
|
|
* Fail a testcase. The exitcode is used as the exit code of the test process.
|
|
* It may not be 0 (which indicates success) or 77 (which indicates a skipped
|
|
* test).
|
|
*
|
|
* For tests with subtests this will either bail out of the current subtest or
|
|
* mark all subsequent subtests as FAIL (presuming some global setup code
|
|
* failed).
|
|
*
|
|
* For normal tests without subtest it will directly exit with the given
|
|
* exitcode.
|
|
*/
|
|
void igt_fail(int exitcode)
|
|
{
|
|
assert(exitcode != IGT_EXIT_SUCCESS && exitcode != IGT_EXIT_SKIP);
|
|
|
|
igt_debug_wait_for_keypress("failure");
|
|
|
|
/* Exit immediately if the test is already exiting and igt_fail is
|
|
* called. This can happen if an igt_assert fails in an exit handler */
|
|
if (in_atexit_handler)
|
|
_exit(IGT_EXIT_FAILURE);
|
|
|
|
if (!failed_one)
|
|
igt_exitcode = exitcode;
|
|
|
|
failed_one = true;
|
|
|
|
/* Silent exit, parent will do the yelling. */
|
|
if (test_child)
|
|
exit(exitcode);
|
|
|
|
_igt_log_buffer_dump();
|
|
|
|
if (in_subtest) {
|
|
exit_subtest("FAIL");
|
|
} else {
|
|
assert(igt_can_fail());
|
|
|
|
if (in_fixture) {
|
|
skip_subtests_henceforth = FAIL;
|
|
__igt_fixture_end();
|
|
}
|
|
|
|
igt_exit();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* igt_fatal_error: Stop test execution on fatal errors
|
|
*
|
|
* Stop test execution or optionally, if the IGT_REBOOT_ON_FATAL_ERROR
|
|
* environment variable is set, reboot the machine.
|
|
*
|
|
* Since out test runner (piglit) does support fatal test exit codes, we
|
|
* implement the default behaviour by waiting endlessly.
|
|
*/
|
|
void __attribute__((noreturn)) igt_fatal_error(void)
|
|
{
|
|
if (igt_check_boolean_env_var("IGT_REBOOT_ON_FATAL_ERROR", false)) {
|
|
igt_warn("FATAL ERROR - REBOOTING\n");
|
|
igt_sysrq_reboot();
|
|
} else {
|
|
igt_warn("FATAL ERROR\n");
|
|
for (;;)
|
|
pause();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* igt_can_fail:
|
|
*
|
|
* Returns true if called from either an #igt_fixture, #igt_subtest or a
|
|
* testcase without subtests, i.e. #igt_simple_main. Returns false otherwise. In
|
|
* other words, it checks whether it's legal to call igt_fail(), igt_skip_on()
|
|
* and all the convenience macros build around those.
|
|
*
|
|
* This is useful to make sure that library code is called from the right
|
|
* places.
|
|
*/
|
|
bool igt_can_fail(void)
|
|
{
|
|
return !test_with_subtests || in_fixture || in_subtest;
|
|
}
|
|
|
|
/**
|
|
* igt_describe_f:
|
|
* @fmt: format string containing description
|
|
* @...: argument used by the format string
|
|
*
|
|
* Attach a description to the following #igt_subtest or #igt_subtest_group
|
|
* block.
|
|
*
|
|
* Check #igt_describe for more details.
|
|
*
|
|
*/
|
|
void igt_describe_f(const char *fmt, ...)
|
|
{
|
|
int ret;
|
|
va_list args;
|
|
|
|
if (!describe_subtests)
|
|
return;
|
|
|
|
va_start(args, fmt);
|
|
|
|
ret = vsnprintf(__current_description, sizeof(__current_description), fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
assert(ret < sizeof(__current_description));
|
|
}
|
|
|
|
static bool running_under_gdb(void)
|
|
{
|
|
char pathname[30], buf[1024];
|
|
ssize_t len;
|
|
|
|
sprintf(pathname, "/proc/%d/exe", getppid());
|
|
len = readlink(pathname, buf, sizeof(buf) - 1);
|
|
if (len < 0)
|
|
return false;
|
|
|
|
buf[len] = '\0';
|
|
|
|
return strncmp(basename(buf), "gdb", 3) == 0;
|
|
}
|
|
|
|
static void __write_stderr(const char *str, size_t len)
|
|
{
|
|
igt_ignore_warn(write(STDERR_FILENO, str, len));
|
|
}
|
|
|
|
static void write_stderr(const char *str)
|
|
{
|
|
__write_stderr(str, strlen(str));
|
|
}
|
|
|
|
#ifndef ANDROID
|
|
|
|
static void print_backtrace(void)
|
|
{
|
|
unw_cursor_t cursor;
|
|
unw_context_t uc;
|
|
int stack_num = 0;
|
|
|
|
Dwfl_Callbacks cbs = {
|
|
.find_elf = dwfl_linux_proc_find_elf,
|
|
.find_debuginfo = dwfl_standard_find_debuginfo,
|
|
};
|
|
|
|
Dwfl *dwfl = dwfl_begin(&cbs);
|
|
|
|
if (dwfl_linux_proc_report(dwfl, getpid())) {
|
|
dwfl_end(dwfl);
|
|
dwfl = NULL;
|
|
} else
|
|
dwfl_report_end(dwfl, NULL, NULL);
|
|
|
|
igt_info("Stack trace:\n");
|
|
|
|
unw_getcontext(&uc);
|
|
unw_init_local(&cursor, &uc);
|
|
while (unw_step(&cursor) > 0) {
|
|
char name[255];
|
|
unw_word_t off, ip;
|
|
Dwfl_Module *mod = NULL;
|
|
|
|
unw_get_reg(&cursor, UNW_REG_IP, &ip);
|
|
|
|
if (dwfl)
|
|
mod = dwfl_addrmodule(dwfl, ip);
|
|
|
|
if (mod) {
|
|
const char *src, *dwfl_name;
|
|
Dwfl_Line *line;
|
|
int lineno;
|
|
GElf_Sym sym;
|
|
|
|
line = dwfl_module_getsrc(mod, ip);
|
|
dwfl_name = dwfl_module_addrsym(mod, ip, &sym, NULL);
|
|
|
|
if (line && dwfl_name) {
|
|
src = dwfl_lineinfo(line, NULL, &lineno, NULL, NULL, NULL);
|
|
igt_info(" #%d %s:%d %s()\n", stack_num++, src, lineno, dwfl_name);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (unw_get_proc_name(&cursor, name, 255, &off) < 0)
|
|
igt_info(" #%d [<unknown>+0x%x]\n", stack_num++,
|
|
(unsigned int) ip);
|
|
else
|
|
igt_info(" #%d [%s+0x%x]\n", stack_num++, name,
|
|
(unsigned int) off);
|
|
}
|
|
|
|
if (dwfl)
|
|
dwfl_end(dwfl);
|
|
}
|
|
|
|
static const char hex[] = "0123456789abcdef";
|
|
|
|
static void
|
|
xputch(int c)
|
|
{
|
|
igt_ignore_warn(write(STDERR_FILENO, (const void *) &c, 1));
|
|
}
|
|
|
|
static int
|
|
xpow(int base, int pow)
|
|
{
|
|
int i, r = 1;
|
|
|
|
for (i = 0; i < pow; i++)
|
|
r *= base;
|
|
|
|
return r;
|
|
}
|
|
|
|
static void
|
|
printnum(unsigned long long num, unsigned base)
|
|
{
|
|
int i = 0;
|
|
unsigned long long __num = num;
|
|
|
|
/* determine from where we should start dividing */
|
|
do {
|
|
__num /= base;
|
|
i++;
|
|
} while (__num);
|
|
|
|
while (i--)
|
|
xputch(hex[num / xpow(base, i) % base]);
|
|
}
|
|
|
|
static size_t
|
|
xstrlcpy(char *dst, const char *src, size_t size)
|
|
{
|
|
char *dst_in;
|
|
|
|
dst_in = dst;
|
|
if (size > 0) {
|
|
while (--size > 0 && *src != '\0')
|
|
*dst++ = *src++;
|
|
*dst = '\0';
|
|
}
|
|
|
|
return dst - dst_in;
|
|
}
|
|
|
|
static void
|
|
xprintfmt(const char *fmt, va_list ap)
|
|
{
|
|
const char *p;
|
|
int ch, base;
|
|
unsigned long long num;
|
|
|
|
while (1) {
|
|
while ((ch = *(const unsigned char *) fmt++) != '%') {
|
|
if (ch == '\0') {
|
|
return;
|
|
}
|
|
xputch(ch);
|
|
}
|
|
|
|
ch = *(const unsigned char *) fmt++;
|
|
switch (ch) {
|
|
/* character */
|
|
case 'c':
|
|
xputch(va_arg(ap, int));
|
|
break;
|
|
/* string */
|
|
case 's':
|
|
if ((p = va_arg(ap, char *)) == NULL) {
|
|
p = "(null)";
|
|
}
|
|
|
|
for (; (ch = *p++) != '\0';) {
|
|
if (ch < ' ' || ch > '~') {
|
|
xputch('?');
|
|
} else {
|
|
xputch(ch);
|
|
}
|
|
}
|
|
break;
|
|
/* (signed) decimal */
|
|
case 'd':
|
|
num = va_arg(ap, int);
|
|
if ((long long) num < 0) {
|
|
xputch('-');
|
|
num = -(long long) num;
|
|
}
|
|
base = 10;
|
|
goto number;
|
|
/* unsigned decimal */
|
|
case 'u':
|
|
num = va_arg(ap, unsigned int);
|
|
base = 10;
|
|
goto number;
|
|
/* (unsigned) hexadecimal */
|
|
case 'x':
|
|
num = va_arg(ap, unsigned int);
|
|
base = 16;
|
|
number:
|
|
printnum(num, base);
|
|
break;
|
|
|
|
/* The following are not implemented */
|
|
|
|
/* width field */
|
|
case '1': case '2':
|
|
case '3': case '4':
|
|
case '5': case '6':
|
|
case '7': case '8':
|
|
case '9':
|
|
case '.': case '#':
|
|
/* long */
|
|
case 'l':
|
|
/* octal */
|
|
case 'o':
|
|
/* pointer */
|
|
case 'p':
|
|
/* float */
|
|
case 'f':
|
|
abort();
|
|
/* escaped '%' character */
|
|
case '%':
|
|
xputch(ch);
|
|
break;
|
|
/* unrecognized escape sequence - just print it literally */
|
|
default:
|
|
xputch('%');
|
|
for (fmt--; fmt[-1] != '%'; fmt--)
|
|
; /* do nothing */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* async-safe printf */
|
|
static void
|
|
xprintf(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
xprintfmt(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
static void print_backtrace_sig_safe(void)
|
|
{
|
|
unw_cursor_t cursor;
|
|
unw_context_t uc;
|
|
int stack_num = 0;
|
|
|
|
write_stderr("Stack trace: \n");
|
|
|
|
unw_getcontext(&uc);
|
|
unw_init_local(&cursor, &uc);
|
|
while (unw_step(&cursor) > 0) {
|
|
char name[255];
|
|
unw_word_t off;
|
|
|
|
if (unw_get_proc_name(&cursor, name, 255, &off) < 0)
|
|
xstrlcpy(name, "<unknown>", 10);
|
|
|
|
xprintf(" #%d [%s+0x%x]\n", stack_num++, name,
|
|
(unsigned int) off);
|
|
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
void __igt_fail_assert(const char *domain, const char *file, const int line,
|
|
const char *func, const char *assertion,
|
|
const char *f, ...)
|
|
{
|
|
va_list args;
|
|
int err = errno;
|
|
|
|
igt_log(domain, IGT_LOG_CRITICAL,
|
|
"Test assertion failure function %s, file %s:%i:\n", func, file,
|
|
line);
|
|
igt_log(domain, IGT_LOG_CRITICAL, "Failed assertion: %s\n", assertion);
|
|
if (err)
|
|
igt_log(domain, IGT_LOG_CRITICAL, "Last errno: %i, %s\n", err,
|
|
strerror(err));
|
|
|
|
if (f) {
|
|
va_start(args, f);
|
|
igt_vlog(domain, IGT_LOG_CRITICAL, f, args);
|
|
va_end(args);
|
|
}
|
|
|
|
#ifndef ANDROID
|
|
print_backtrace();
|
|
#endif
|
|
|
|
if (running_under_gdb())
|
|
abort();
|
|
igt_fail(IGT_EXIT_FAILURE);
|
|
}
|
|
|
|
/**
|
|
* igt_exit:
|
|
*
|
|
* exit() for both types (simple and with subtests) of i-g-t tests.
|
|
*
|
|
* 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.
|
|
*
|
|
* It is an error to normally exit a test calling igt_exit() - without it the
|
|
* result reporting will be wrong. To avoid such issues it is highly recommended
|
|
* to use #igt_main or #igt_simple_main instead of a hand-rolled main() function.
|
|
*/
|
|
void igt_exit(void)
|
|
{
|
|
int tmp;
|
|
|
|
igt_exit_called = true;
|
|
|
|
if (igt_key_file)
|
|
g_key_file_free(igt_key_file);
|
|
|
|
if (run_single_subtest && !run_single_subtest_found) {
|
|
igt_critical("Unknown subtest: %s\n", run_single_subtest);
|
|
exit(IGT_EXIT_INVALID);
|
|
}
|
|
|
|
if (igt_only_list_subtests())
|
|
exit(IGT_EXIT_SUCCESS);
|
|
|
|
/* Calling this without calling one of the above is a failure */
|
|
assert(!test_with_subtests ||
|
|
skipped_one ||
|
|
succeeded_one ||
|
|
failed_one);
|
|
|
|
if (test_with_subtests && !failed_one) {
|
|
if (succeeded_one)
|
|
igt_exitcode = IGT_EXIT_SUCCESS;
|
|
else
|
|
igt_exitcode = IGT_EXIT_SKIP;
|
|
}
|
|
|
|
if (command_str)
|
|
igt_kmsg(KMSG_INFO "%s: exiting, ret=%d\n",
|
|
command_str, igt_exitcode);
|
|
igt_debug("Exiting with status code %d\n", igt_exitcode);
|
|
|
|
for (int c = 0; c < num_test_children; c++)
|
|
kill(test_children[c], SIGKILL);
|
|
assert(!num_test_children);
|
|
|
|
assert(waitpid(-1, &tmp, WNOHANG) == -1 && errno == ECHILD);
|
|
|
|
if (!test_with_subtests) {
|
|
struct timespec now;
|
|
const char *result;
|
|
|
|
igt_gettime(&now);
|
|
|
|
switch (igt_exitcode) {
|
|
case IGT_EXIT_SUCCESS:
|
|
result = "SUCCESS";
|
|
break;
|
|
case IGT_EXIT_SKIP:
|
|
result = "SKIP";
|
|
break;
|
|
default:
|
|
result = "FAIL";
|
|
}
|
|
|
|
printf("%s (%.3fs)\n",
|
|
result, igt_time_elapsed(&subtest_time, &now));
|
|
}
|
|
|
|
exit(igt_exitcode);
|
|
}
|
|
|
|
/* 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 int __waitpid(pid_t pid)
|
|
{
|
|
int status = -1;
|
|
while (waitpid(pid, &status, 0) == -1 &&
|
|
errno == EINTR)
|
|
;
|
|
|
|
return status;
|
|
}
|
|
|
|
static void fork_helper_exit_handler(int sig)
|
|
{
|
|
/* Inside a signal handler, play safe */
|
|
for (int i = 0; i < ARRAY_SIZE(helper_process_pids); i++) {
|
|
pid_t pid = helper_process_pids[i];
|
|
if (pid != -1) {
|
|
kill(pid, SIGTERM);
|
|
__waitpid(pid);
|
|
helper_process_count--;
|
|
}
|
|
}
|
|
|
|
assert(helper_process_count == 0);
|
|
}
|
|
|
|
bool __igt_fork_helper(struct igt_helper_process *proc)
|
|
{
|
|
pid_t pid;
|
|
int id;
|
|
int tmp_count;
|
|
|
|
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);
|
|
|
|
/*
|
|
* Avoid races when the parent stops the child before the setup code
|
|
* had a chance to run. This happens e.g. when skipping tests wrapped in
|
|
* the signal helper.
|
|
*/
|
|
tmp_count = exit_handler_count;
|
|
exit_handler_count = 0;
|
|
|
|
/* ensure any buffers are flushed before fork */
|
|
fflush(NULL);
|
|
|
|
switch (pid = fork()) {
|
|
case -1:
|
|
exit_handler_count = tmp_count;
|
|
igt_assert(0);
|
|
case 0:
|
|
reset_helper_process_list();
|
|
oom_adjust_for_doom();
|
|
|
|
return true;
|
|
default:
|
|
exit_handler_count = tmp_count;
|
|
proc->running = true;
|
|
proc->pid = pid;
|
|
proc->id = id;
|
|
helper_process_pids[id] = pid;
|
|
helper_process_count++;
|
|
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* igt_wait_helper:
|
|
* @proc: #igt_helper_process structure
|
|
*
|
|
* Joins a helper process. It is an error to call this on a helper process which
|
|
* hasn't been spawned yet.
|
|
*/
|
|
int igt_wait_helper(struct igt_helper_process *proc)
|
|
{
|
|
int status;
|
|
|
|
assert(proc->running);
|
|
|
|
status = __waitpid(proc->pid);
|
|
|
|
proc->running = false;
|
|
|
|
helper_process_pids[proc->id] = -1;
|
|
helper_process_count--;
|
|
|
|
return status;
|
|
}
|
|
|
|
static bool helper_was_alive(struct igt_helper_process *proc,
|
|
int status)
|
|
{
|
|
return (WIFSIGNALED(status) &&
|
|
WTERMSIG(status) == (proc->use_SIGKILL ? SIGKILL : SIGTERM));
|
|
}
|
|
|
|
/**
|
|
* igt_stop_helper:
|
|
* @proc: #igt_helper_process structure
|
|
*
|
|
* Terminates a helper process. It is legal to call this on a helper process
|
|
* which hasn't been spawned yet, e.g. if the helper was skipped due to
|
|
* HW restrictions.
|
|
*/
|
|
void igt_stop_helper(struct igt_helper_process *proc)
|
|
{
|
|
int status;
|
|
|
|
if (!proc->running) /* never even started */
|
|
return;
|
|
|
|
/* failure here means the pid is already dead and so waiting is safe */
|
|
kill(proc->pid, proc->use_SIGKILL ? SIGKILL : SIGTERM);
|
|
|
|
status = igt_wait_helper(proc);
|
|
if (!helper_was_alive(proc, status))
|
|
igt_debug("Helper died too early with status=%d\n", status);
|
|
assert(helper_was_alive(proc, status));
|
|
}
|
|
|
|
static void children_exit_handler(int sig)
|
|
{
|
|
int status;
|
|
|
|
/* The exit handler can be called from a fatal signal, so play safe */
|
|
while (num_test_children-- && wait(&status))
|
|
;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
/* ensure any buffers are flushed before fork */
|
|
fflush(NULL);
|
|
|
|
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();
|
|
igt_unshare_spins();
|
|
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
int __igt_waitchildren(void)
|
|
{
|
|
int err = 0;
|
|
int count;
|
|
|
|
assert(!test_child);
|
|
|
|
count = 0;
|
|
while (count < num_test_children) {
|
|
int status = -1;
|
|
pid_t pid;
|
|
int c;
|
|
|
|
pid = wait(&status);
|
|
if (pid == -1)
|
|
continue;
|
|
|
|
for (c = 0; c < num_test_children; c++)
|
|
if (pid == test_children[c])
|
|
break;
|
|
if (c == num_test_children)
|
|
continue;
|
|
|
|
if (err == 0 && status != 0) {
|
|
if (WIFEXITED(status)) {
|
|
printf("child %i failed with exit status %i\n",
|
|
c, WEXITSTATUS(status));
|
|
err = WEXITSTATUS(status);
|
|
} else if (WIFSIGNALED(status)) {
|
|
printf("child %i died with signal %i, %s\n",
|
|
c, WTERMSIG(status),
|
|
strsignal(WTERMSIG(status)));
|
|
err = 128 + WTERMSIG(status);
|
|
} else {
|
|
printf("Unhandled failure [%d] in child %i\n", status, c);
|
|
err = 256;
|
|
}
|
|
|
|
for (c = 0; c < num_test_children; c++)
|
|
kill(test_children[c], SIGKILL);
|
|
}
|
|
|
|
count++;
|
|
}
|
|
|
|
num_test_children = 0;
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* igt_waitchildren:
|
|
*
|
|
* Wait for all children forked with igt_fork.
|
|
*
|
|
* The magic here is that exit codes from children will be correctly propagated
|
|
* to the main thread, including the relevant exit code if a child thread failed.
|
|
* Of course if multiple children failed with different exit codes the resulting
|
|
* exit code will be non-deterministic.
|
|
*
|
|
* Note that igt_skip() will not be forwarded, feature tests need to be done
|
|
* before spawning threads with igt_fork().
|
|
*/
|
|
void igt_waitchildren(void)
|
|
{
|
|
int err = __igt_waitchildren();
|
|
if (err)
|
|
igt_fail(err);
|
|
}
|
|
|
|
static void igt_alarm_killchildren(int signal)
|
|
{
|
|
igt_info("Timed out waiting for children\n");
|
|
|
|
for (int c = 0; c < num_test_children; c++)
|
|
kill(test_children[c], SIGKILL);
|
|
}
|
|
|
|
/**
|
|
* igt_waitchildren_timeout:
|
|
* @seconds: timeout in seconds to wait
|
|
* @reason: debug string explaining what timedout
|
|
*
|
|
* Wait for all children forked with igt_fork, for a maximum of @seconds. If the
|
|
* timeout expires, kills all children, cleans them up, and then fails by
|
|
* calling igt_fail().
|
|
*/
|
|
void igt_waitchildren_timeout(int seconds, const char *reason)
|
|
{
|
|
struct sigaction sa;
|
|
int ret;
|
|
|
|
sa.sa_handler = igt_alarm_killchildren;
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_flags = 0;
|
|
|
|
sigaction(SIGALRM, &sa, NULL);
|
|
|
|
alarm(seconds);
|
|
|
|
ret = __igt_waitchildren();
|
|
igt_reset_timeout();
|
|
if (ret)
|
|
igt_fail(ret);
|
|
}
|
|
|
|
/* exit handler code */
|
|
#define MAX_SIGNALS 32
|
|
#define MAX_EXIT_HANDLERS 10
|
|
|
|
#ifndef HAVE_SIGHANDLER_T
|
|
typedef void (*sighandler_t)(int);
|
|
#endif
|
|
|
|
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 const struct {
|
|
int number;
|
|
const char *name;
|
|
size_t name_len;
|
|
} handled_signals[] = {
|
|
#define SIGDEF(x) { x, #x, sizeof(#x) - 1 }
|
|
#define SILENT(x) { x, NULL, 0 }
|
|
|
|
SILENT(SIGINT),
|
|
SILENT(SIGHUP),
|
|
SILENT(SIGPIPE),
|
|
SILENT(SIGTERM),
|
|
|
|
SIGDEF(SIGQUIT), /* used by igt_runner for its external timeout */
|
|
|
|
SIGDEF(SIGABRT),
|
|
SIGDEF(SIGSEGV),
|
|
SIGDEF(SIGBUS),
|
|
SIGDEF(SIGFPE)
|
|
|
|
#undef SILENT
|
|
#undef SIGDEF
|
|
};
|
|
|
|
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;
|
|
|
|
igt_terminate_spins();
|
|
|
|
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)
|
|
{
|
|
in_atexit_handler = true;
|
|
|
|
restore_all_sig_handler();
|
|
|
|
if (!exit_handler_disabled)
|
|
call_exit_handlers(0);
|
|
}
|
|
|
|
static bool crash_signal(int sig)
|
|
{
|
|
switch (sig) {
|
|
case SIGILL:
|
|
case SIGBUS:
|
|
case SIGFPE:
|
|
case SIGSEGV:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static void fatal_sig_handler(int sig)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(handled_signals); i++) {
|
|
if (handled_signals[i].number != sig)
|
|
continue;
|
|
|
|
if (handled_signals[i].name_len) {
|
|
write_stderr("Received signal ");
|
|
__write_stderr(handled_signals[i].name,
|
|
handled_signals[i].name_len);
|
|
write_stderr(".\n");
|
|
|
|
#ifndef ANDROID
|
|
print_backtrace_sig_safe();
|
|
#endif
|
|
}
|
|
|
|
if (crash_signal(sig)) {
|
|
/* Linux standard to return exit code as 128 + signal */
|
|
if (!failed_one)
|
|
igt_exitcode = 128 + sig;
|
|
failed_one = true;
|
|
|
|
if (in_subtest)
|
|
exit_subtest("CRASH");
|
|
}
|
|
break;
|
|
}
|
|
|
|
restore_all_sig_handler();
|
|
|
|
/*
|
|
* exit_handler_disabled is always false here, since when we set it
|
|
* we also block signals.
|
|
*/
|
|
call_exit_handlers(sig);
|
|
|
|
{
|
|
#ifdef __linux__
|
|
/* Workaround cached PID and TID races on glibc and Bionic libc. */
|
|
pid_t pid = syscall(SYS_getpid);
|
|
pid_t tid = gettid();
|
|
|
|
syscall(SYS_tgkill, pid, tid, sig);
|
|
#else
|
|
pthread_t tid = pthread_self();
|
|
union sigval value = { .sival_ptr = NULL };
|
|
|
|
pthread_sigqueue(tid, sig, value);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/**
|
|
* igt_install_exit_handler:
|
|
* @fn: exit handler function
|
|
*
|
|
* 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. Exit handlers can also be used from test children spawned with
|
|
* igt_fork(), but not from within helper processes spawned with
|
|
* igt_fork_helper(). The list of exit handlers is reset when forking to
|
|
* avoid issues with children cleanup up the parent's state too early.
|
|
*/
|
|
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].number,
|
|
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");
|
|
}
|
|
|
|
/* simulation enviroment support */
|
|
|
|
/**
|
|
* igt_run_in_simulation:
|
|
*
|
|
* This function can be used to select a reduced test set when running in
|
|
* simulation environments. This i-g-t mode is selected by setting the
|
|
* INTEL_SIMULATION environment variable to 1.
|
|
*
|
|
* Returns: True when run in simulation mode, false otherwise.
|
|
*/
|
|
bool igt_run_in_simulation(void)
|
|
{
|
|
static int simulation = -1;
|
|
|
|
if (simulation == -1)
|
|
simulation = igt_check_boolean_env_var("INTEL_SIMULATION", false);
|
|
|
|
return simulation;
|
|
}
|
|
|
|
/**
|
|
* igt_skip_on_simulation:
|
|
*
|
|
* Skip tests when INTEL_SIMULATION environment variable is set. It uses
|
|
* igt_skip() internally and hence is fully subtest aware.
|
|
*
|
|
* Note that in contrast to all other functions which use igt_skip() internally
|
|
* it is allowed to use this outside of an #igt_fixture block in a test with
|
|
* subtests. This is because in contrast to most other test requirements,
|
|
* checking for simulation mode doesn't depend upon the present hardware and it
|
|
* so makes a lot of sense to have this check in the outermost #igt_main block.
|
|
*/
|
|
void igt_skip_on_simulation(void)
|
|
{
|
|
if (igt_only_list_subtests())
|
|
return;
|
|
|
|
if (!igt_can_fail()) {
|
|
igt_fixture
|
|
igt_require(!igt_run_in_simulation());
|
|
} else
|
|
igt_require(!igt_run_in_simulation());
|
|
}
|
|
|
|
/* structured logging */
|
|
|
|
/**
|
|
* igt_log:
|
|
* @domain: the log domain, or NULL for no domain
|
|
* @level: #igt_log_level
|
|
* @format: format string
|
|
* @...: optional arguments used in the format string
|
|
*
|
|
* This is the generic structured logging helper function. i-g-t testcase should
|
|
* output all normal message to stdout. Warning level message should be printed
|
|
* to stderr and the test runner should treat this as an intermediate result
|
|
* between SUCCESS and FAILURE.
|
|
*
|
|
* The log level can be set through the IGT_LOG_LEVEL environment variable with
|
|
* values "debug", "info", "warn", "critical" and "none". By default verbose
|
|
* debug message are disabled. "none" completely disables all output and is not
|
|
* recommended since crucial issues only reported at the IGT_LOG_WARN level are
|
|
* ignored.
|
|
*/
|
|
void igt_log(const char *domain, enum igt_log_level level, const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
igt_vlog(domain, level, format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
/**
|
|
* igt_vlog:
|
|
* @domain: the log domain, or NULL for no domain
|
|
* @level: #igt_log_level
|
|
* @format: format string
|
|
* @args: variable arguments lists
|
|
*
|
|
* This is the generic logging helper function using an explicit varargs
|
|
* structure and hence useful to implement domain-specific logging
|
|
* functions.
|
|
*
|
|
* If there is no need to wrap up a vararg list in the caller it is simpler to
|
|
* just use igt_log().
|
|
*/
|
|
void igt_vlog(const char *domain, enum igt_log_level level, const char *format, va_list args)
|
|
{
|
|
FILE *file;
|
|
char *line, *formatted_line;
|
|
const char *program_name;
|
|
const char *igt_log_level_str[] = {
|
|
"DEBUG",
|
|
"INFO",
|
|
"WARNING",
|
|
"CRITICAL",
|
|
"NONE"
|
|
};
|
|
static bool line_continuation = false;
|
|
|
|
assert(format);
|
|
|
|
#ifdef __GLIBC__
|
|
program_name = program_invocation_short_name;
|
|
#else
|
|
program_name = command_str;
|
|
#endif
|
|
|
|
if (list_subtests && level <= IGT_LOG_WARN)
|
|
return;
|
|
|
|
if (vasprintf(&line, format, args) == -1)
|
|
return;
|
|
|
|
if (line_continuation) {
|
|
formatted_line = strdup(line);
|
|
if (!formatted_line)
|
|
goto out;
|
|
} else if (asprintf(&formatted_line, "(%s:%d) %s%s%s: %s", program_name,
|
|
getpid(), (domain) ? domain : "", (domain) ? "-" : "",
|
|
igt_log_level_str[level], line) == -1) {
|
|
goto out;
|
|
}
|
|
|
|
line_continuation = line[strlen(line) - 1] != '\n';
|
|
|
|
/* append log buffer */
|
|
_igt_log_buffer_append(formatted_line);
|
|
|
|
/* check print log level */
|
|
if (igt_log_level > level)
|
|
goto out;
|
|
|
|
/* check domain filter */
|
|
if (igt_log_domain_filter) {
|
|
/* if null domain and filter is not "application", return */
|
|
if (!domain && strcmp(igt_log_domain_filter, "application"))
|
|
goto out;
|
|
/* else if domain and filter do not match, return */
|
|
else if (domain && strcmp(igt_log_domain_filter, domain))
|
|
goto out;
|
|
}
|
|
|
|
/* use stderr for warning messages and above */
|
|
if (level >= IGT_LOG_WARN) {
|
|
file = stderr;
|
|
fflush(stdout);
|
|
}
|
|
else
|
|
file = stdout;
|
|
|
|
/* prepend all except information messages with process, domain and log
|
|
* level information */
|
|
if (level != IGT_LOG_INFO)
|
|
fwrite(formatted_line, sizeof(char), strlen(formatted_line),
|
|
file);
|
|
else
|
|
fwrite(line, sizeof(char), strlen(line), file);
|
|
|
|
out:
|
|
free(line);
|
|
}
|
|
|
|
static const char *timeout_op;
|
|
static void __attribute__((noreturn)) igt_alarm_handler(int signal)
|
|
{
|
|
if (timeout_op)
|
|
igt_info("Timed out: %s\n", timeout_op);
|
|
else
|
|
igt_info("Timed out\n");
|
|
|
|
/* exit with failure status */
|
|
igt_fail(IGT_EXIT_FAILURE);
|
|
}
|
|
|
|
/**
|
|
* igt_set_timeout:
|
|
* @seconds: number of seconds before timeout
|
|
* @op: Optional string to explain what operation has timed out in the debug log
|
|
*
|
|
* Fail a test and exit with #IGT_EXIT_FAILURE status after the specified
|
|
* number of seconds have elapsed. If the current test has subtests and the
|
|
* timeout occurs outside a subtest, subsequent subtests will be skipped and
|
|
* marked as failed.
|
|
*
|
|
* Any previous timer is cancelled and no timeout is scheduled if @seconds is
|
|
* zero. But for clarity the timeout set with this function should be cleared
|
|
* with igt_reset_timeout().
|
|
*/
|
|
void igt_set_timeout(unsigned int seconds,
|
|
const char *op)
|
|
{
|
|
struct sigaction sa;
|
|
|
|
sa.sa_handler = igt_alarm_handler;
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_flags = 0;
|
|
|
|
timeout_op = op;
|
|
|
|
if (seconds == 0)
|
|
sigaction(SIGALRM, NULL, NULL);
|
|
else
|
|
sigaction(SIGALRM, &sa, NULL);
|
|
|
|
alarm(seconds);
|
|
}
|
|
|
|
/**
|
|
* igt_reset_timeout:
|
|
*
|
|
* This function resets a timeout set by igt_set_timeout() and disables any
|
|
* timer set up by the former function.
|
|
*/
|
|
void igt_reset_timeout(void)
|
|
{
|
|
igt_set_timeout(0, NULL);
|
|
}
|
|
|
|
FILE *__igt_fopen_data(const char* igt_srcdir, const char* igt_datadir,
|
|
const char* filename)
|
|
{
|
|
char path[PATH_MAX];
|
|
FILE *fp;
|
|
|
|
snprintf(path, sizeof(path), "%s/%s", igt_datadir, filename);
|
|
fp = fopen(path, "r");
|
|
if (!fp) {
|
|
snprintf(path, sizeof(path), "%s/%s", igt_srcdir, filename);
|
|
fp = fopen(path, "r");
|
|
}
|
|
if (!fp) {
|
|
snprintf(path, sizeof(path), "./%s", filename);
|
|
fp = fopen(path, "r");
|
|
}
|
|
|
|
if (!fp)
|
|
igt_critical("Could not open data file \"%s\": %s", filename,
|
|
strerror(errno));
|
|
|
|
return fp;
|
|
}
|
|
|
|
static void log_output(int *fd, enum igt_log_level level)
|
|
{
|
|
ssize_t len;
|
|
char buf[PIPE_BUF];
|
|
|
|
if (*fd < 0)
|
|
return;
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
len = read(*fd, buf, sizeof(buf));
|
|
if (len <= 0) {
|
|
close(*fd);
|
|
*fd = -1;
|
|
return;
|
|
}
|
|
|
|
igt_log(IGT_LOG_DOMAIN, level, "[cmd] %s", buf);
|
|
}
|
|
|
|
/**
|
|
* igt_system:
|
|
*
|
|
* An improved replacement of the system() call.
|
|
*
|
|
* Executes the shell command specified in @command with the added feature of
|
|
* concurrently capturing its stdout and stderr to igt_log and igt_warn
|
|
* respectively.
|
|
*
|
|
* Returns: The exit status of the executed process. -1 for failure.
|
|
*/
|
|
int igt_system(const char *command)
|
|
{
|
|
int outpipe[2] = { -1, -1 };
|
|
int errpipe[2] = { -1, -1 };
|
|
int status;
|
|
struct igt_helper_process process = {};
|
|
|
|
if (pipe(outpipe) < 0)
|
|
goto err;
|
|
if (pipe(errpipe) < 0)
|
|
goto err;
|
|
|
|
/*
|
|
* The clone() system call called from a largish executable has
|
|
* difficulty to make progress if interrupted too frequently, so
|
|
* suspend the signal helper for the time of the syscall.
|
|
*/
|
|
igt_suspend_signal_helper();
|
|
|
|
igt_fork_helper(&process) {
|
|
close(outpipe[0]);
|
|
close(errpipe[0]);
|
|
|
|
if (dup2(outpipe[1], STDOUT_FILENO) < 0)
|
|
goto child_err;
|
|
if (dup2(errpipe[1], STDERR_FILENO) < 0)
|
|
goto child_err;
|
|
|
|
execl("/bin/sh", "sh", "-c", command,
|
|
(char *) NULL);
|
|
|
|
child_err:
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
igt_resume_signal_helper();
|
|
|
|
close(outpipe[1]);
|
|
close(errpipe[1]);
|
|
|
|
while (outpipe[0] >= 0 || errpipe[0] >= 0) {
|
|
log_output(&outpipe[0], IGT_LOG_INFO);
|
|
log_output(&errpipe[0], IGT_LOG_WARN);
|
|
}
|
|
|
|
status = igt_wait_helper(&process);
|
|
|
|
return WEXITSTATUS(status);
|
|
err:
|
|
close(outpipe[0]);
|
|
close(outpipe[1]);
|
|
close(errpipe[0]);
|
|
close(errpipe[1]);
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* igt_system_quiet:
|
|
* Similar to igt_system(), except redirect output to /dev/null
|
|
*
|
|
* Returns: The exit status of the executed process. -1 for failure.
|
|
*/
|
|
int igt_system_quiet(const char *command)
|
|
{
|
|
int stderr_fd_copy = -1, stdout_fd_copy = -1, status, nullfd = -1;
|
|
|
|
/* redirect */
|
|
if ((nullfd = open("/dev/null", O_WRONLY)) == -1)
|
|
goto err;
|
|
if ((stdout_fd_copy = dup(STDOUT_FILENO)) == -1)
|
|
goto err;
|
|
if ((stderr_fd_copy = dup(STDERR_FILENO)) == -1)
|
|
goto err;
|
|
|
|
if (dup2(nullfd, STDOUT_FILENO) == -1)
|
|
goto err;
|
|
if (dup2(nullfd, STDERR_FILENO) == -1)
|
|
goto err;
|
|
|
|
/* See igt_system() for the reason for suspending the signal helper. */
|
|
igt_suspend_signal_helper();
|
|
|
|
if ((status = system(command)) == -1)
|
|
goto err;
|
|
|
|
igt_resume_signal_helper();
|
|
|
|
/* restore */
|
|
if (dup2(stdout_fd_copy, STDOUT_FILENO) == -1)
|
|
goto err;
|
|
if (dup2(stderr_fd_copy, STDERR_FILENO) == -1)
|
|
goto err;
|
|
|
|
close(stdout_fd_copy);
|
|
close(stderr_fd_copy);
|
|
close(nullfd);
|
|
|
|
return WEXITSTATUS(status);
|
|
err:
|
|
igt_resume_signal_helper();
|
|
|
|
close(stderr_fd_copy);
|
|
close(stdout_fd_copy);
|
|
close(nullfd);
|
|
|
|
return -1;
|
|
}
|