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>
This commit is contained in:
Alex Bennée 2020-04-30 20:01:19 +01:00
parent e0a1e20847
commit fcedd92086
4 changed files with 106 additions and 31 deletions

View File

@ -738,7 +738,7 @@ int main(int argc, char **argv)
CPUState *cpu; CPUState *cpu;
int optind; int optind;
const char *r; const char *r;
int gdbstub_port = 0; const char *gdbstub = NULL;
char **target_environ, **wrk; char **target_environ, **wrk;
envlist_t *envlist = NULL; envlist_t *envlist = NULL;
char *trace_file = NULL; char *trace_file = NULL;
@ -814,7 +814,7 @@ int main(int argc, char **argv)
exit(1); exit(1);
} }
} else if (!strcmp(r, "g")) { } else if (!strcmp(r, "g")) {
gdbstub_port = atoi(argv[optind++]); gdbstub = g_strdup(argv[optind++]);
} else if (!strcmp(r, "r")) { } else if (!strcmp(r, "r")) {
qemu_uname_release = argv[optind++]; qemu_uname_release = argv[optind++];
} else if (!strcmp(r, "cpu")) { } else if (!strcmp(r, "cpu")) {
@ -1124,8 +1124,8 @@ int main(int argc, char **argv)
#error unsupported target CPU #error unsupported target CPU
#endif #endif
if (gdbstub_port) { if (gdbstub) {
gdbserver_start (gdbstub_port); gdbserver_start(gdbstub);
gdb_handlesig(cpu, 0); gdb_handlesig(cpu, 0);
} }
cpu_loop(env); cpu_loop(env);

101
gdbstub.c
View File

@ -355,6 +355,7 @@ typedef struct GDBState {
int signal; int signal;
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
int fd; int fd;
char *socket_path;
int running_state; int running_state;
#else #else
CharBackend chr; CharBackend chr;
@ -2962,6 +2963,9 @@ void gdb_exit(CPUArchState *env, int code)
return; return;
} }
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
if (gdbserver_state.socket_path) {
unlink(gdbserver_state.socket_path);
}
if (gdbserver_state.fd < 0) { if (gdbserver_state.fd < 0) {
return; return;
} }
@ -3066,7 +3070,66 @@ void gdb_signalled(CPUArchState *env, int sig)
put_packet(buf); 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; struct sockaddr_in sockaddr;
socklen_t len; socklen_t len;
@ -3091,17 +3154,11 @@ static bool gdb_accept(int gdb_fd)
return false; return false;
} }
init_gdbserver_state(); gdb_accept_init(fd);
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;
return true; return true;
} }
static int gdbserver_open(int port) static int gdbserver_open_port(int port)
{ {
struct sockaddr_in sockaddr; struct sockaddr_in sockaddr;
int fd, ret; int fd, ret;
@ -3130,22 +3187,36 @@ static int gdbserver_open(int port)
close(fd); close(fd);
return -1; return -1;
} }
return fd; 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) { if (gdb_fd < 0) {
return -1; return -1;
} }
/* accept connections */
if (!gdb_accept(gdb_fd)) { 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;
}
/* gone wrong */
close(gdb_fd); close(gdb_fd);
return -1; return -1;
} }
return 0;
}
/* Disable gdb stub for child processes. */ /* Disable gdb stub for child processes. */
void gdbserver_fork(CPUState *cpu) void gdbserver_fork(CPUState *cpu)

View File

@ -177,11 +177,15 @@ static inline uint8_t * gdb_get_reg_ptr(GByteArray *buf, int len)
#endif #endif
#ifdef CONFIG_USER_ONLY /**
int gdbserver_start(int); * gdbserver_start: start the gdb server
#else * @port_or_device: connection spec for gdb
int gdbserver_start(const char *port); *
#endif * For CONFIG_USER this is either a tcp port or a path to a fifo. For
* system emulation you can use a full chardev spec for your gdbserver
* port.
*/
int gdbserver_start(const char *port_or_device);
void gdbserver_cleanup(void); void gdbserver_cleanup(void);

View File

@ -51,7 +51,7 @@ char *exec_path;
int singlestep; int singlestep;
static const char *argv0; static const char *argv0;
static int gdbstub_port; static const char *gdbstub;
static envlist_t *envlist; static envlist_t *envlist;
static const char *cpu_model; static const char *cpu_model;
static const char *cpu_type; static const char *cpu_type;
@ -310,7 +310,7 @@ static void handle_arg_seed(const char *arg)
static void handle_arg_gdb(const char *arg) static void handle_arg_gdb(const char *arg)
{ {
gdbstub_port = atoi(arg); gdbstub = g_strdup(arg);
} }
static void handle_arg_uname(const char *arg) static void handle_arg_uname(const char *arg)
@ -861,10 +861,10 @@ int main(int argc, char **argv, char **envp)
target_cpu_copy_regs(env, regs); target_cpu_copy_regs(env, regs);
if (gdbstub_port) { if (gdbstub) {
if (gdbserver_start(gdbstub_port) < 0) { if (gdbserver_start(gdbstub) < 0) {
fprintf(stderr, "qemu: could not open gdbserver on port %d\n", fprintf(stderr, "qemu: could not open gdbserver on %s\n",
gdbstub_port); gdbstub);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
gdb_handlesig(cpu, 0); gdb_handlesig(cpu, 0);