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:
parent
e0a1e20847
commit
fcedd92086
|
@ -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
101
gdbstub.c
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue