From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1LyZJF-0007bj-LU for qemu-devel@nongnu.org; Mon, 27 Apr 2009 18:28:21 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1LyZJA-0007aO-CW for qemu-devel@nongnu.org; Mon, 27 Apr 2009 18:28:20 -0400 Received: from [199.232.76.173] (port=60900 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1LyZJA-0007aL-5z for qemu-devel@nongnu.org; Mon, 27 Apr 2009 18:28:16 -0400 Received: from imag.imag.fr ([129.88.30.1]:38727) by monty-python.gnu.org with esmtps (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1LyZJ9-0001iq-Gg for qemu-devel@nongnu.org; Mon, 27 Apr 2009 18:28:16 -0400 Date: Tue, 28 Apr 2009 00:22:09 +0200 From: Philippe Waille Message-ID: <20090427222209.GA7456@otto.imag.fr> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Subject: [Qemu-devel] [PATCH] gdbstub user mode : canonical -gdb dev option + unix sockets List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Extend -gdb dev system mode canonical configuration to user mode. Allow tcp sockets (existing code) and unix sockets (addition). Sign-off-by: Philippe Waille --- gdbstub.c | 307 +++++++++++++++++++++++++++++++++++++++++------------ gdbstub.h | 5 +- linux-user/main.c | 24 +++-- 3 files changed, 253 insertions(+), 83 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 3c34741..6b788d5 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -19,22 +19,14 @@ */ #include "config.h" #include "qemu-common.h" +#include "gdbstub.h" #ifdef CONFIG_USER_ONLY -#include -#include -#include -#include -#include -#include -#include - #include "qemu.h" -#else +#else /* !CONFIG_USER_ONLY */ #include "monitor.h" #include "qemu-char.h" #include "sysemu.h" -#include "gdbstub.h" -#endif +#endif /* !CONFIG_USER_ONLY */ #define MAX_PACKET_LENGTH 4096 @@ -305,8 +297,16 @@ static GDBState *gdbserver_state; static int gdb_has_xml; #ifdef CONFIG_USER_ONLY +typedef int (*gdbstub_func)(void); + /* XXX: This is not thread safe. Do we care? */ static int gdbserver_fd = -1; +static gdbstub_func gdbserver_do_accept = NULL; + +static int gdb_disconnected(GDBState *s) +{ + return (gdbserver_fd < 0) || (s->fd < 0); +} static int get_char(GDBState *s) { @@ -332,6 +332,7 @@ static int get_char(GDBState *s) } #endif + static gdb_syscall_complete_cb gdb_current_syscall_cb; static enum { @@ -2098,10 +2099,11 @@ gdb_queuesig (void) s = gdbserver_state; - if (gdbserver_fd < 0 || s->fd < 0) + if (gdb_disconnected(s)) { return 0; - else + } else { return 1; + } } int @@ -2112,8 +2114,9 @@ gdb_handlesig (CPUState *env, int sig) int n; s = gdbserver_state; - if (gdbserver_fd < 0 || s->fd < 0) + if (gdb_disconnected(s)) { return sig; + } /* disable single step if it was enabled */ cpu_single_step(env, 0); @@ -2126,8 +2129,9 @@ gdb_handlesig (CPUState *env, int sig) } /* put_packet() might have detected that the peer terminated the connection. */ - if (s->fd < 0) + if (gdb_disconnected(s)) { return sig; + } sig = 0; s->state = RS_IDLE; @@ -2160,8 +2164,9 @@ void gdb_exit(CPUState *env, int code) char buf[4]; s = gdbserver_state; - if (gdbserver_fd < 0 || s->fd < 0) + if (gdb_disconnected(s)) { return; + } snprintf(buf, sizeof(buf), "W%02x", code); put_packet(s, buf); @@ -2174,84 +2179,245 @@ void gdb_signalled(CPUState *env, int sig) char buf[4]; s = gdbserver_state; - if (gdbserver_fd < 0 || s->fd < 0) + if (gdb_disconnected(s)) { return; + } snprintf(buf, sizeof(buf), "X%02x", target_signal_to_gdb (sig)); put_packet(s, buf); } -static void gdb_accept(void) +static GDBState *gdbserver_create_gdbstate(void) + { + GDBState *s; + + s = qemu_mallocz(sizeof(GDBState)); + s->c_cpu = first_cpu; + s->g_cpu = first_cpu; + gdb_has_xml = 0; + gdbserver_state = s; + return s; +} + +static int gdbserver_tcp_accept(void) { + int fd,res; GDBState *s; - struct sockaddr_in sockaddr; - socklen_t len; - int val, fd; - for(;;) { - len = sizeof(sockaddr); - fd = accept(gdbserver_fd, (struct sockaddr *)&sockaddr, &len); - if (fd < 0 && errno != EINTR) { - perror("accept"); - return; - } else if (fd >= 0) { - break; + for (;;) { + fd = accept(gdbserver_fd, + (struct sockaddr *) NULL, (socklen_t *) NULL); + if (fd < 0) { + if (errno != EINTR) { + perror("accept"); + return -1; + } + } else { + break; } } - /* set short latency */ - val = 1; - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); + res = 1; + res = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&res, sizeof (res)); + if (res < 0) { + fprintf(stderr, "gdbserver ERROR : set nodelay\n"); + return -1; + } + res = fcntl(fd, F_SETFL, O_NONBLOCK); + if (res < 0) { + fprintf(stderr, "gdbserver ERROR : set nonblock\n"); + return -1; + } + s = gdbserver_create_gdbstate(); + s -> fd = fd; + return 0; +} - s = qemu_mallocz(sizeof(GDBState)); - s->c_cpu = first_cpu; - s->g_cpu = first_cpu; - s->fd = fd; - gdb_has_xml = 0; +static int gdbserver_tcp_start(const char *host, const char *port, + gdbstub_func faccept) +{ + struct addrinfo info_in, *info_out; + struct sockaddr_in addrin, *addr; + int res; + + memset(&info_in, 0, sizeof(struct addrinfo)); + info_in.ai_flags=AI_PASSIVE; /* force INADDR_ANY if host == NULL */ + info_in.ai_family= AF_INET; + info_in.ai_socktype=SOCK_STREAM; + info_in.ai_protocol=IPPROTO_TCP; + + if (getaddrinfo(host, port, &info_in, &info_out) < 0) { + perror("hostname/portname name conversion error"); + return -1; + } + addr = (struct sockaddr_in *) info_out -> ai_addr; + gdbserver_fd = socket(info_in.ai_family, info_in.ai_socktype, + info_in.ai_protocol); + if (info_in.ai_protocol < 0) { + perror("gdb socket"); + return -1; + } + /* allow fast reuse */ + res = 1; + res = setsockopt(gdbserver_fd, SOL_SOCKET, SO_REUSEADDR, + (char *) &res, sizeof(res)); + if (res <0) { + perror("gdb socket : set fast reuse\n"); + return -1; + } + res = bind(gdbserver_fd, (struct sockaddr *) addr, sizeof (addrin)); + if (res <0) { + perror("gdb socket bind\n"); + return -1; + } + res = listen(gdbserver_fd,0); + if (res <0) { + perror("listen\n"); + return -1; + } + gdbserver_do_accept = faccept; + return 0; +} + +#ifndef _WIN32 +static int gdbserver_unix_unlink(void) +{ + struct sockaddr_un sock; + socklen_t len; + int res; + len = sizeof(sock.sun_path); + res = getsockname(gdbserver_fd, &sock, &len); + if (res < 0) { + fprintf(stderr,"gdb accept/unlink : cannot get socket name\n"); + return -1; + } + res = unlink(sock.sun_path); + if (res < 0) { + fprintf(stderr,"gdb accept/unlink : cannot unlink %s\n",sock.sun_path); + return -1; + } + return 0; +} - gdbserver_state = s; +static int gdbserver_unix_accept(void) +{ + int fd,res; + GDBState *s; + for (;;) { + fd = accept(gdbserver_fd, + (struct sockaddr *) NULL, (socklen_t *) NULL); + if (fd < 0) { + if (errno != EINTR) { + perror("accept"); + return -1; + } + } else { + break; + } + } + res = fcntl(fd, F_SETFL, O_NONBLOCK); + if (res < 0) { + fprintf (stderr, "gdbserver ERROR : set nonblock\n"); + return -1; + } + s = gdbserver_create_gdbstate(); + s -> fd = fd; + return 0; +} - fcntl(fd, F_SETFL, O_NONBLOCK); +static int gdbserver_unix_accept_unlink(void) +{ + return (gdbserver_unix_accept() || gdbserver_unix_unlink()); } -static int gdbserver_open(int port) +static int gdbserver_unix_start(const char *filename, gdbstub_func faccept) { - struct sockaddr_in sockaddr; - int fd, val, ret; + struct sockaddr_un addr; + int res; - fd = socket(PF_INET, SOCK_STREAM, 0); - if (fd < 0) { - perror("socket"); - return -1; + if (strlen(filename) == 0) { + fprintf(stderr, "gdbserver ERROR : missing unix socket name\n"); + return -1; } - /* allow fast reuse */ - val = 1; - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)); + memset(&addr, 0, sizeof(struct sockaddr_un)); + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, filename); - sockaddr.sin_family = AF_INET; - sockaddr.sin_port = htons(port); - sockaddr.sin_addr.s_addr = 0; - ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); - if (ret < 0) { - perror("bind"); - return -1; + gdbserver_fd = socket(AF_UNIX,SOCK_STREAM,0); + if (gdbserver_fd < 0) { + perror("gsbserver socket"); + return -1; } - ret = listen(fd, 0); - if (ret < 0) { - perror("listen"); + res = bind (gdbserver_fd, (struct sockaddr *) &addr, + sizeof(struct sockaddr_un)); + if (res < 0) { + perror("gdbserver bind\n"); + } + res = listen (gdbserver_fd,0); + if (res <0) { + perror("listen\n"); return -1; } - return fd; + gdbserver_do_accept = faccept; + return 0; +} +#endif /* _WIN32 */ + +void gdbserver_accept(CPUState *env) +{ + if ((gdbserver_do_accept != NULL) && ((*gdbserver_do_accept)() >= 0)) { + gdb_handlesig(env, 0); + } } -int gdbserver_start(int port) +int gdbserver_start(const char *device) { - gdbserver_fd = gdbserver_open(port); - if (gdbserver_fd < 0) - return -1; - /* accept connections */ - gdb_accept(); + const char *p; + char *name, *devicename,*opt; + + name = malloc(strlen(device)+1); + /* Light memory leak : will never be freed */ + + if (strstart(device, "tcp:",&p)) { + if ((p == NULL) || (strlen(p) == 0)) { + perror("tcp socket syntax error"); + return -1; + } + strcpy(name,p); + devicename = strrchr(name,':'); + if (devicename == NULL) { + return -1; + } + *devicename++ = 0; + if (*devicename == 0) { + return -1; + } + if (*name == 0) { + name = NULL; + } + return gdbserver_tcp_start(name,devicename, + gdbserver_tcp_accept); +#ifndef _WIN32 + } else if (strstart (device, "unix:",&p)) { + strcpy (name,p); + opt = strrchr(name,','); + if (opt == NULL) { + return gdbserver_unix_start(name,gdbserver_unix_accept); + } else { + *opt++ = 0; + if (!strcmp(opt,"unlink")) { + return gdbserver_unix_start(name, + gdbserver_unix_accept_unlink); + } else { + fprintf(stderr,"gdb unix socket : unknown %s option\n",opt); + return -1; + } + } +#endif /* _WIN32 */ + } else { + return -1; + } return 0; } @@ -2259,14 +2425,15 @@ int gdbserver_start(int port) void gdbserver_fork(CPUState *env) { GDBState *s = gdbserver_state; - if (gdbserver_fd < 0 || s->fd < 0) - return; + if (gdb_disconnected(s)) { + return; + } close(s->fd); s->fd = -1; cpu_breakpoint_remove_all(env, BP_GDB); cpu_watchpoint_remove_all(env, BP_GDB); } -#else +#else /* !CONFIG_USER_ONLY */ static int gdb_chr_can_receive(void *opaque) { /* We can handle an arbitrarily large amount of data. @@ -2390,4 +2557,4 @@ int gdbserver_start(const char *device) return 0; } -#endif +#endif /* !CONFIG_USER_ONLY */ diff --git a/gdbstub.h b/gdbstub.h index 5740041..513965c 100644 --- a/gdbstub.h +++ b/gdbstub.h @@ -21,11 +21,10 @@ int gdb_queuesig (void); int gdb_handlesig (CPUState *, int); void gdb_exit(CPUState *, int); void gdb_signalled(CPUState *, int); -int gdbserver_start(int); void gdbserver_fork(CPUState *); -#else -int gdbserver_start(const char *port); +void gdbserver_accept(CPUState *); #endif +int gdbserver_start(const char *device); /* Get or set a register. Returns the size of the register. */ typedef int (*gdb_reg_cb)(CPUState *env, uint8_t *buf, int reg); void gdb_register_coprocessor(CPUState *env, diff --git a/linux-user/main.c b/linux-user/main.c index dc39b05..9e54f62 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -2220,7 +2220,12 @@ static void usage(void) "\n" "Standard options:\n" "-h print this help\n" - "-g port wait gdb connection to port\n" + "-gdb device wait gdb connection to device\n" + " device syntax use gdb target remote command \n" + " tcp:host:port remote host:port\n" + " tcp::port remote :port\n" + " unix:sockname[,unlink] remote | socat stdio unix:sockname\n" + " unlink : removes sockname after connection\n" "-L path set the elf interpreter prefix (default=%s)\n" "-s size set the stack size in bytes (default=%ld)\n" "-cpu model select CPU (-cpu ? for list)\n" @@ -2277,7 +2282,6 @@ int main(int argc, char **argv, char **envp) CPUState *env; int optind; const char *r; - int gdbstub_port = 0; char **target_environ, **wrk; char **target_argv; int target_argc; @@ -2365,10 +2369,13 @@ int main(int argc, char **argv, char **envp) fprintf(stderr, "page size must be a power of two\n"); exit(1); } - } else if (!strcmp(r, "g")) { - if (optind >= argc) - break; - gdbstub_port = atoi(argv[optind++]); + } else if (!strcmp(r, "gdb")) { + if (optind >= argc) { + usage (); + } + if (gdbserver_start(argv[optind++]) < 0) { + usage (); + } } else if (!strcmp(r, "r")) { qemu_uname_release = argv[optind++]; } else if (!strcmp(r, "cpu")) { @@ -2754,10 +2761,7 @@ int main(int argc, char **argv, char **envp) ts->heap_limit = 0; #endif - if (gdbstub_port) { - gdbserver_start (gdbstub_port); - gdb_handlesig(env, 0); - } + gdbserver_accept (env); cpu_loop(env); /* never exits */ return 0; -- 1.6.2.1 -- ----------------------------------------------------------------------------- Philippe WAILLE email : Philippe.Waille@imag.fr IMAG ID (Informatique et distribution) Tel : 04 76 61 20 13 ENSIMAG - antenne de Montbonnot Foreign : 33 4 76 61 20 13 INOVALLEE Fax : 04 76 61 20 99 51, avenue Jean Kuntzmann 38330 MONTBONNOT SAINT MARTIN