Commit fcedd920 authored by Alex Bennée's avatar Alex Bennée
Browse files

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: default avatarAlex Bennée <alex.bennee@linaro.org>
Reviewed-by: default avatarRichard Henderson <richard.henderson@linaro.org>
Message-Id: <20200430190122.4592-7-alex.bennee@linaro.org>
parent e0a1e208
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -738,7 +738,7 @@ int main(int argc, char **argv)
    CPUState *cpu;
    int optind;
    const char *r;
    int gdbstub_port = 0;
    const char *gdbstub = NULL;
    char **target_environ, **wrk;
    envlist_t *envlist = NULL;
    char *trace_file = NULL;
@@ -814,7 +814,7 @@ int main(int argc, char **argv)
                exit(1);
            }
        } else if (!strcmp(r, "g")) {
            gdbstub_port = atoi(argv[optind++]);
            gdbstub = g_strdup(argv[optind++]);
        } else if (!strcmp(r, "r")) {
            qemu_uname_release = argv[optind++];
        } else if (!strcmp(r, "cpu")) {
@@ -1124,8 +1124,8 @@ int main(int argc, char **argv)
#error unsupported target CPU
#endif

    if (gdbstub_port) {
        gdbserver_start (gdbstub_port);
    if (gdbstub) {
        gdbserver_start(gdbstub);
        gdb_handlesig(cpu, 0);
    }
    cpu_loop(env);
+87 −16
Original line number Diff line number Diff line
@@ -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,22 +3187,36 @@ 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)) {

    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);
    return -1;
}
    return 0;
}

/* Disable gdb stub for child processes.  */
void gdbserver_fork(CPUState *cpu)
+9 −5
Original line number Diff line number Diff line
@@ -177,11 +177,15 @@ static inline uint8_t * gdb_get_reg_ptr(GByteArray *buf, int len)

#endif

#ifdef CONFIG_USER_ONLY
int gdbserver_start(int);
#else
int gdbserver_start(const char *port);
#endif
/**
 * gdbserver_start: start the gdb server
 * @port_or_device: connection spec for gdb
 *
 * 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);

+6 −6
Original line number Diff line number Diff line
@@ -51,7 +51,7 @@ char *exec_path;

int singlestep;
static const char *argv0;
static int gdbstub_port;
static const char *gdbstub;
static envlist_t *envlist;
static const char *cpu_model;
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)
{
    gdbstub_port = atoi(arg);
    gdbstub = g_strdup(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);

    if (gdbstub_port) {
        if (gdbserver_start(gdbstub_port) < 0) {
            fprintf(stderr, "qemu: could not open gdbserver on port %d\n",
                    gdbstub_port);
    if (gdbstub) {
        if (gdbserver_start(gdbstub) < 0) {
            fprintf(stderr, "qemu: could not open gdbserver on %s\n",
                    gdbstub);
            exit(EXIT_FAILURE);
        }
        gdb_handlesig(cpu, 0);