qemu/tests/tcg/multiarch/linux/linux-test.c
Alex Bennée 5343a837cd tests/tcg: move some multiarch files and make conditional
We had some messy code to filter out stuff we can't build. Lets junk
that and simplify the logic by pushing some stuff into subdirs. In
particular we move:

  float_helpers into libs - not a standalone test
  linux-test into linux - so we only build on Linux hosts

This allows for at least some of the tests to be nominally usable
by *BSD user builds.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Cc: Warner Losh <imp@bsdimp.com>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Reviewed-by: Warner Losh <imp@bsdimp.com>
Message-Id: <20210917162332.3511179-4-alex.bennee@linaro.org>
2021-10-12 08:37:05 +01:00

546 lines
14 KiB
C

/*
* linux and CPU test
*
* Copyright (c) 2003 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define _GNU_SOURCE
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <inttypes.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <errno.h>
#include <utime.h>
#include <time.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sched.h>
#include <dirent.h>
#include <setjmp.h>
#include <sys/shm.h>
#include <assert.h>
#define STACK_SIZE 16384
static void error1(const char *filename, int line, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "%s:%d: ", filename, line);
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
exit(1);
}
static int __chk_error(const char *filename, int line, int ret)
{
if (ret < 0) {
error1(filename, line, "%m (ret=%d, errno=%d/%s)",
ret, errno, strerror(errno));
}
return ret;
}
#define error(fmt, ...) error1(__FILE__, __LINE__, fmt, ## __VA_ARGS__)
#define chk_error(ret) __chk_error(__FILE__, __LINE__, (ret))
/*******************************************************/
#define FILE_BUF_SIZE 300
static void test_file(void)
{
int fd, i, len, ret;
uint8_t buf[FILE_BUF_SIZE];
uint8_t buf2[FILE_BUF_SIZE];
uint8_t buf3[FILE_BUF_SIZE];
char cur_dir[1024];
struct stat st;
struct utimbuf tbuf;
struct iovec vecs[2];
DIR *dir;
struct dirent64 *de;
/* TODO: make common tempdir creation for tcg tests */
char template[] = "/tmp/linux-test-XXXXXX";
char *tmpdir = mkdtemp(template);
assert(tmpdir);
if (getcwd(cur_dir, sizeof(cur_dir)) == NULL)
error("getcwd");
chk_error(chdir(tmpdir));
/* open/read/write/close/readv/writev/lseek */
fd = chk_error(open("file1", O_WRONLY | O_TRUNC | O_CREAT, 0644));
for(i=0;i < FILE_BUF_SIZE; i++)
buf[i] = i;
len = chk_error(write(fd, buf, FILE_BUF_SIZE / 2));
if (len != (FILE_BUF_SIZE / 2))
error("write");
vecs[0].iov_base = buf + (FILE_BUF_SIZE / 2);
vecs[0].iov_len = 16;
vecs[1].iov_base = buf + (FILE_BUF_SIZE / 2) + 16;
vecs[1].iov_len = (FILE_BUF_SIZE / 2) - 16;
len = chk_error(writev(fd, vecs, 2));
if (len != (FILE_BUF_SIZE / 2))
error("writev");
chk_error(close(fd));
chk_error(rename("file1", "file2"));
fd = chk_error(open("file2", O_RDONLY));
len = chk_error(read(fd, buf2, FILE_BUF_SIZE));
if (len != FILE_BUF_SIZE)
error("read");
if (memcmp(buf, buf2, FILE_BUF_SIZE) != 0)
error("memcmp");
#define FOFFSET 16
ret = chk_error(lseek(fd, FOFFSET, SEEK_SET));
if (ret != 16)
error("lseek");
vecs[0].iov_base = buf3;
vecs[0].iov_len = 32;
vecs[1].iov_base = buf3 + 32;
vecs[1].iov_len = FILE_BUF_SIZE - FOFFSET - 32;
len = chk_error(readv(fd, vecs, 2));
if (len != FILE_BUF_SIZE - FOFFSET)
error("readv");
if (memcmp(buf + FOFFSET, buf3, FILE_BUF_SIZE - FOFFSET) != 0)
error("memcmp");
chk_error(close(fd));
/* access */
chk_error(access("file2", R_OK));
/* stat/chmod/utime/truncate */
chk_error(chmod("file2", 0600));
tbuf.actime = 1001;
tbuf.modtime = 1000;
chk_error(truncate("file2", 100));
chk_error(utime("file2", &tbuf));
chk_error(stat("file2", &st));
if (st.st_size != 100)
error("stat size");
if (!S_ISREG(st.st_mode))
error("stat mode");
if ((st.st_mode & 0777) != 0600)
error("stat mode2");
if (st.st_atime != 1001 ||
st.st_mtime != 1000)
error("stat time");
chk_error(stat(tmpdir, &st));
if (!S_ISDIR(st.st_mode))
error("stat mode");
/* fstat */
fd = chk_error(open("file2", O_RDWR));
chk_error(ftruncate(fd, 50));
chk_error(fstat(fd, &st));
chk_error(close(fd));
if (st.st_size != 50)
error("stat size");
if (!S_ISREG(st.st_mode))
error("stat mode");
/* symlink/lstat */
chk_error(symlink("file2", "file3"));
chk_error(lstat("file3", &st));
if (!S_ISLNK(st.st_mode))
error("stat mode");
/* getdents */
dir = opendir(tmpdir);
if (!dir)
error("opendir");
len = 0;
for(;;) {
de = readdir64(dir);
if (!de)
break;
if (strcmp(de->d_name, ".") != 0 &&
strcmp(de->d_name, "..") != 0 &&
strcmp(de->d_name, "file2") != 0 &&
strcmp(de->d_name, "file3") != 0)
error("readdir");
len++;
}
closedir(dir);
if (len != 4)
error("readdir");
chk_error(unlink("file3"));
chk_error(unlink("file2"));
chk_error(chdir(cur_dir));
chk_error(rmdir(tmpdir));
}
static void test_fork(void)
{
int pid, status;
pid = chk_error(fork());
if (pid == 0) {
/* child */
sleep(2);
exit(2);
}
chk_error(waitpid(pid, &status, 0));
if (!WIFEXITED(status) || WEXITSTATUS(status) != 2)
error("waitpid status=0x%x", status);
}
static void test_time(void)
{
struct timeval tv, tv2;
struct timespec ts, rem;
struct rusage rusg1, rusg2;
int ti, i;
chk_error(gettimeofday(&tv, NULL));
rem.tv_sec = 1;
ts.tv_sec = 0;
ts.tv_nsec = 20 * 1000000;
chk_error(nanosleep(&ts, &rem));
if (rem.tv_sec != 1)
error("nanosleep");
chk_error(gettimeofday(&tv2, NULL));
ti = tv2.tv_sec - tv.tv_sec;
if (ti >= 2)
error("gettimeofday");
chk_error(getrusage(RUSAGE_SELF, &rusg1));
for(i = 0;i < 10000; i++);
chk_error(getrusage(RUSAGE_SELF, &rusg2));
if ((rusg2.ru_utime.tv_sec - rusg1.ru_utime.tv_sec) < 0 ||
(rusg2.ru_stime.tv_sec - rusg1.ru_stime.tv_sec) < 0)
error("getrusage");
}
static int server_socket(void)
{
int val, fd;
struct sockaddr_in sockaddr = {};
/* server socket */
fd = chk_error(socket(PF_INET, SOCK_STREAM, 0));
val = 1;
chk_error(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)));
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(0); /* choose random ephemeral port) */
sockaddr.sin_addr.s_addr = 0;
chk_error(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)));
chk_error(listen(fd, 0));
return fd;
}
static int client_socket(uint16_t port)
{
int fd;
struct sockaddr_in sockaddr = {};
/* server socket */
fd = chk_error(socket(PF_INET, SOCK_STREAM, 0));
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(port);
inet_aton("127.0.0.1", &sockaddr.sin_addr);
chk_error(connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)));
return fd;
}
static const char socket_msg[] = "hello socket\n";
static void test_socket(void)
{
int server_fd, client_fd, fd, pid, ret, val;
struct sockaddr_in sockaddr;
struct sockaddr_in server_addr;
socklen_t len, socklen;
uint16_t server_port;
char buf[512];
server_fd = server_socket();
/* find out what port we got */
socklen = sizeof(server_addr);
ret = getsockname(server_fd, (struct sockaddr *)&server_addr, &socklen);
chk_error(ret);
server_port = ntohs(server_addr.sin_port);
/* test a few socket options */
len = sizeof(val);
chk_error(getsockopt(server_fd, SOL_SOCKET, SO_TYPE, &val, &len));
if (val != SOCK_STREAM)
error("getsockopt");
pid = chk_error(fork());
if (pid == 0) {
client_fd = client_socket(server_port);
send(client_fd, socket_msg, sizeof(socket_msg), 0);
close(client_fd);
exit(0);
}
len = sizeof(sockaddr);
fd = chk_error(accept(server_fd, (struct sockaddr *)&sockaddr, &len));
ret = chk_error(recv(fd, buf, sizeof(buf), 0));
if (ret != sizeof(socket_msg))
error("recv");
if (memcmp(buf, socket_msg, sizeof(socket_msg)) != 0)
error("socket_msg");
chk_error(close(fd));
chk_error(close(server_fd));
}
#define WCOUNT_MAX 512
static void test_pipe(void)
{
fd_set rfds, wfds;
int fds[2], fd_max, ret;
uint8_t ch;
int wcount, rcount;
chk_error(pipe(fds));
chk_error(fcntl(fds[0], F_SETFL, O_NONBLOCK));
chk_error(fcntl(fds[1], F_SETFL, O_NONBLOCK));
wcount = 0;
rcount = 0;
for(;;) {
FD_ZERO(&rfds);
fd_max = fds[0];
FD_SET(fds[0], &rfds);
FD_ZERO(&wfds);
FD_SET(fds[1], &wfds);
if (fds[1] > fd_max)
fd_max = fds[1];
ret = chk_error(select(fd_max + 1, &rfds, &wfds, NULL, NULL));
if (ret > 0) {
if (FD_ISSET(fds[0], &rfds)) {
chk_error(read(fds[0], &ch, 1));
rcount++;
if (rcount >= WCOUNT_MAX)
break;
}
if (FD_ISSET(fds[1], &wfds)) {
ch = 'a';
chk_error(write(fds[1], &ch, 1));
wcount++;
}
}
}
chk_error(close(fds[0]));
chk_error(close(fds[1]));
}
static int thread1_res;
static int thread2_res;
static int thread1_func(void *arg)
{
int i;
for(i=0;i<5;i++) {
thread1_res++;
usleep(10 * 1000);
}
return 0;
}
static int thread2_func(void *arg)
{
int i;
for(i=0;i<6;i++) {
thread2_res++;
usleep(10 * 1000);
}
return 0;
}
static void wait_for_child(pid_t pid)
{
int status;
chk_error(waitpid(pid, &status, 0));
}
/* For test_clone we must match the clone flags used by glibc, see
* CLONE_THREAD_FLAGS in the QEMU source code.
*/
static void test_clone(void)
{
uint8_t *stack1, *stack2;
pid_t pid1, pid2;
stack1 = malloc(STACK_SIZE);
pid1 = chk_error(clone(thread1_func, stack1 + STACK_SIZE,
CLONE_VM | CLONE_FS | CLONE_FILES |
CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM,
"hello1"));
stack2 = malloc(STACK_SIZE);
pid2 = chk_error(clone(thread2_func, stack2 + STACK_SIZE,
CLONE_VM | CLONE_FS | CLONE_FILES |
CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM,
"hello2"));
wait_for_child(pid1);
free(stack1);
wait_for_child(pid2);
free(stack2);
if (thread1_res != 5 ||
thread2_res != 6)
error("clone");
}
/***********************************/
volatile int alarm_count;
jmp_buf jmp_env;
static void sig_alarm(int sig)
{
if (sig != SIGALRM)
error("signal");
alarm_count++;
}
static void sig_segv(int sig, siginfo_t *info, void *puc)
{
if (sig != SIGSEGV)
error("signal");
longjmp(jmp_env, 1);
}
static void test_signal(void)
{
struct sigaction act;
struct itimerval it, oit;
/* timer test */
alarm_count = 0;
act.sa_handler = sig_alarm;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
chk_error(sigaction(SIGALRM, &act, NULL));
it.it_interval.tv_sec = 0;
it.it_interval.tv_usec = 10 * 1000;
it.it_value.tv_sec = 0;
it.it_value.tv_usec = 10 * 1000;
chk_error(setitimer(ITIMER_REAL, &it, NULL));
chk_error(getitimer(ITIMER_REAL, &oit));
while (alarm_count < 5) {
usleep(10 * 1000);
getitimer(ITIMER_REAL, &oit);
}
it.it_interval.tv_sec = 0;
it.it_interval.tv_usec = 0;
it.it_value.tv_sec = 0;
it.it_value.tv_usec = 0;
memset(&oit, 0xff, sizeof(oit));
chk_error(setitimer(ITIMER_REAL, &it, &oit));
/* SIGSEGV test */
act.sa_sigaction = sig_segv;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
chk_error(sigaction(SIGSEGV, &act, NULL));
if (setjmp(jmp_env) == 0) {
/*
* clang requires volatile or it will turn this into a
* call to abort() instead of forcing a SIGSEGV.
*/
*(volatile uint8_t *)0 = 0;
}
act.sa_handler = SIG_DFL;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
chk_error(sigaction(SIGSEGV, &act, NULL));
if (sigaction(SIGKILL, &act, NULL) == 0) {
error("sigaction(SIGKILL, &act, NULL) must not succeed");
}
if (sigaction(SIGSTOP, &act, NULL) == 0) {
error("sigaction(SIGSTOP, &act, NULL) must not succeed");
}
chk_error(sigaction(SIGKILL, NULL, &act));
chk_error(sigaction(SIGSTOP, NULL, &act));
}
#define SHM_SIZE 32768
static void test_shm(void)
{
void *ptr;
int shmid;
shmid = chk_error(shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | 0777));
ptr = shmat(shmid, NULL, 0);
if (ptr == (void *)-1) {
error("shmat");
}
memset(ptr, 0, SHM_SIZE);
chk_error(shmctl(shmid, IPC_RMID, 0));
chk_error(shmdt(ptr));
}
int main(int argc, char **argv)
{
test_file();
test_pipe();
test_fork();
test_time();
test_socket();
if (argc > 1) {
printf("test_clone still considered buggy\n");
test_clone();
}
test_signal();
test_shm();
return 0;
}