aboutsummaryrefslogtreecommitdiff
path: root/gdbstub.c
diff options
context:
space:
mode:
authorAlex Bennée <alex.bennee@linaro.org>2020-04-30 20:01:19 +0100
committerAlex Bennée <alex.bennee@linaro.org>2020-05-06 09:29:26 +0100
commitfcedd920867b8eb26ec803901a24db53a38882c5 (patch)
treec55c9dec8d2ff08e6448cfd9c8cf1fa0abacc758 /gdbstub.c
parente0a1e2084779fec1adc72866212bda3549fc4c22 (diff)
gdbstub/linux-user: support debugging over a unix socket
While debugging over TCP is fairly straightforward now we have test cases that want to orchestrate via make and currently a parallel build fails as two processes can't use the same listening port. While system emulation offers a wide cornucopia of connection methods thanks to the chardev abstraction we are a little more limited for linux user. Thankfully the programming API for a TCP socket and a local UNIX socket is pretty much the same once it's set up. Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20200430190122.4592-7-alex.bennee@linaro.org>
Diffstat (limited to 'gdbstub.c')
-rw-r--r--gdbstub.c103
1 files changed, 87 insertions, 16 deletions
diff --git a/gdbstub.c b/gdbstub.c
index b5381aa520..6950fd243f 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -355,6 +355,7 @@ typedef struct GDBState {
int signal;
#ifdef CONFIG_USER_ONLY
int fd;
+ char *socket_path;
int running_state;
#else
CharBackend chr;
@@ -2962,6 +2963,9 @@ void gdb_exit(CPUArchState *env, int code)
return;
}
#ifdef CONFIG_USER_ONLY
+ if (gdbserver_state.socket_path) {
+ unlink(gdbserver_state.socket_path);
+ }
if (gdbserver_state.fd < 0) {
return;
}
@@ -3066,7 +3070,66 @@ void gdb_signalled(CPUArchState *env, int sig)
put_packet(buf);
}
-static bool gdb_accept(int gdb_fd)
+static void gdb_accept_init(int fd)
+{
+ init_gdbserver_state();
+ create_default_process(&gdbserver_state);
+ gdbserver_state.processes[0].attached = true;
+ gdbserver_state.c_cpu = gdb_first_attached_cpu();
+ gdbserver_state.g_cpu = gdbserver_state.c_cpu;
+ gdbserver_state.fd = fd;
+ gdb_has_xml = false;
+}
+
+static bool gdb_accept_socket(int gdb_fd)
+{
+ int fd;
+
+ for(;;) {
+ fd = accept(gdb_fd, NULL, NULL);
+ if (fd < 0 && errno != EINTR) {
+ perror("accept socket");
+ return false;
+ } else if (fd >= 0) {
+ qemu_set_cloexec(fd);
+ break;
+ }
+ }
+
+ gdb_accept_init(fd);
+ return true;
+}
+
+static int gdbserver_open_socket(const char *path)
+{
+ struct sockaddr_un sockaddr;
+ int fd, ret;
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("create socket");
+ return -1;
+ }
+
+ 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;
+}
+
+static bool gdb_accept_tcp(int gdb_fd)
{
struct sockaddr_in sockaddr;
socklen_t len;
@@ -3091,17 +3154,11 @@ static bool gdb_accept(int gdb_fd)
return false;
}
- init_gdbserver_state();
- create_default_process(&gdbserver_state);
- gdbserver_state.processes[0].attached = true;
- gdbserver_state.c_cpu = gdb_first_attached_cpu();
- gdbserver_state.g_cpu = gdbserver_state.c_cpu;
- gdbserver_state.fd = fd;
- gdb_has_xml = false;
+ gdb_accept_init(fd);
return true;
}
-static int gdbserver_open(int port)
+static int gdbserver_open_port(int port)
{
struct sockaddr_in sockaddr;
int fd, ret;
@@ -3130,21 +3187,35 @@ static int gdbserver_open(int port)
close(fd);
return -1;
}
+
return fd;
}
-int gdbserver_start(int port)
+int gdbserver_start(const char *port_or_path)
{
- int gdb_fd = gdbserver_open(port);
+ int port = g_ascii_strtoull(port_or_path, NULL, 10);
+ int gdb_fd;
+
+ if (port > 0) {
+ gdb_fd = gdbserver_open_port(port);
+ } else {
+ gdb_fd = gdbserver_open_socket(port_or_path);
+ }
+
if (gdb_fd < 0) {
return -1;
}
- /* accept connections */
- if (!gdb_accept(gdb_fd)) {
- close(gdb_fd);
- return -1;
+
+ if (port > 0 && gdb_accept_tcp(gdb_fd)) {
+ return 0;
+ } else if (gdb_accept_socket(gdb_fd)) {
+ gdbserver_state.socket_path = g_strdup(port_or_path);
+ return 0;
}
- return 0;
+
+ /* gone wrong */
+ close(gdb_fd);
+ return -1;
}
/* Disable gdb stub for child processes. */