Hi On Tue, Sep 20, 2022 at 1:36 PM Bin Meng <bmeng...@gmail.com> wrote:
> From: Bin Meng <bin.m...@windriver.com> > > At present the libqtest codes were written to depend on several > POSIX APIs, including fork(), kill() and waitpid(). Unfortunately > these APIs are not available on Windows. > > This commit implements the corresponding functionalities using > win32 native APIs. With this change, all qtest cases can build > successfully on a Windows host, and we can start qtest testing > on Windows now. > > Signed-off-by: Xuzhou Cheng <xuzhou.ch...@windriver.com> > Signed-off-by: Bin Meng <bin.m...@windriver.com> > lgtm Reviewed-by: Marc-André Lureau <marcandre.lur...@redhat.com> > --- > > Changes in v2: > - Move the enabling of building qtests on Windows to a separate > patch to keep bisectablity > - Call socket_init() unconditionally > - Add a missing CloseHandle() call > > tests/qtest/libqtest.c | 98 +++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 96 insertions(+), 2 deletions(-) > > diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c > index f46a21fa45..5d15e39289 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,6 +29,7 @@ > #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" > @@ -35,6 +38,16 @@ > #define MAX_IRQ 256 > #define SOCKET_TIMEOUT 50 > > +#ifndef _WIN32 > +# define CMD_EXEC "exec " > +# define DEV_STDERR "/dev/fd/2" > +# define DEV_NULL "/dev/null" > +#else > +# 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); > typedef GString* (*QTestRecvFn)(QTestState *); > @@ -66,6 +79,9 @@ struct QTestState > }; > > static GHookList abrt_hooks; > +#ifdef _WIN32 > +typedef void (*sighandler_t)(int); > +#endif > static sighandler_t sighandler_old; > > static int qtest_query_target_endianness(QTestState *s); > @@ -118,10 +134,19 @@ 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 > + DWORD exit_code; > + GetExitCodeProcess((HANDLE)pid, &exit_code); > + if (exit_code == STILL_ACTIVE) { > + return true; > + } > + CloseHandle((HANDLE)pid); > +#endif > s->qemu_pid = -1; > } > return false; > @@ -135,13 +160,23 @@ void qtest_set_expected_status(QTestState *s, int > status) > void qtest_kill_qemu(QTestState *s) > { > pid_t pid = s->qemu_pid; > +#ifndef _WIN32 > int wstatus; > +#else > + DWORD ret, exit_code; > +#endif > > /* Skip wait if qtest_probe_child already reaped. */ > if (pid != -1) { > +#ifndef _WIN32 > kill(pid, SIGTERM); > TFR(pid = waitpid(s->qemu_pid, &s->wstatus, 0)); > assert(pid == s->qemu_pid); > +#else > + TerminateProcess((HANDLE)pid, s->expected_status); > + ret = WaitForSingleObject((HANDLE)pid, INFINITE); > + assert(ret == WAIT_OBJECT_0); > +#endif > s->qemu_pid = -1; > } > > @@ -149,6 +184,7 @@ void qtest_kill_qemu(QTestState *s) > * Check whether qemu exited with expected exit status; anything else > is > * fishy and should be logged with as much detail as possible. > */ > +#ifndef _WIN32 > wstatus = s->wstatus; > if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != s->expected_status) > { > fprintf(stderr, "%s:%d: kill_qemu() tried to terminate QEMU " > @@ -165,6 +201,16 @@ void qtest_kill_qemu(QTestState *s) > __FILE__, __LINE__, sig, signame, dump); > abort(); > } > +#else > + GetExitCodeProcess((HANDLE)pid, &exit_code); > + CloseHandle((HANDLE)pid); > + if (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__, exit_code, s->expected_status); > + abort(); > + } > +#endif > } > > static void kill_qemu_hook_func(void *s) > @@ -243,6 +289,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 +348,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 +357,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 +366,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 +375,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 +398,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 +419,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 +485,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", > -- > 2.34.1 > > > -- Marc-André Lureau