This is a new selftest that raises SIGUSR1 signals and handles it in a set of different ways, trying to create different scenario for testing purpose. This test works raising a signal and calling sigreturn interleaved with TM operations, as starting, suspending and terminating a transaction. The test depends on random numbers, and, based on them, it sets different TM states. Other than that, the test fills out the user context struct that is passed to the sigreturn system call with random data, in order to make sure that the signal handler syscall can handle different and invalid states properly. This selftest has command line parameters to control what kind of tests the user wants to run, as for example, if a transaction should be started prior to signal being raised, or, after the signal being raised and before the sigreturn. If no parameter is given, the default is enabling all options. This test does not check if the user context is being read and set properly by the kernel. Its purpose, at this time, is basically guaranteeing that the kernel does not crash on invalid scenarios. Signed-off-by: Breno Leitao <leitao@debian.org> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
133 lines
2.3 KiB
C
133 lines
2.3 KiB
C
/*
|
|
* Copyright 2013, Michael Ellerman, IBM Corp.
|
|
* Licensed under GPLv2.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
#include <elf.h>
|
|
#include <fcntl.h>
|
|
#include <link.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "subunit.h"
|
|
#include "utils.h"
|
|
|
|
#define KILL_TIMEOUT 5
|
|
|
|
/* Setting timeout to -1 disables the alarm */
|
|
static uint64_t timeout = 120;
|
|
|
|
int run_test(int (test_function)(void), char *name)
|
|
{
|
|
bool terminated;
|
|
int rc, status;
|
|
pid_t pid;
|
|
|
|
/* Make sure output is flushed before forking */
|
|
fflush(stdout);
|
|
|
|
pid = fork();
|
|
if (pid == 0) {
|
|
setpgid(0, 0);
|
|
exit(test_function());
|
|
} else if (pid == -1) {
|
|
perror("fork");
|
|
return 1;
|
|
}
|
|
|
|
setpgid(pid, pid);
|
|
|
|
if (timeout != -1)
|
|
/* Wake us up in timeout seconds */
|
|
alarm(timeout);
|
|
terminated = false;
|
|
|
|
wait:
|
|
rc = waitpid(pid, &status, 0);
|
|
if (rc == -1) {
|
|
if (errno != EINTR) {
|
|
printf("unknown error from waitpid\n");
|
|
return 1;
|
|
}
|
|
|
|
if (terminated) {
|
|
printf("!! force killing %s\n", name);
|
|
kill(-pid, SIGKILL);
|
|
return 1;
|
|
} else {
|
|
printf("!! killing %s\n", name);
|
|
kill(-pid, SIGTERM);
|
|
terminated = true;
|
|
alarm(KILL_TIMEOUT);
|
|
goto wait;
|
|
}
|
|
}
|
|
|
|
/* Kill anything else in the process group that is still running */
|
|
kill(-pid, SIGTERM);
|
|
|
|
if (WIFEXITED(status))
|
|
status = WEXITSTATUS(status);
|
|
else {
|
|
if (WIFSIGNALED(status))
|
|
printf("!! child died by signal %d\n", WTERMSIG(status));
|
|
else
|
|
printf("!! child died by unknown cause\n");
|
|
|
|
status = 1; /* Signal or other */
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static void sig_handler(int signum)
|
|
{
|
|
/* Just wake us up from waitpid */
|
|
}
|
|
|
|
static struct sigaction sig_action = {
|
|
.sa_handler = sig_handler,
|
|
};
|
|
|
|
void test_harness_set_timeout(uint64_t time)
|
|
{
|
|
timeout = time;
|
|
}
|
|
|
|
int test_harness(int (test_function)(void), char *name)
|
|
{
|
|
int rc;
|
|
|
|
test_start(name);
|
|
test_set_git_version(GIT_VERSION);
|
|
|
|
if (sigaction(SIGINT, &sig_action, NULL)) {
|
|
perror("sigaction (sigint)");
|
|
test_error(name);
|
|
return 1;
|
|
}
|
|
|
|
if (sigaction(SIGALRM, &sig_action, NULL)) {
|
|
perror("sigaction (sigalrm)");
|
|
test_error(name);
|
|
return 1;
|
|
}
|
|
|
|
rc = run_test(test_function, name);
|
|
|
|
if (rc == MAGIC_SKIP_RETURN_VALUE) {
|
|
test_skip(name);
|
|
/* so that skipped test is not marked as failed */
|
|
rc = 0;
|
|
} else
|
|
test_finish(name, rc);
|
|
|
|
return rc;
|
|
}
|