From patchwork Sun Sep 25 11:30:14 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Bin Meng X-Patchwork-Id: 12987932 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id ED362C04A95 for ; Sun, 25 Sep 2022 12:14:26 +0000 (UTC) Received: from localhost ([::1]:58682 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ocQWf-00020M-Kq for qemu-devel@archiver.kernel.org; Sun, 25 Sep 2022 08:14:25 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:55882) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ocPrt-0003Jl-IZ for qemu-devel@nongnu.org; Sun, 25 Sep 2022 07:32:27 -0400 Received: from mail-pf1-x42b.google.com ([2607:f8b0:4864:20::42b]:43962) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1ocPrr-0006Qs-DW for qemu-devel@nongnu.org; Sun, 25 Sep 2022 07:32:17 -0400 Received: by mail-pf1-x42b.google.com with SMTP id d82so4152930pfd.10 for ; Sun, 25 Sep 2022 04:32:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date; bh=Piw1OXyWwwLD9JGgfjn4u0pfw+bsIph7Kp2bvtzr/Js=; b=eb3faEaDa/EKMQwOaEBYIvKXNq40p2dH3X9bkNgyVR3homHLG1wP2/XakmHx1/PaqT rNIh+VM5inOm+TqdTf0a8RttswbcaXF6J2q7ogMeQ3i3OQRlGGSh24NzkwvVBRn7uMs0 E0waPXdEX0H7ItRquoSFNoHwcCQ8TFNhcImbEgwwwv66sJKbAstm5sCgMCYeIpKOSt84 U+JJpzD/qUSbdrJyqpox7OUE8zeQOFM9ZdEiL0H2FPs9q75i2ikRq0PuGhW8qDScBLMm 4xF7ghXyQjxWGqXOS5WE3g13XVgcHvSZvvC9Q7lyfnU0umBo5h74wSEZoU5P9JKb4Nhc UN7Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date; bh=Piw1OXyWwwLD9JGgfjn4u0pfw+bsIph7Kp2bvtzr/Js=; b=ECQ1dZH5wLOFSMyVmkYvRG6LEz7hSioyRRi6+27+d9iJtVdZ3tvGNE54oLiIsLwdcp hqWBEhOQp7X9jlOBq+wX8dzNsNh6vqTHyOeexU3rXq0dU3FD9grxaZe9vlzRQ5W6tgmN zSbpX3XkmtgF7WaNDYiOjcLInp2c63cWDPoswhGOPcqpPaV8Flfvhc4BwyKv15bnxt5P CucrE8CyoRNc19pAX+6u1cYDC4zz6W1yRx9oUOYhRWXtuKvvo3vfAFCahQ5KmkUEJXeV O08XVNcR1/HwGe5PiPhfBk7IWsc7Z79SIbD2olazQ9b0a2fEzcMA4WKoXJzYdMNRroKh bxHQ== X-Gm-Message-State: ACrzQf2bGNlNw2Ffir0FuKwOmnnEOJf8V3R+iObSDT4150qQ1lYOYj/1 BS8Dxczgl8J8HGlx/66xRBl5ZPCpuEY= X-Google-Smtp-Source: AMsMyM4kyTB8GXA3CyLgplKV/qpsiAzgTiP6uEV+fwbCKiXkB2lcmS0P0zo+I9EmDXEkHa68iTo9xA== X-Received: by 2002:a63:4c5f:0:b0:439:49b4:809f with SMTP id m31-20020a634c5f000000b0043949b4809fmr14841749pgl.382.1664105533942; Sun, 25 Sep 2022 04:32:13 -0700 (PDT) Received: from ubuntu.. (144.168.56.201.16clouds.com. [144.168.56.201]) by smtp.gmail.com with ESMTPSA id z4-20020a1709027e8400b00176c6738d13sm9187760pla.169.2022.09.25.04.32.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 25 Sep 2022 04:32:13 -0700 (PDT) From: Bin Meng To: qemu-devel@nongnu.org Cc: =?utf-8?q?Marc-Andr=C3=A9_Lureau?= , Bin Meng , Xuzhou Cheng , Laurent Vivier , Paolo Bonzini , Thomas Huth Subject: [PATCH v3 36/54] tests/qtest: Support libqtest to build and run on Windows Date: Sun, 25 Sep 2022 19:30:14 +0800 Message-Id: <20220925113032.1949844-37-bmeng.cn@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220925113032.1949844-1-bmeng.cn@gmail.com> References: <20220925113032.1949844-1-bmeng.cn@gmail.com> MIME-Version: 1.0 Received-SPF: pass client-ip=2607:f8b0:4864:20::42b; envelope-from=bmeng.cn@gmail.com; helo=mail-pf1-x42b.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" 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 Reviewed-by: Marc-André Lureau --- (no changes since v2) 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 f0ac467903..12b1e85b51 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, &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",