mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-04 00:03:54 -06:00
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----- 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>
This commit is contained in:
commit
ffaf7f0376
40 changed files with 491 additions and 136 deletions
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
||||
/*
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
152
gdbstub/user.c
152
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);
|
||||
int gdb_fd;
|
||||
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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
25
include/user/signal.h
Normal file
25
include/user/signal.h
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
9
stubs/monitor-fd.c
Normal file
9
stubs/monitor-fd.c
Normal file
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
size_t len = end - inbuf->str;
|
||||
g_autofree char *cmd = g_strndup(inbuf->str, len);
|
||||
g_auto(GStrv) words = g_strsplit(cmd, " ", 0);
|
||||
|
||||
offset = end - inbuf->str;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -236,3 +236,6 @@ docker-image: ${DOCKER_IMAGES:%=docker-image-%}
|
|||
|
||||
docker-clean:
|
||||
$(call quiet-command, $(DOCKER_SCRIPT) clean)
|
||||
|
||||
# Overrides
|
||||
docker-test-rust%: NETWORK=1
|
||||
|
|
21
tests/docker/test-rust
Executable file
21
tests/docker/test-rust
Executable file
|
@ -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
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
#
|
||||
|
|
28
tests/tcg/multiarch/gdbstub/late-attach.py
Normal file
28
tests/tcg/multiarch/gdbstub/late-attach.py
Normal file
|
@ -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)
|
41
tests/tcg/multiarch/late-attach.c
Normal file
41
tests/tcg/multiarch/late-attach.c
Normal file
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue