aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2025-02-10 13:26:17 -0500
committerStefan Hajnoczi <stefanha@redhat.com>2025-02-10 13:26:17 -0500
commitffaf7f0376f8040ce9068d71ae9ae8722505c42e (patch)
treec84be244b60ee3e1f96cb858bad1a1589a586c98
parentf2ec48fefd172a8dd20cb0073087d659aca9578c (diff)
parent24c61663dcec0e87bb4206a7623f0e222e188b47 (diff)
downloadqemu-ffaf7f0376f8040ce9068d71ae9ae8722505c42e.zip
qemu-ffaf7f0376f8040ce9068d71ae9ae8722505c42e.tar.gz
qemu-ffaf7f0376f8040ce9068d71ae9ae8722505c42e.tar.bz2
Merge tag 'pull-10.0-testing-and-gdstub-updates-100225-1' of https://gitlab.com/stsquad/qemu into staging
testing and gdbstub updates: - add a check-rust test to docker builds - re-factor the qtest logic to be cleaner - fix tests to not clock_step when no timers enabled - roll-up log prefix into qtest_send - cleaner error reporting when qtest_clock_set fails - revert old deadlock fix now tests are updated - only run full set of migration tests under HW acceleration - support late attachment to user-mode gdbstubs # -----BEGIN PGP SIGNATURE----- # # iQEzBAABCgAdFiEEZoWumedRZ7yvyN81+9DbCVqeKkQFAmeqBSsACgkQ+9DbCVqe # KkQS/Af+K0hpdGc1msiuMsqmuESBvhoQniYZFLN1/pwe2KpG8i/+fq2fsCuxJhJ1 # 2TzPH7aj54p9MGCZf2k9JLhO22XldN+oezZMc1crhoWK0AtrWhnLs58I2oEPIsUo # NmGO6Zfm98ge89o2y8GCvd0QXAtUf+jduDKnW0mfnOnw+w/mky5KzWS7/1091VGW # 42LSY4KnqgdLSqLyuLBOrgADEjB1ChWS4/bSC+kEYSGrmNQB+n1KeIzzlJBGpOr0 # Z9yzmhMCm7TWdkFNPmnVfYH/7ZUNcpv6PtQSpkku4f6b/gybyvJBknHpM4i+Gpb5 # 87wSjljrCpdNm/9KFRjiJuUWdS/jCg== # =UF0n # -----END PGP SIGNATURE----- # gpg: Signature made Mon 10 Feb 2025 08:54:51 EST # gpg: using RSA key 6685AE99E75167BCAFC8DF35FBD0DB095A9E2A44 # gpg: Good signature from "Alex Bennée (Master Work Key) <alex.bennee@linaro.org>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 6685 AE99 E751 67BC AFC8 DF35 FBD0 DB09 5A9E 2A44 * tag 'pull-10.0-testing-and-gdstub-updates-100225-1' of https://gitlab.com/stsquad/qemu: tests/tcg: Add late gdbstub attach test docs/user: Document the %d placeholder and suspend=n QEMU_GDB features gdbstub: Allow late attachment osdep: Introduce qemu_kill_thread() user: Introduce host_interrupt_signal user: Introduce user/signal.h gdbstub: Try unlinking the unix socket before binding gdbstub: Allow the %d placeholder in the socket path tests/qtest/migration: Pick smoke tests tests/qtest/migration: Add --full option Revert "util/timer: avoid deadlock when shutting down" tests/qtest: tighten up the checks on clock_step tests/qtest: rename qtest_send_prefix and roll-up into qtest_send tests/qtest: simplify qtest_process_inbuf tests/qtest: don't step clock at start of npcm7xx periodic IRQ test tests/qtest: don't attempt to clock_step while waiting for virtio ISR tests/docker: replicate the check-rust-tools-nightly CI job Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
-rw-r--r--MAINTAINERS1
-rw-r--r--bsd-user/main.c1
-rw-r--r--bsd-user/signal-common.h1
-rw-r--r--bsd-user/signal.c13
-rw-r--r--docs/user/main.rst16
-rw-r--r--gdbstub/user.c150
-rw-r--r--hw/ppc/spapr_rtas.c1
-rw-r--r--hw/riscv/riscv_hart.c1
-rw-r--r--include/qemu/osdep.h9
-rw-r--r--include/system/qtest.h1
-rw-r--r--include/user/signal.h25
-rw-r--r--linux-user/main.c1
-rw-r--r--linux-user/signal-common.h1
-rw-r--r--linux-user/signal.c26
-rw-r--r--linux-user/syscall.c1
-rw-r--r--stubs/meson.build2
-rw-r--r--stubs/monitor-fd.c9
-rw-r--r--system/qtest.c59
-rw-r--r--tests/docker/Makefile.include3
-rwxr-xr-xtests/docker/test-rust21
-rwxr-xr-xtests/guest-debug/run-test.py15
-rw-r--r--tests/qtest/libqos/virtio-pci-modern.c6
-rw-r--r--tests/qtest/libqos/virtio-pci.c6
-rw-r--r--tests/qtest/meson.build11
-rw-r--r--tests/qtest/migration-test.c25
-rw-r--r--tests/qtest/migration/compression-tests.c15
-rw-r--r--tests/qtest/migration/cpr-tests.c6
-rw-r--r--tests/qtest/migration/file-tests.c19
-rw-r--r--tests/qtest/migration/framework.h1
-rw-r--r--tests/qtest/migration/misc-tests.c16
-rw-r--r--tests/qtest/migration/postcopy-tests.c14
-rw-r--r--tests/qtest/migration/precopy-tests.c23
-rw-r--r--tests/qtest/migration/tls-tests.c14
-rw-r--r--tests/qtest/npcm7xx_timer-test.c1
-rw-r--r--tests/tcg/multiarch/Makefile.target9
-rw-r--r--tests/tcg/multiarch/gdbstub/late-attach.py28
-rw-r--r--tests/tcg/multiarch/late-attach.c41
-rw-r--r--util/meson.build2
-rw-r--r--util/oslib-posix.c15
-rw-r--r--util/qemu-timer.c16
40 files changed, 490 insertions, 135 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index fefabe0..842bac8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3383,6 +3383,7 @@ F: rust/rustfmt.toml
Rust-related patches CC here
L: qemu-rust@nongnu.org
+F: tests/docker/test-rust
F: rust/
SLIRP
diff --git a/bsd-user/main.c b/bsd-user/main.c
index b2f6a9b..fdb160b 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -629,7 +629,6 @@ int main(int argc, char **argv)
if (gdbstub) {
gdbserver_start(gdbstub, &error_fatal);
- gdb_handlesig(cpu, 0, NULL, NULL, 0);
}
cpu_loop(env);
/* never exits */
diff --git a/bsd-user/signal-common.h b/bsd-user/signal-common.h
index 77d7c7a..4e634e0 100644
--- a/bsd-user/signal-common.h
+++ b/bsd-user/signal-common.h
@@ -42,7 +42,6 @@ void process_pending_signals(CPUArchState *env);
void queue_signal(CPUArchState *env, int sig, int si_type,
target_siginfo_t *info);
void signal_init(void);
-int target_to_host_signal(int sig);
void target_to_host_sigset(sigset_t *d, const target_sigset_t *s);
/*
diff --git a/bsd-user/signal.c b/bsd-user/signal.c
index b4e1458..ff2ccbb 100644
--- a/bsd-user/signal.c
+++ b/bsd-user/signal.c
@@ -24,6 +24,7 @@
#include "user/cpu_loop.h"
#include "exec/page-protection.h"
#include "user/page-protection.h"
+#include "user/signal.h"
#include "user/tswap-target.h"
#include "gdbstub/user.h"
#include "signal-common.h"
@@ -50,6 +51,8 @@ static inline int sas_ss_flags(TaskState *ts, unsigned long sp)
on_sig_stack(ts, sp) ? SS_ONSTACK : 0;
}
+int host_interrupt_signal = SIGRTMAX;
+
/*
* The BSD ABIs use the same signal numbers across all the CPU architectures, so
* (unlike Linux) these functions are just the identity mapping. This might not
@@ -490,6 +493,12 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
uintptr_t pc = 0;
bool sync_sig = false;
+ if (host_sig == host_interrupt_signal) {
+ ts->signal_pending = 1;
+ cpu_exit(thread_cpu);
+ return;
+ }
+
/*
* Non-spoofed SIGSEGV and SIGBUS are synchronous, and need special
* handling wrt signal blocking and unwinding.
@@ -853,6 +862,9 @@ void signal_init(void)
for (i = 1; i <= TARGET_NSIG; i++) {
host_sig = target_to_host_signal(i);
+ if (host_sig == host_interrupt_signal) {
+ continue;
+ }
sigaction(host_sig, NULL, &oact);
if (oact.sa_sigaction == (void *)SIG_IGN) {
sigact_table[i - 1]._sa_handler = TARGET_SIG_IGN;
@@ -871,6 +883,7 @@ void signal_init(void)
sigaction(host_sig, &act, NULL);
}
}
+ sigaction(host_interrupt_signal, &act, NULL);
}
static void handle_pending_signal(CPUArchState *env, int sig,
diff --git a/docs/user/main.rst b/docs/user/main.rst
index 80a77f0..9a1c604 100644
--- a/docs/user/main.rst
+++ b/docs/user/main.rst
@@ -54,7 +54,7 @@ Command line options
::
- qemu-i386 [-h] [-d] [-L path] [-s size] [-cpu model] [-g port] [-B offset] [-R size] program [arguments...]
+ qemu-i386 [-h] [-d] [-L path] [-s size] [-cpu model] [-g endpoint] [-B offset] [-R size] program [arguments...]
``-h``
Print the help
@@ -91,8 +91,18 @@ Debug options:
Activate logging of the specified items (use '-d help' for a list of
log items)
-``-g port``
- Wait gdb connection to port
+``-g endpoint``
+ Wait gdb connection to a port (e.g., ``1234``) or a unix socket (e.g.,
+ ``/tmp/qemu.sock``).
+
+ If a unix socket path contains single ``%d`` placeholder (e.g.,
+ ``/tmp/qemu-%d.sock``), it is replaced by the emulator PID, which is useful
+ when passing this option via the ``QEMU_GDB`` environment variable to a
+ multi-process application.
+
+ If the endpoint address is followed by ``,suspend=n`` (e.g.,
+ ``1234,suspend=n``), then the emulated program starts without waiting for a
+ connection, which can be established at any later point in time.
``-one-insn-per-tb``
Run the emulation with one guest instruction per translation block.
diff --git a/gdbstub/user.c b/gdbstub/user.c
index c2bdfc3..3730f32 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -22,6 +22,7 @@
#include "gdbstub/user.h"
#include "gdbstub/enums.h"
#include "hw/core/cpu.h"
+#include "user/signal.h"
#include "trace.h"
#include "internals.h"
@@ -315,33 +316,20 @@ static bool gdb_accept_socket(int gdb_fd)
return true;
}
-static int gdbserver_open_socket(const char *path)
+static int gdbserver_open_socket(const char *path, Error **errp)
{
- struct sockaddr_un sockaddr = {};
- int fd, ret;
+ g_autoptr(GString) buf = g_string_new("");
+ char *pid_placeholder;
- fd = socket(AF_UNIX, SOCK_STREAM, 0);
- if (fd < 0) {
- perror("create socket");
- return -1;
+ pid_placeholder = strstr(path, "%d");
+ if (pid_placeholder != NULL) {
+ g_string_append_len(buf, path, pid_placeholder - path);
+ g_string_append_printf(buf, "%d", qemu_get_thread_id());
+ g_string_append(buf, pid_placeholder + 2);
+ path = buf->str;
}
- sockaddr.sun_family = AF_UNIX;
- pstrcpy(sockaddr.sun_path, sizeof(sockaddr.sun_path) - 1, path);
- ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
- if (ret < 0) {
- perror("bind socket");
- close(fd);
- return -1;
- }
- ret = listen(fd, 1);
- if (ret < 0) {
- perror("listen socket");
- close(fd);
- return -1;
- }
-
- return fd;
+ return unix_listen(path, errp);
}
static bool gdb_accept_tcp(int gdb_fd)
@@ -406,32 +394,122 @@ static int gdbserver_open_port(int port, Error **errp)
return fd;
}
-bool gdbserver_start(const char *port_or_path, Error **errp)
+static bool gdbserver_accept(int port, int gdb_fd, const char *path)
{
- int port = g_ascii_strtoull(port_or_path, NULL, 10);
+ bool ret;
+
+ if (port > 0) {
+ ret = gdb_accept_tcp(gdb_fd);
+ } else {
+ ret = gdb_accept_socket(gdb_fd);
+ if (ret) {
+ gdbserver_user_state.socket_path = g_strdup(path);
+ }
+ }
+
+ if (!ret) {
+ close(gdb_fd);
+ }
+
+ return ret;
+}
+
+struct {
+ int port;
int gdb_fd;
+ char *path;
+} gdbserver_args;
+
+static void do_gdb_handlesig(CPUState *cs, run_on_cpu_data arg)
+{
+ int sig;
+
+ sig = target_to_host_signal(gdb_handlesig(cs, 0, NULL, NULL, 0));
+ if (sig >= 1 && sig < NSIG) {
+ qemu_kill_thread(gdb_get_cpu_index(cs), sig);
+ }
+}
+
+static void *gdbserver_accept_thread(void *arg)
+{
+ if (gdbserver_accept(gdbserver_args.port, gdbserver_args.gdb_fd,
+ gdbserver_args.path)) {
+ CPUState *cs = first_cpu;
+
+ async_safe_run_on_cpu(cs, do_gdb_handlesig, RUN_ON_CPU_NULL);
+ qemu_kill_thread(gdb_get_cpu_index(cs), host_interrupt_signal);
+ }
+
+ g_free(gdbserver_args.path);
+ gdbserver_args.path = NULL;
+
+ return NULL;
+}
+#define USAGE "\nUsage: -g {port|path}[,suspend={y|n}]"
+
+bool gdbserver_start(const char *args, Error **errp)
+{
+ g_auto(GStrv) argv = g_strsplit(args, ",", 0);
+ const char *port_or_path = NULL;
+ bool suspend = true;
+ int gdb_fd, port;
+ GStrv arg;
+
+ for (arg = argv; *arg; arg++) {
+ g_auto(GStrv) tokens = g_strsplit(*arg, "=", 2);
+
+ if (g_strcmp0(tokens[0], "suspend") == 0) {
+ if (tokens[1] == NULL) {
+ error_setg(errp,
+ "gdbstub: missing \"suspend\" option value" USAGE);
+ return false;
+ } else if (!qapi_bool_parse(tokens[0], tokens[1],
+ &suspend, errp)) {
+ return false;
+ }
+ } else {
+ if (port_or_path) {
+ error_setg(errp, "gdbstub: unknown option \"%s\"" USAGE, *arg);
+ return false;
+ }
+ port_or_path = *arg;
+ }
+ }
+ if (!port_or_path) {
+ error_setg(errp, "gdbstub: port or path not specified" USAGE);
+ return false;
+ }
+
+ port = g_ascii_strtoull(port_or_path, NULL, 10);
if (port > 0) {
gdb_fd = gdbserver_open_port(port, errp);
} else {
- gdb_fd = gdbserver_open_socket(port_or_path);
+ gdb_fd = gdbserver_open_socket(port_or_path, errp);
}
-
if (gdb_fd < 0) {
return false;
}
- if (port > 0 && gdb_accept_tcp(gdb_fd)) {
- return true;
- } else if (gdb_accept_socket(gdb_fd)) {
- gdbserver_user_state.socket_path = g_strdup(port_or_path);
+ if (suspend) {
+ if (gdbserver_accept(port, gdb_fd, port_or_path)) {
+ gdb_handlesig(first_cpu, 0, NULL, NULL, 0);
+ return true;
+ } else {
+ error_setg(errp, "gdbstub: failed to accept connection");
+ return false;
+ }
+ } else {
+ QemuThread thread;
+
+ gdbserver_args.port = port;
+ gdbserver_args.gdb_fd = gdb_fd;
+ gdbserver_args.path = g_strdup(port_or_path);
+ qemu_thread_create(&thread, "gdb-accept",
+ &gdbserver_accept_thread, NULL,
+ QEMU_THREAD_DETACHED);
return true;
}
-
- /* gone wrong */
- close(gdb_fd);
- error_setg(errp, "gdbstub: failed to accept connection");
- return false;
}
void gdbserver_fork_start(void)
diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c
index df2e837..503d441 100644
--- a/hw/ppc/spapr_rtas.c
+++ b/hw/ppc/spapr_rtas.c
@@ -565,7 +565,6 @@ static bool spapr_qtest_callback(CharBackend *chr, gchar **words)
g_assert(rc == 0);
res = qtest_rtas_call(words[1], nargs, args, nret, ret);
- qtest_send_prefix(chr);
qtest_sendf(chr, "OK %"PRIu64"\n", res);
return true;
diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c
index ad67cd7..a55d156 100644
--- a/hw/riscv/riscv_hart.c
+++ b/hw/riscv/riscv_hart.c
@@ -94,7 +94,6 @@ static bool csr_qtest_callback(CharBackend *chr, gchar **words)
g_assert(rc == 0);
csr_call(words[1], cpu, csr, &val);
- qtest_send_prefix(chr);
qtest_sendf(chr, "OK 0 "TARGET_FMT_lx"\n", (target_ulong)val);
return true;
diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h
index 112ebdf..4397a90 100644
--- a/include/qemu/osdep.h
+++ b/include/qemu/osdep.h
@@ -631,6 +631,15 @@ bool qemu_write_pidfile(const char *pidfile, Error **errp);
int qemu_get_thread_id(void);
+/**
+ * qemu_kill_thread:
+ * @tid: thread id.
+ * @sig: host signal.
+ *
+ * Send @sig to one of QEMU's own threads with identifier @tid.
+ */
+int qemu_kill_thread(int tid, int sig);
+
#ifndef CONFIG_IOVEC
struct iovec {
void *iov_base;
diff --git a/include/system/qtest.h b/include/system/qtest.h
index c161d75..6ddddc5 100644
--- a/include/system/qtest.h
+++ b/include/system/qtest.h
@@ -24,7 +24,6 @@ static inline bool qtest_enabled(void)
}
#ifndef CONFIG_USER_ONLY
-void qtest_send_prefix(CharBackend *chr);
void G_GNUC_PRINTF(2, 3) qtest_sendf(CharBackend *chr, const char *fmt, ...);
void qtest_set_command_cb(bool (*pc_cb)(CharBackend *chr, gchar **words));
bool qtest_driver(void);
diff --git a/include/user/signal.h b/include/user/signal.h
new file mode 100644
index 0000000..7fa33b0
--- /dev/null
+++ b/include/user/signal.h
@@ -0,0 +1,25 @@
+/*
+ * Signal-related declarations.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef USER_SIGNAL_H
+#define USER_SIGNAL_H
+
+#ifndef CONFIG_USER_ONLY
+#error Cannot include this header from system emulation
+#endif
+
+/**
+ * target_to_host_signal:
+ * @sig: target signal.
+ *
+ * On success, return the host signal between 0 (inclusive) and NSIG
+ * (exclusive) corresponding to the target signal @sig. Return any other value
+ * on failure.
+ */
+int target_to_host_signal(int sig);
+
+extern int host_interrupt_signal;
+
+#endif
diff --git a/linux-user/main.c b/linux-user/main.c
index 7198fa0..5c74c52 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -1024,7 +1024,6 @@ int main(int argc, char **argv, char **envp)
if (gdbstub) {
gdbserver_start(gdbstub, &error_fatal);
- gdb_handlesig(cpu, 0, NULL, NULL, 0);
}
#ifdef CONFIG_SEMIHOSTING
diff --git a/linux-user/signal-common.h b/linux-user/signal-common.h
index 8584d9e..196d240 100644
--- a/linux-user/signal-common.h
+++ b/linux-user/signal-common.h
@@ -61,7 +61,6 @@ void queue_signal(CPUArchState *env, int sig, int si_type,
target_siginfo_t *info);
void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info);
void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo);
-int target_to_host_signal(int sig);
int host_to_target_signal(int sig);
long do_sigreturn(CPUArchState *env);
long do_rt_sigreturn(CPUArchState *env);
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 087c4d2..81a98c6 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -36,6 +36,7 @@
#include "user/cpu_loop.h"
#include "user/page-protection.h"
#include "user/safe-syscall.h"
+#include "user/signal.h"
#include "tcg/tcg.h"
/* target_siginfo_t must fit in gdbstub's siginfo save area. */
@@ -516,6 +517,8 @@ static int core_dump_signal(int sig)
}
}
+int host_interrupt_signal;
+
static void signal_table_init(const char *rtsig_map)
{
int hsig, tsig, count;
@@ -579,10 +582,10 @@ static void signal_table_init(const char *rtsig_map)
* Attempts for configure "missing" signals via sigaction will be
* silently ignored.
*
- * Reserve one signal for internal usage (see below).
+ * Reserve two signals for internal usage (see below).
*/
- hsig = SIGRTMIN + 1;
+ hsig = SIGRTMIN + 2;
for (tsig = TARGET_SIGRTMIN;
hsig <= SIGRTMAX && tsig <= TARGET_NSIG;
hsig++, tsig++) {
@@ -603,12 +606,17 @@ static void signal_table_init(const char *rtsig_map)
host_to_target_signal_table[SIGABRT] = 0;
for (hsig = SIGRTMIN; hsig <= SIGRTMAX; hsig++) {
if (!host_to_target_signal_table[hsig]) {
- host_to_target_signal_table[hsig] = TARGET_SIGABRT;
- break;
+ if (host_interrupt_signal) {
+ host_to_target_signal_table[hsig] = TARGET_SIGABRT;
+ break;
+ } else {
+ host_interrupt_signal = hsig;
+ }
}
}
if (hsig > SIGRTMAX) {
- fprintf(stderr, "No rt signals left for SIGABRT mapping\n");
+ fprintf(stderr,
+ "No rt signals left for interrupt and SIGABRT mapping\n");
exit(EXIT_FAILURE);
}
@@ -688,6 +696,8 @@ void signal_init(const char *rtsig_map)
}
sigact_table[tsig - 1]._sa_handler = thand;
}
+
+ sigaction(host_interrupt_signal, &act, NULL);
}
/* Force a synchronously taken signal. The kernel force_sig() function
@@ -1035,6 +1045,12 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
bool sync_sig = false;
void *sigmask;
+ if (host_sig == host_interrupt_signal) {
+ ts->signal_pending = 1;
+ cpu_exit(thread_cpu);
+ return;
+ }
+
/*
* Non-spoofed SIGSEGV and SIGBUS are synchronous, and need special
* handling wrt signal blocking and unwinding. Non-spoofed SIGILL,
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index df5ed18..02ea422 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -138,6 +138,7 @@
#include "user-mmap.h"
#include "user/page-protection.h"
#include "user/safe-syscall.h"
+#include "user/signal.h"
#include "qemu/guest-random.h"
#include "qemu/selfmap.h"
#include "user/syscall-trace.h"
diff --git a/stubs/meson.build b/stubs/meson.build
index a8b3aeb..b0fee37 100644
--- a/stubs/meson.build
+++ b/stubs/meson.build
@@ -61,6 +61,8 @@ if have_user
if not have_system
stub_ss.add(files('qdev.c'))
endif
+
+ stub_ss.add(files('monitor-fd.c'))
endif
if have_system
diff --git a/stubs/monitor-fd.c b/stubs/monitor-fd.c
new file mode 100644
index 0000000..9bb6749
--- /dev/null
+++ b/stubs/monitor-fd.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "qemu/osdep.h"
+#include "monitor/monitor.h"
+
+int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp)
+{
+ abort();
+}
diff --git a/system/qtest.c b/system/qtest.c
index e68ed0f..12152ef 100644
--- a/system/qtest.c
+++ b/system/qtest.c
@@ -265,7 +265,7 @@ static int hex2nib(char ch)
}
}
-void qtest_send_prefix(CharBackend *chr)
+static void qtest_log_timestamp(void)
{
if (!qtest_log_fp || !qtest_opened) {
return;
@@ -282,7 +282,7 @@ static void G_GNUC_PRINTF(1, 2) qtest_log_send(const char *fmt, ...)
return;
}
- qtest_send_prefix(NULL);
+ qtest_log_timestamp();
va_start(ap, fmt);
vfprintf(qtest_log_fp, fmt, ap);
@@ -301,6 +301,7 @@ static void qtest_server_char_be_send(void *opaque, const char *str)
static void qtest_send(CharBackend *chr, const char *str)
{
+ qtest_log_timestamp();
qtest_server_send(qtest_server_send_opaque, str);
}
@@ -324,7 +325,6 @@ static void qtest_irq_handler(void *opaque, int n, int level)
if (irq_levels[n] != level) {
CharBackend *chr = &qtest->qtest_chr;
irq_levels[n] = level;
- qtest_send_prefix(chr);
qtest_sendf(chr, "IRQ %s %d\n",
level ? "raise" : "lower", n);
}
@@ -380,19 +380,16 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
is_outbound = words[0][14] == 'o';
dev = DEVICE(object_resolve_path(words[1], NULL));
if (!dev) {
- qtest_send_prefix(chr);
qtest_send(chr, "FAIL Unknown device\n");
return;
}
if (is_named && !is_outbound) {
- qtest_send_prefix(chr);
qtest_send(chr, "FAIL Interception of named in-GPIOs not yet supported\n");
return;
}
if (irq_intercept_dev) {
- qtest_send_prefix(chr);
if (irq_intercept_dev != dev) {
qtest_send(chr, "FAIL IRQ intercept already enabled\n");
} else {
@@ -419,7 +416,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
}
}
- qtest_send_prefix(chr);
if (interception_succeeded) {
irq_intercept_dev = dev;
qtest_send(chr, "OK\n");
@@ -438,7 +434,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
dev = DEVICE(object_resolve_path(words[1], NULL));
if (!dev) {
- qtest_send_prefix(chr);
qtest_send(chr, "FAIL Unknown device\n");
return;
}
@@ -457,7 +452,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
irq = qdev_get_gpio_in_named(dev, name, num);
qemu_set_irq(irq, level);
- qtest_send_prefix(chr);
qtest_send(chr, "OK\n");
} else if (strcmp(words[0], "outb") == 0 ||
strcmp(words[0], "outw") == 0 ||
@@ -480,7 +474,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
} else if (words[0][3] == 'l') {
cpu_outl(addr, value);
}
- qtest_send_prefix(chr);
qtest_send(chr, "OK\n");
} else if (strcmp(words[0], "inb") == 0 ||
strcmp(words[0], "inw") == 0 ||
@@ -501,7 +494,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
} else if (words[0][2] == 'l') {
value = cpu_inl(addr);
}
- qtest_send_prefix(chr);
qtest_sendf(chr, "OK 0x%04x\n", value);
} else if (strcmp(words[0], "writeb") == 0 ||
strcmp(words[0], "writew") == 0 ||
@@ -537,7 +529,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
&data, 8);
}
- qtest_send_prefix(chr);
qtest_send(chr, "OK\n");
} else if (strcmp(words[0], "readb") == 0 ||
strcmp(words[0], "readw") == 0 ||
@@ -571,7 +562,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
&value, 8);
tswap64s(&value);
}
- qtest_send_prefix(chr);
qtest_sendf(chr, "OK 0x%016" PRIx64 "\n", value);
} else if (strcmp(words[0], "read") == 0) {
g_autoptr(GString) enc = NULL;
@@ -593,7 +583,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
enc = qemu_hexdump_line(NULL, data, len, 0, 0);
- qtest_send_prefix(chr);
qtest_sendf(chr, "OK 0x%s\n", enc->str);
g_free(data);
@@ -613,7 +602,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data,
len);
b64_data = g_base64_encode(data, len);
- qtest_send_prefix(chr);
qtest_sendf(chr, "OK %s\n", b64_data);
g_free(data);
@@ -649,7 +637,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
len);
g_free(data);
- qtest_send_prefix(chr);
qtest_send(chr, "OK\n");
} else if (strcmp(words[0], "memset") == 0) {
uint64_t addr, len;
@@ -673,7 +660,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
g_free(data);
}
- qtest_send_prefix(chr);
qtest_send(chr, "OK\n");
} else if (strcmp(words[0], "b64write") == 0) {
uint64_t addr, len;
@@ -705,10 +691,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data,
len);
- qtest_send_prefix(chr);
qtest_send(chr, "OK\n");
} else if (strcmp(words[0], "endianness") == 0) {
- qtest_send_prefix(chr);
if (target_words_bigendian()) {
qtest_sendf(chr, "OK big\n");
} else {
@@ -724,17 +708,24 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
} else {
ns = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
QEMU_TIMER_ATTR_ALL);
+ if (ns < 0) {
+ qtest_send(chr, "FAIL "
+ "cannot advance clock to the next deadline "
+ "because there is no pending deadline\n");
+ return;
+ }
}
new_ns = qemu_clock_advance_virtual_time(old_ns + ns);
- qtest_send_prefix(chr);
- qtest_sendf(chr, "%s %"PRIi64"\n",
- new_ns > old_ns ? "OK" : "FAIL", new_ns);
+ if (new_ns > old_ns) {
+ qtest_sendf(chr, "OK %"PRIi64"\n", new_ns);
+ } else {
+ qtest_sendf(chr, "FAIL could not advance time\n");
+ }
} else if (strcmp(words[0], "module_load") == 0) {
Error *local_err = NULL;
int rv;
g_assert(words[1] && words[2]);
- qtest_send_prefix(chr);
rv = module_load(words[1], words[2], &local_err);
if (rv > 0) {
qtest_sendf(chr, "OK\n");
@@ -752,36 +743,30 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
ret = qemu_strtoi64(words[1], NULL, 0, &ns);
g_assert(ret == 0);
new_ns = qemu_clock_advance_virtual_time(ns);
- qtest_send_prefix(chr);
qtest_sendf(chr, "%s %"PRIi64"\n",
new_ns == ns ? "OK" : "FAIL", new_ns);
} else if (process_command_cb && process_command_cb(chr, words)) {
/* Command got consumed by the callback handler */
} else {
- qtest_send_prefix(chr);
qtest_sendf(chr, "FAIL Unknown command '%s'\n", words[0]);
}
}
+/*
+ * Process as much of @inbuf as we can in newline terminated chunks.
+ * Remove the processed commands from @inbuf as we go.
+ */
static void qtest_process_inbuf(CharBackend *chr, GString *inbuf)
{
char *end;
while ((end = strchr(inbuf->str, '\n')) != NULL) {
- size_t offset;
- GString *cmd;
- gchar **words;
-
- offset = end - inbuf->str;
+ size_t len = end - inbuf->str;
+ g_autofree char *cmd = g_strndup(inbuf->str, len);
+ g_auto(GStrv) words = g_strsplit(cmd, " ", 0);
- cmd = g_string_new_len(inbuf->str, offset);
- g_string_erase(inbuf, 0, offset + 1);
-
- words = g_strsplit(cmd->str, " ", 0);
+ g_string_erase(inbuf, 0, len + 1);
qtest_process_command(chr, words);
- g_strfreev(words);
-
- g_string_free(cmd, TRUE);
}
}
diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include
index fead7d3..fa1cbb6 100644
--- a/tests/docker/Makefile.include
+++ b/tests/docker/Makefile.include
@@ -236,3 +236,6 @@ docker-image: ${DOCKER_IMAGES:%=docker-image-%}
docker-clean:
$(call quiet-command, $(DOCKER_SCRIPT) clean)
+
+# Overrides
+docker-test-rust%: NETWORK=1
diff --git a/tests/docker/test-rust b/tests/docker/test-rust
new file mode 100755
index 0000000..e7e3e94
--- /dev/null
+++ b/tests/docker/test-rust
@@ -0,0 +1,21 @@
+#!/bin/bash -e
+#
+# Run the rust code checks (a.k.a. check-rust-tools-nightly)
+#
+# Copyright (c) 2025 Linaro Ltd
+#
+# Authors:
+# Alex Bennée <alex.bennee@linaro.org>
+#
+# This work is licensed under the terms of the GNU GPL, version 2
+# or (at your option) any later version. See the COPYING file in
+# the top-level directory.
+
+. common.rc
+
+cd "$BUILD_DIR"
+
+configure_qemu --disable-user --disable-docs --enable-rust
+pyvenv/bin/meson devenv -w $QEMU_SRC/rust ${CARGO-cargo} fmt --check
+make clippy
+make rustdoc
diff --git a/tests/guest-debug/run-test.py b/tests/guest-debug/run-test.py
index 5a091db..75e9c92 100755
--- a/tests/guest-debug/run-test.py
+++ b/tests/guest-debug/run-test.py
@@ -36,6 +36,8 @@ def get_args():
parser.add_argument("--gdb-args", help="Additional gdb arguments")
parser.add_argument("--output", help="A file to redirect output to")
parser.add_argument("--stderr", help="A file to redirect stderr to")
+ parser.add_argument("--no-suspend", action="store_true",
+ help="Ask the binary to not wait for GDB connection")
return parser.parse_args()
@@ -73,10 +75,19 @@ if __name__ == '__main__':
# Launch QEMU with binary
if "system" in args.qemu:
+ if args.no_suspend:
+ suspend = ''
+ else:
+ suspend = ' -S'
cmd = f'{args.qemu} {args.qargs} {args.binary}' \
- f' -S -gdb unix:path={socket_name},server=on'
+ f'{suspend} -gdb unix:path={socket_name},server=on'
else:
- cmd = f'{args.qemu} {args.qargs} -g {socket_name} {args.binary}'
+ if args.no_suspend:
+ suspend = ',suspend=n'
+ else:
+ suspend = ''
+ cmd = f'{args.qemu} {args.qargs} -g {socket_name}{suspend}' \
+ f' {args.binary}'
log(output, "QEMU CMD: %s" % (cmd))
inferior = subprocess.Popen(shlex.split(cmd))
diff --git a/tests/qtest/libqos/virtio-pci-modern.c b/tests/qtest/libqos/virtio-pci-modern.c
index 18d1188..4e67fcb 100644
--- a/tests/qtest/libqos/virtio-pci-modern.c
+++ b/tests/qtest/libqos/virtio-pci-modern.c
@@ -173,13 +173,11 @@ static bool get_config_isr_status(QVirtioDevice *d)
static void wait_config_isr_status(QVirtioDevice *d, gint64 timeout_us)
{
- QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
gint64 start_time = g_get_monotonic_time();
- do {
+ while (!get_config_isr_status(d)) {
g_assert(g_get_monotonic_time() - start_time <= timeout_us);
- qtest_clock_step(dev->pdev->bus->qts, 100);
- } while (!get_config_isr_status(d));
+ }
}
static void queue_select(QVirtioDevice *d, uint16_t index)
diff --git a/tests/qtest/libqos/virtio-pci.c b/tests/qtest/libqos/virtio-pci.c
index 485b8f6..002bf8b 100644
--- a/tests/qtest/libqos/virtio-pci.c
+++ b/tests/qtest/libqos/virtio-pci.c
@@ -171,13 +171,11 @@ static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d)
static void qvirtio_pci_wait_config_isr_status(QVirtioDevice *d,
gint64 timeout_us)
{
- QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
gint64 start_time = g_get_monotonic_time();
- do {
+ while (!qvirtio_pci_get_config_isr_status(d)) {
g_assert(g_get_monotonic_time() - start_time <= timeout_us);
- qtest_clock_step(dev->pdev->bus->qts, 100);
- } while (!qvirtio_pci_get_config_isr_status(d));
+ }
}
static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index)
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 5e062c7..68316db 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -405,6 +405,8 @@ foreach dir : target_dirs
target_base = dir.split('-')[0]
qtest_emulator = emulators['qemu-system-' + target_base]
target_qtests = get_variable('qtests_' + target_base, []) + qtests_generic
+ has_kvm = ('CONFIG_KVM' in config_all_accel and host_os == 'linux'
+ and cpu == target_base and fs.exists('/dev/kvm'))
test_deps = roms
qtest_env = environment()
@@ -438,11 +440,18 @@ foreach dir : target_dirs
test: executable(test, src, dependencies: deps)
}
endif
+
+ test_args = ['--tap', '-k']
+
+ if test == 'migration-test' and has_kvm
+ test_args += ['--full']
+ endif
+
test('qtest-@0@/@1@'.format(target_base, test),
qtest_executables[test],
depends: [test_deps, qtest_emulator, emulator_modules],
env: qtest_env,
- args: ['--tap', '-k'],
+ args: test_args,
protocol: 'tap',
timeout: slow_qtests.get(test, 60),
priority: slow_qtests.get(test, 60),
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index 5cad506..0893687 100644
--- a/tests/qtest/migration-test.c
+++ b/tests/qtest/migration-test.c
@@ -14,13 +14,38 @@
#include "migration/framework.h"
#include "qemu/module.h"
+static void parse_args(int *argc_p, char ***argv_p, bool *full_set)
+{
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ int i, j;
+
+ j = 1;
+ for (i = 1; i < argc; i++) {
+ if (g_str_equal(argv[i], "--full")) {
+ *full_set = true;
+ continue;
+ }
+ argv[j++] = argv[i];
+ if (i >= j) {
+ argv[i] = NULL;
+ }
+ }
+ *argc_p = j;
+}
+
int main(int argc, char **argv)
{
MigrationTestEnv *env;
int ret;
+ bool full_set = false;
+
+ /* strip the --full option if it's present */
+ parse_args(&argc, &argv, &full_set);
g_test_init(&argc, &argv, NULL);
env = migration_get_env();
+ env->full_set = full_set;
module_call_init(MODULE_INIT_QOM);
migration_test_add_tls(env);
diff --git a/tests/qtest/migration/compression-tests.c b/tests/qtest/migration/compression-tests.c
index d78f1f1..8b58401 100644
--- a/tests/qtest/migration/compression-tests.c
+++ b/tests/qtest/migration/compression-tests.c
@@ -151,10 +151,22 @@ static void test_multifd_tcp_zlib(void)
test_precopy_common(&args);
}
+static void migration_test_add_compression_smoke(MigrationTestEnv *env)
+{
+ migration_test_add("/migration/multifd/tcp/plain/zlib",
+ test_multifd_tcp_zlib);
+}
+
void migration_test_add_compression(MigrationTestEnv *env)
{
tmpfs = env->tmpfs;
+ migration_test_add_compression_smoke(env);
+
+ if (!env->full_set) {
+ return;
+ }
+
#ifdef CONFIG_ZSTD
migration_test_add("/migration/multifd/tcp/plain/zstd",
test_multifd_tcp_zstd);
@@ -179,7 +191,4 @@ void migration_test_add_compression(MigrationTestEnv *env)
migration_test_add("/migration/precopy/unix/xbzrle",
test_precopy_unix_xbzrle);
}
-
- migration_test_add("/migration/multifd/tcp/plain/zlib",
- test_multifd_tcp_zlib);
}
diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c
index 215b0df..4758841 100644
--- a/tests/qtest/migration/cpr-tests.c
+++ b/tests/qtest/migration/cpr-tests.c
@@ -104,6 +104,12 @@ void migration_test_add_cpr(MigrationTestEnv *env)
{
tmpfs = env->tmpfs;
+ /* no tests in the smoke set for now */
+
+ if (!env->full_set) {
+ return;
+ }
+
/*
* Our CI system has problems with shared memory.
* Don't run this test until we find a workaround.
diff --git a/tests/qtest/migration/file-tests.c b/tests/qtest/migration/file-tests.c
index 6400ddc..f260e28 100644
--- a/tests/qtest/migration/file-tests.c
+++ b/tests/qtest/migration/file-tests.c
@@ -300,12 +300,24 @@ static void test_multifd_file_mapped_ram_fdset_dio(void)
}
#endif /* !_WIN32 */
+static void migration_test_add_file_smoke(MigrationTestEnv *env)
+{
+ migration_test_add("/migration/precopy/file",
+ test_precopy_file);
+
+ migration_test_add("/migration/multifd/file/mapped-ram/dio",
+ test_multifd_file_mapped_ram_dio);
+}
+
void migration_test_add_file(MigrationTestEnv *env)
{
tmpfs = env->tmpfs;
- migration_test_add("/migration/precopy/file",
- test_precopy_file);
+ migration_test_add_file_smoke(env);
+
+ if (!env->full_set) {
+ return;
+ }
migration_test_add("/migration/precopy/file/offset",
test_precopy_file_offset);
@@ -326,9 +338,6 @@ void migration_test_add_file(MigrationTestEnv *env)
migration_test_add("/migration/multifd/file/mapped-ram/live",
test_multifd_file_mapped_ram_live);
- migration_test_add("/migration/multifd/file/mapped-ram/dio",
- test_multifd_file_mapped_ram_dio);
-
#ifndef _WIN32
migration_test_add("/migration/multifd/file/mapped-ram/fdset",
test_multifd_file_mapped_ram_fdset);
diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h
index cb4a984..e4a1187 100644
--- a/tests/qtest/migration/framework.h
+++ b/tests/qtest/migration/framework.h
@@ -24,6 +24,7 @@ typedef struct MigrationTestEnv {
bool uffd_feature_thread_id;
bool has_dirty_ring;
bool is_x86;
+ bool full_set;
const char *arch;
const char *qemu_src;
const char *qemu_dst;
diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c
index 04e5a47..2e612d9 100644
--- a/tests/qtest/migration/misc-tests.c
+++ b/tests/qtest/migration/misc-tests.c
@@ -258,14 +258,24 @@ static void test_validate_uri_channels_none_set(void)
do_test_validate_uri_channel(&args);
}
+static void migration_test_add_misc_smoke(MigrationTestEnv *env)
+{
+#ifndef _WIN32
+ migration_test_add("/migration/analyze-script", test_analyze_script);
+#endif
+}
+
void migration_test_add_misc(MigrationTestEnv *env)
{
tmpfs = env->tmpfs;
+ migration_test_add_misc_smoke(env);
+
+ if (!env->full_set) {
+ return;
+ }
+
migration_test_add("/migration/bad_dest", test_baddest);
-#ifndef _WIN32
- migration_test_add("/migration/analyze-script", test_analyze_script);
-#endif
/*
* Our CI system has problems with shared memory.
diff --git a/tests/qtest/migration/postcopy-tests.c b/tests/qtest/migration/postcopy-tests.c
index 59e8c12..982457b 100644
--- a/tests/qtest/migration/postcopy-tests.c
+++ b/tests/qtest/migration/postcopy-tests.c
@@ -79,7 +79,7 @@ static void test_postcopy_preempt_recovery(void)
test_postcopy_recovery_common(&args);
}
-void migration_test_add_postcopy(MigrationTestEnv *env)
+static void migration_test_add_postcopy_smoke(MigrationTestEnv *env)
{
if (env->has_uffd) {
migration_test_add("/migration/postcopy/plain", test_postcopy);
@@ -87,6 +87,18 @@ void migration_test_add_postcopy(MigrationTestEnv *env)
test_postcopy_recovery);
migration_test_add("/migration/postcopy/preempt/plain",
test_postcopy_preempt);
+ }
+}
+
+void migration_test_add_postcopy(MigrationTestEnv *env)
+{
+ migration_test_add_postcopy_smoke(env);
+
+ if (!env->full_set) {
+ return;
+ }
+
+ if (env->has_uffd) {
migration_test_add("/migration/postcopy/preempt/recovery/plain",
test_postcopy_preempt_recovery);
diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c
index 7d6d4f5..162fa69 100644
--- a/tests/qtest/migration/precopy-tests.c
+++ b/tests/qtest/migration/precopy-tests.c
@@ -951,10 +951,8 @@ static void test_dirty_limit(void)
migrate_end(from, to, true);
}
-void migration_test_add_precopy(MigrationTestEnv *env)
+static void migration_test_add_precopy_smoke(MigrationTestEnv *env)
{
- tmpfs = env->tmpfs;
-
if (env->is_x86) {
migration_test_add("/migration/precopy/unix/suspend/live",
test_precopy_unix_suspend_live);
@@ -966,6 +964,21 @@ void migration_test_add_precopy(MigrationTestEnv *env)
test_precopy_unix_plain);
migration_test_add("/migration/precopy/tcp/plain", test_precopy_tcp_plain);
+ migration_test_add("/migration/multifd/tcp/uri/plain/none",
+ test_multifd_tcp_uri_none);
+ migration_test_add("/migration/multifd/tcp/plain/cancel",
+ test_multifd_tcp_cancel);
+}
+
+void migration_test_add_precopy(MigrationTestEnv *env)
+{
+ tmpfs = env->tmpfs;
+
+ migration_test_add_precopy_smoke(env);
+
+ if (!env->full_set) {
+ return;
+ }
migration_test_add("/migration/precopy/tcp/plain/switchover-ack",
test_precopy_tcp_switchover_ack);
@@ -989,16 +1002,12 @@ void migration_test_add_precopy(MigrationTestEnv *env)
test_dirty_limit);
}
}
- migration_test_add("/migration/multifd/tcp/uri/plain/none",
- test_multifd_tcp_uri_none);
migration_test_add("/migration/multifd/tcp/channels/plain/none",
test_multifd_tcp_channels_none);
migration_test_add("/migration/multifd/tcp/plain/zero-page/legacy",
test_multifd_tcp_zero_page_legacy);
migration_test_add("/migration/multifd/tcp/plain/zero-page/none",
test_multifd_tcp_no_zero_page);
- migration_test_add("/migration/multifd/tcp/plain/cancel",
- test_multifd_tcp_cancel);
if (g_str_equal(env->arch, "x86_64")
&& env->has_kvm && env->has_dirty_ring) {
diff --git a/tests/qtest/migration/tls-tests.c b/tests/qtest/migration/tls-tests.c
index 5704a1f..2cb4a44 100644
--- a/tests/qtest/migration/tls-tests.c
+++ b/tests/qtest/migration/tls-tests.c
@@ -722,10 +722,22 @@ static void test_multifd_tcp_tls_x509_reject_anon_client(void)
}
#endif /* CONFIG_TASN1 */
+static void migration_test_add_tls_smoke(MigrationTestEnv *env)
+{
+ migration_test_add("/migration/precopy/tcp/tls/psk/match",
+ test_precopy_tcp_tls_psk_match);
+}
+
void migration_test_add_tls(MigrationTestEnv *env)
{
tmpfs = env->tmpfs;
+ migration_test_add_tls_smoke(env);
+
+ if (!env->full_set) {
+ return;
+ }
+
migration_test_add("/migration/precopy/unix/tls/psk",
test_precopy_unix_tls_psk);
@@ -751,8 +763,6 @@ void migration_test_add_tls(MigrationTestEnv *env)
test_precopy_unix_tls_x509_override_host);
#endif /* CONFIG_TASN1 */
- migration_test_add("/migration/precopy/tcp/tls/psk/match",
- test_precopy_tcp_tls_psk_match);
migration_test_add("/migration/precopy/tcp/tls/psk/mismatch",
test_precopy_tcp_tls_psk_mismatch);
#ifdef CONFIG_TASN1
diff --git a/tests/qtest/npcm7xx_timer-test.c b/tests/qtest/npcm7xx_timer-test.c
index 58f58c2..4371104 100644
--- a/tests/qtest/npcm7xx_timer-test.c
+++ b/tests/qtest/npcm7xx_timer-test.c
@@ -465,7 +465,6 @@ static void test_periodic_interrupt(gconstpointer test_data)
int i;
tim_reset(td);
- clock_step_next();
tim_write_ticr(td, count);
tim_write_tcsr(td, CEN | IE | MODE_PERIODIC | PRESCALE(ps));
diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target
index 18d3cf4..688a6be 100644
--- a/tests/tcg/multiarch/Makefile.target
+++ b/tests/tcg/multiarch/Makefile.target
@@ -130,6 +130,13 @@ run-gdbstub-follow-fork-mode-parent: follow-fork-mode
--bin $< --test $(MULTIARCH_SRC)/gdbstub/follow-fork-mode-parent.py, \
following parents on fork)
+run-gdbstub-late-attach: late-attach
+ $(call run-test, $@, env LATE_ATTACH_PY=1 $(GDB_SCRIPT) \
+ --gdb $(GDB) \
+ --qemu $(QEMU) --qargs "$(QEMU_OPTS)" --no-suspend \
+ --bin $< --test $(MULTIARCH_SRC)/gdbstub/late-attach.py, \
+ attaching to a running process)
+
else
run-gdbstub-%:
$(call skip-test, "gdbstub test $*", "need working gdb with $(patsubst -%,,$(TARGET_NAME)) support")
@@ -139,7 +146,7 @@ EXTRA_RUNS += run-gdbstub-sha1 run-gdbstub-qxfer-auxv-read \
run-gdbstub-registers run-gdbstub-prot-none \
run-gdbstub-catch-syscalls run-gdbstub-follow-fork-mode-child \
run-gdbstub-follow-fork-mode-parent \
- run-gdbstub-qxfer-siginfo-read
+ run-gdbstub-qxfer-siginfo-read run-gdbstub-late-attach
# ARM Compatible Semi Hosting Tests
#
diff --git a/tests/tcg/multiarch/gdbstub/late-attach.py b/tests/tcg/multiarch/gdbstub/late-attach.py
new file mode 100644
index 0000000..1d40efb
--- /dev/null
+++ b/tests/tcg/multiarch/gdbstub/late-attach.py
@@ -0,0 +1,28 @@
+"""Test attaching GDB to a running process.
+
+SPDX-License-Identifier: GPL-2.0-or-later
+"""
+from test_gdbstub import main, report
+
+
+def run_test():
+ """Run through the tests one by one"""
+ try:
+ phase = gdb.parse_and_eval("phase").string()
+ except gdb.error:
+ # Assume the guest did not reach main().
+ phase = "start"
+
+ if phase == "start":
+ gdb.execute("break sigwait")
+ gdb.execute("continue")
+ phase = gdb.parse_and_eval("phase").string()
+ report(phase == "sigwait", "{} == \"sigwait\"".format(phase))
+
+ gdb.execute("signal SIGUSR1")
+
+ exitcode = int(gdb.parse_and_eval("$_exitcode"))
+ report(exitcode == 0, "{} == 0".format(exitcode))
+
+
+main(run_test)
diff --git a/tests/tcg/multiarch/late-attach.c b/tests/tcg/multiarch/late-attach.c
new file mode 100644
index 0000000..20a3640
--- /dev/null
+++ b/tests/tcg/multiarch/late-attach.c
@@ -0,0 +1,41 @@
+/*
+ * Test attaching GDB to a running process.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include <assert.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static const char *phase = "start";
+
+int main(void)
+{
+ sigset_t set;
+ int sig;
+
+ assert(sigfillset(&set) == 0);
+ assert(sigprocmask(SIG_BLOCK, &set, NULL) == 0);
+
+ /* Let GDB know it can send SIGUSR1. */
+ phase = "sigwait";
+ if (getenv("LATE_ATTACH_PY")) {
+ assert(sigwait(&set, &sig) == 0);
+ if (sig != SIGUSR1) {
+ fprintf(stderr, "Unexpected signal %d\n", sig);
+ return EXIT_FAILURE;
+ }
+ }
+
+ /* Check that the guest does not see host_interrupt_signal. */
+ assert(sigpending(&set) == 0);
+ for (sig = 1; sig < NSIG; sig++) {
+ if (sigismember(&set, sig)) {
+ fprintf(stderr, "Unexpected signal %d\n", sig);
+ return EXIT_FAILURE;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/util/meson.build b/util/meson.build
index 5d8bef9..780b597 100644
--- a/util/meson.build
+++ b/util/meson.build
@@ -84,6 +84,8 @@ if have_block or have_ga
util_ss.add(files('qemu-coroutine.c', 'qemu-coroutine-lock.c', 'qemu-coroutine-io.c'))
util_ss.add(files(f'coroutine-@coroutine_backend@.c'))
util_ss.add(files('thread-pool.c', 'qemu-timer.c'))
+endif
+if have_block or have_ga or have_user
util_ss.add(files('qemu-sockets.c'))
endif
if have_block
diff --git a/util/oslib-posix.c b/util/oslib-posix.c
index 2bb34da..a697c60 100644
--- a/util/oslib-posix.c
+++ b/util/oslib-posix.c
@@ -111,6 +111,21 @@ int qemu_get_thread_id(void)
#endif
}
+int qemu_kill_thread(int tid, int sig)
+{
+#if defined(__linux__)
+ return syscall(__NR_tgkill, getpid(), tid, sig);
+#elif defined(__FreeBSD__)
+ return thr_kill2(getpid(), tid, sig);
+#elif defined(__NetBSD__)
+ return _lwp_kill(tid, sig);
+#elif defined(__OpenBSD__)
+ return thrkill(tid, sig, NULL);
+#else
+ return kill(tid, sig);
+#endif
+}
+
int qemu_daemon(int nochdir, int noclose)
{
return daemon(nochdir, noclose);
diff --git a/util/qemu-timer.c b/util/qemu-timer.c
index 0e8a453..3243d2c 100644
--- a/util/qemu-timer.c
+++ b/util/qemu-timer.c
@@ -675,17 +675,10 @@ int64_t qemu_clock_advance_virtual_time(int64_t dest)
{
int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
AioContext *aio_context;
- int64_t deadline;
-
aio_context = qemu_get_aio_context();
-
- deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
- QEMU_TIMER_ATTR_ALL);
- /*
- * A deadline of < 0 indicates this timer is not enabled, so we
- * won't get far trying to run it forward.
- */
- while (deadline >= 0 && clock < dest) {
+ while (clock < dest) {
+ int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
+ QEMU_TIMER_ATTR_ALL);
int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
qemu_virtual_clock_set_ns(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + warp);
@@ -693,9 +686,6 @@ int64_t qemu_clock_advance_virtual_time(int64_t dest)
qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]);
clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-
- deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
- QEMU_TIMER_ATTR_ALL);
}
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);