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

Reply via email to