Re: [PATCH v2 19/39] tests/qtest: Support libqtest to build and run on Windows

2022-09-22 Thread Marc-André Lureau
Hi

On Tue, Sep 20, 2022 at 1:36 PM Bin Meng  wrote:

> From: Bin Meng 
>
> 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 
> Signed-off-by: Bin Meng 
>

lgtm
Reviewed-by: Marc-André Lureau 


> ---
>
> 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 
>  #include 
>  #include 
> +#endif /* _WIN32 */
>  #ifdef __linux__
>  #include 
>  #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, >wstatus, WNOHANG);
>  if (pid == 0) {
>  return true;
>  }
> +#else
> +DWORD exit_code;
> +GetExitCodeProcess((HANDLE)pid, _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, >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, _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(, sizeof(si));
> +si.cb = sizeof(si);
> +ZeroMemory(, sizeof(pi));
> +
> +ret = CreateProcess(NULL,   /* module name */
> +cmd,/* command line */
> +NULL,   /* process handle not inheritable */
> +NULL,   /* 

[PATCH v2 19/39] tests/qtest: Support libqtest to build and run on Windows

2022-09-20 Thread Bin Meng
From: Bin Meng 

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 
Signed-off-by: Bin Meng 
---

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 
 #include 
 #include 
+#endif /* _WIN32 */
 #ifdef __linux__
 #include 
 #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, >wstatus, WNOHANG);
 if (pid == 0) {
 return true;
 }
+#else
+DWORD exit_code;
+GetExitCodeProcess((HANDLE)pid, _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, >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, _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(, sizeof(si));
+si.cb = sizeof(si);
+ZeroMemory(, 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 */
+,/* pointer to STARTUPINFO structure */
+ /*