[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v5 6/8] gdbstub: Allow late attachment
From: |
Ilya Leoshkevich |
Subject: |
[PATCH v5 6/8] gdbstub: Allow late attachment |
Date: |
Fri, 10 Jan 2025 15:09:38 +0100 |
Allow debugging individual processes in multi-process applications by
starting them with export QEMU_GDB=/tmp/qemu-%d.sock,suspend=n.
Currently one would have to attach to every process to ensure the app
makes progress.
In case suspend=n is not specified, the flow remains unchanged. If it
is specified, then accepting the client connection is delegated to a
thread. In the future this machinery may be reused for handling
reconnections and interruptions.
On accepting a connection, the thread schedules gdb_handlesig() on the
first CPU and wakes it up with host_interrupt_signal. Note that the
result of this gdb_handlesig() invocation is handled, as opposed to
many other existing call sites. These other call sites probably need to
be fixed separately.
Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
---
bsd-user/main.c | 1 -
gdbstub/user.c | 122 ++++++++++++++++++++++++++++++++++++++++++----
linux-user/main.c | 1 -
3 files changed, 112 insertions(+), 12 deletions(-)
diff --git a/bsd-user/main.c b/bsd-user/main.c
index 0a5bc578365..478c2b65e7d 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);
- gdb_handlesig(cpu, 0, NULL, NULL, 0);
}
cpu_loop(env);
/* never exits */
diff --git a/gdbstub/user.c b/gdbstub/user.c
index 2c500eb1e23..c024a1f0019 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"
@@ -401,11 +402,104 @@ static int gdbserver_open_port(int port)
return fd;
}
-int gdbserver_start(const char *port_or_path)
+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;
+}
+
+__attribute__((__format__(__printf__, 1, 2)))
+static void print_usage(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+ fprintf(stderr, "Usage: -g {port|path}[,suspend={y|n}]\n");
+}
+
+int gdbserver_start(const char *args)
+{
+ 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);
+ Error *err = NULL;
+ if (g_strcmp0(tokens[0], "suspend") == 0) {
+ if (tokens[1] == NULL) {
+ print_usage("Missing \"suspend\" option value\n");
+ return -1;
+ } else if (!qapi_bool_parse(tokens[0], tokens[1],
+ &suspend, &err)) {
+ warn_report_err(err);
+ return -1;
+ }
+ } else {
+ if (port_or_path) {
+ print_usage("Unknown option: %s\n", *arg);
+ return -1;
+ }
+ port_or_path = *arg;
+ }
+ }
+ if (!port_or_path) {
+ print_usage("Port or path not specified\n");
+ return -1;
+ }
+
+ port = g_ascii_strtoull(port_or_path, NULL, 10);
if (port > 0) {
gdb_fd = gdbserver_open_port(port);
} else {
@@ -416,16 +510,24 @@ int gdbserver_start(const char *port_or_path)
return -1;
}
- if (port > 0 && gdb_accept_tcp(gdb_fd)) {
- return 0;
- } 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 0;
+ } else {
+ return -1;
+ }
+ } 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 0;
}
-
- /* gone wrong */
- close(gdb_fd);
- return -1;
}
void gdbserver_fork_start(void)
diff --git a/linux-user/main.c b/linux-user/main.c
index b97634a32dd..affec6863aa 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -1028,7 +1028,6 @@ int main(int argc, char **argv, char **envp)
gdbstub);
exit(EXIT_FAILURE);
}
- gdb_handlesig(cpu, 0, NULL, NULL, 0);
}
#ifdef CONFIG_SEMIHOSTING
--
2.47.1
- [PATCH v5 0/8] gdbstub: Allow late attachment, Ilya Leoshkevich, 2025/01/10
- [PATCH v5 2/8] gdbstub: Try unlinking the unix socket before binding, Ilya Leoshkevich, 2025/01/10
- [PATCH v5 1/8] gdbstub: Allow the %d placeholder in the socket path, Ilya Leoshkevich, 2025/01/10
- [PATCH v5 3/8] user: Introduce user/signal.h, Ilya Leoshkevich, 2025/01/10
- [PATCH v5 5/8] osdep: Introduce qemu_kill_thread(), Ilya Leoshkevich, 2025/01/10
- [PATCH v5 6/8] gdbstub: Allow late attachment,
Ilya Leoshkevich <=
- [PATCH v5 4/8] user: Introduce host_interrupt_signal, Ilya Leoshkevich, 2025/01/10
- [PATCH v5 8/8] tests/tcg: Add late gdbstub attach test, Ilya Leoshkevich, 2025/01/10
- [PATCH v5 7/8] docs/user: Document the %d placeholder and suspend=n QEMU_GDB features, Ilya Leoshkevich, 2025/01/10