diff options
Diffstat (limited to 'tests/qtest/libqtest.c')
-rw-r--r-- | tests/qtest/libqtest.c | 151 |
1 files changed, 133 insertions, 18 deletions
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index b23eb3edc3..2fbc3b88f3 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -16,9 +16,11 @@ #include "qemu/osdep.h" +#ifndef _WIN32 #include <sys/socket.h> #include <sys/wait.h> #include <sys/un.h> +#endif /* _WIN32 */ #ifdef __linux__ #include <sys/prctl.h> #endif /* __linux__ */ @@ -27,13 +29,25 @@ #include "libqmp.h" #include "qemu/ctype.h" #include "qemu/cutils.h" +#include "qemu/sockets.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qlist.h" #include "qapi/qmp/qstring.h" #define MAX_IRQ 256 -#define SOCKET_TIMEOUT 50 + +#ifndef _WIN32 +# define SOCKET_TIMEOUT 50 +# define CMD_EXEC "exec " +# define DEV_STDERR "/dev/fd/2" +# define DEV_NULL "/dev/null" +#else +# define SOCKET_TIMEOUT 50000 +# define CMD_EXEC "" +# define DEV_STDERR "2" +# define DEV_NULL "nul" +#endif typedef void (*QTestSendFn)(QTestState *s, const char *buf); typedef void (*ExternalSendFn)(void *s, const char *buf); @@ -57,6 +71,9 @@ struct QTestState int qmp_fd; pid_t qemu_pid; /* our child QEMU process */ int wstatus; +#ifdef _WIN32 + DWORD exit_code; +#endif int expected_status; bool big_endian; bool irq_level[MAX_IRQ]; @@ -90,8 +107,16 @@ static int socket_accept(int sock) struct sockaddr_un addr; socklen_t addrlen; int ret; + /* + * timeout unit of blocking receive calls is different among platfoms. + * It's in seconds on non-Windows platforms but milliseconds on Windows. + */ +#ifndef _WIN32 struct timeval timeout = { .tv_sec = SOCKET_TIMEOUT, .tv_usec = 0 }; +#else + DWORD timeout = SOCKET_TIMEOUT; +#endif if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&timeout, sizeof(timeout))) { @@ -118,10 +143,18 @@ bool qtest_probe_child(QTestState *s) pid_t pid = s->qemu_pid; if (pid != -1) { +#ifndef _WIN32 pid = waitpid(pid, &s->wstatus, WNOHANG); if (pid == 0) { return true; } +#else + GetExitCodeProcess((HANDLE)pid, &s->exit_code); + if (s->exit_code == STILL_ACTIVE) { + return true; + } + CloseHandle((HANDLE)pid); +#endif s->qemu_pid = -1; } return false; @@ -132,24 +165,14 @@ void qtest_set_expected_status(QTestState *s, int status) s->expected_status = status; } -void qtest_kill_qemu(QTestState *s) +static void qtest_check_status(QTestState *s) { - pid_t pid = s->qemu_pid; - int wstatus; - - /* Skip wait if qtest_probe_child already reaped. */ - if (pid != -1) { - kill(pid, SIGTERM); - TFR(pid = waitpid(s->qemu_pid, &s->wstatus, 0)); - assert(pid == s->qemu_pid); - s->qemu_pid = -1; - } - /* * Check whether qemu exited with expected exit status; anything else is * fishy and should be logged with as much detail as possible. */ - wstatus = s->wstatus; +#ifndef _WIN32 + int wstatus = s->wstatus; if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != s->expected_status) { fprintf(stderr, "%s:%d: kill_qemu() tried to terminate QEMU " "process but encountered exit status %d (expected %d)\n", @@ -165,6 +188,50 @@ void qtest_kill_qemu(QTestState *s) __FILE__, __LINE__, sig, signame, dump); abort(); } +#else + if (s->exit_code != s->expected_status) { + fprintf(stderr, "%s:%d: kill_qemu() tried to terminate QEMU " + "process but encountered exit status %ld (expected %d)\n", + __FILE__, __LINE__, s->exit_code, s->expected_status); + abort(); + } +#endif +} + +void qtest_wait_qemu(QTestState *s) +{ +#ifndef _WIN32 + pid_t pid; + + TFR(pid = waitpid(s->qemu_pid, &s->wstatus, 0)); + assert(pid == s->qemu_pid); +#else + DWORD ret; + + ret = WaitForSingleObject((HANDLE)s->qemu_pid, INFINITE); + assert(ret == WAIT_OBJECT_0); + GetExitCodeProcess((HANDLE)s->qemu_pid, &s->exit_code); + CloseHandle((HANDLE)s->qemu_pid); +#endif + + qtest_check_status(s); +} + +void qtest_kill_qemu(QTestState *s) +{ + /* Skip wait if qtest_probe_child() already reaped */ + if (s->qemu_pid != -1) { +#ifndef _WIN32 + kill(s->qemu_pid, SIGTERM); +#else + TerminateProcess((HANDLE)s->qemu_pid, s->expected_status); +#endif + qtest_wait_qemu(s); + s->qemu_pid = -1; + return; + } + + qtest_check_status(s); } static void kill_qemu_hook_func(void *s) @@ -243,6 +310,38 @@ static const char *qtest_qemu_binary(void) return qemu_bin; } +#ifdef _WIN32 +static pid_t qtest_create_process(char *cmd) +{ + STARTUPINFO si; + PROCESS_INFORMATION pi; + BOOL ret; + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + + ret = CreateProcess(NULL, /* module name */ + cmd, /* command line */ + NULL, /* process handle not inheritable */ + NULL, /* thread handle not inheritable */ + FALSE, /* set handle inheritance to FALSE */ + 0, /* No creation flags */ + NULL, /* use parent's environment block */ + NULL, /* use parent's starting directory */ + &si, /* pointer to STARTUPINFO structure */ + &pi /* pointer to PROCESS_INFORMATION structure */ + ); + if (ret == 0) { + fprintf(stderr, "%s:%d: unable to create a new process (%s)\n", + __FILE__, __LINE__, strerror(GetLastError())); + abort(); + } + + return (pid_t)pi.hProcess; +} +#endif /* _WIN32 */ + QTestState *qtest_init_without_qmp_handshake(const char *extra_args) { QTestState *s; @@ -270,6 +369,7 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) unlink(socket_path); unlink(qmp_socket_path); + socket_init(); sock = init_socket(socket_path); qmpsock = init_socket(qmp_socket_path); @@ -278,7 +378,7 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) qtest_add_abrt_handler(kill_qemu_hook_func, s); - command = g_strdup_printf("exec %s %s" + command = g_strdup_printf(CMD_EXEC "%s %s" "-qtest unix:%s " "-qtest-log %s " "-chardev socket,path=%s,id=char0 " @@ -287,7 +387,7 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) "%s" " -accel qtest", qemu_binary, tracearg, socket_path, - getenv("QTEST_LOG") ? "/dev/fd/2" : "/dev/null", + getenv("QTEST_LOG") ? DEV_STDERR : DEV_NULL, qmp_socket_path, extra_args ?: ""); @@ -296,6 +396,7 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) s->pending_events = NULL; s->wstatus = 0; s->expected_status = 0; +#ifndef _WIN32 s->qemu_pid = fork(); if (s->qemu_pid == 0) { #ifdef __linux__ @@ -318,6 +419,9 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) execlp("/bin/sh", "sh", "-c", command, NULL); exit(1); } +#else + s->qemu_pid = qtest_create_process(command); +#endif /* _WIN32 */ g_free(command); s->fd = socket_accept(sock); @@ -336,9 +440,19 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) s->irq_level[i] = false; } + /* + * Stopping QEMU for debugging is not supported on Windows. + * + * Using DebugActiveProcess() API can suspend the QEMU process, + * but gdb cannot attach to the process. Using the undocumented + * NtSuspendProcess() can suspend the QEMU process and gdb can + * attach to the process, but gdb cannot resume it. + */ +#ifndef _WIN32 if (getenv("QTEST_STOP")) { kill(s->qemu_pid, SIGSTOP); } +#endif /* ask endianness of the target */ @@ -392,6 +506,7 @@ QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd) g_assert_true(sock_dir != NULL); sock_path = g_strdup_printf("%s/sock", sock_dir); + socket_init(); sock_fd_init = init_socket(sock_path); qts = qtest_initf("-chardev socket,id=s0,path=%s -serial chardev:s0 %s", @@ -428,7 +543,7 @@ void qtest_quit(QTestState *s) static void socket_send(int fd, const char *buf, size_t size) { - size_t res = qemu_write_full(fd, buf, size); + ssize_t res = qemu_send_full(fd, buf, size); assert(res == size); } @@ -460,7 +575,7 @@ static GString *qtest_client_socket_recv_line(QTestState *s) ssize_t len; char buffer[1024]; - len = read(s->fd, buffer, sizeof(buffer)); + len = recv(s->fd, buffer, sizeof(buffer), 0); if (len == -1 && errno == EINTR) { continue; } |