|
@@ -22,6 +22,7 @@
|
|
#include "gdbstub/user.h"
|
|
#include "gdbstub/user.h"
|
|
#include "gdbstub/enums.h"
|
|
#include "gdbstub/enums.h"
|
|
#include "hw/core/cpu.h"
|
|
#include "hw/core/cpu.h"
|
|
|
|
+#include "user/signal.h"
|
|
#include "trace.h"
|
|
#include "trace.h"
|
|
#include "internals.h"
|
|
#include "internals.h"
|
|
|
|
|
|
@@ -393,32 +394,122 @@ static int gdbserver_open_port(int port, Error **errp)
|
|
return fd;
|
|
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;
|
|
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) {
|
|
if (port > 0) {
|
|
gdb_fd = gdbserver_open_port(port, errp);
|
|
gdb_fd = gdbserver_open_port(port, errp);
|
|
} else {
|
|
} else {
|
|
gdb_fd = gdbserver_open_socket(port_or_path, errp);
|
|
gdb_fd = gdbserver_open_socket(port_or_path, errp);
|
|
}
|
|
}
|
|
-
|
|
|
|
if (gdb_fd < 0) {
|
|
if (gdb_fd < 0) {
|
|
return false;
|
|
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;
|
|
return true;
|
|
}
|
|
}
|
|
-
|
|
|
|
- /* gone wrong */
|
|
|
|
- close(gdb_fd);
|
|
|
|
- error_setg(errp, "gdbstub: failed to accept connection");
|
|
|
|
- return false;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void gdbserver_fork_start(void)
|
|
void gdbserver_fork_start(void)
|