提交 858693c6 编写于 作者: B bellard

moved gdbstub to qemu - new asynchronous gdbstub


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@686 c046a42c-6fe2-441c-8c8c-71466251a162
上级 9b14bb04
...@@ -166,7 +166,7 @@ SRCS:= $(OBJS:.o=.c) ...@@ -166,7 +166,7 @@ SRCS:= $(OBJS:.o=.c)
OBJS+= libqemu.a OBJS+= libqemu.a
# cpu emulator library # cpu emulator library
LIBOBJS=exec.o translate-all.o cpu-exec.o gdbstub.o \ LIBOBJS=exec.o translate-all.o cpu-exec.o\
translate.o op.o translate.o op.o
ifeq ($(TARGET_ARCH), i386) ifeq ($(TARGET_ARCH), i386)
...@@ -219,7 +219,7 @@ ifeq ($(ARCH),alpha) ...@@ -219,7 +219,7 @@ ifeq ($(ARCH),alpha)
endif endif
# must use static linking to avoid leaving stuff in virtual address space # must use static linking to avoid leaving stuff in virtual address space
VL_OBJS=vl.o osdep.o block.o monitor.o \ VL_OBJS=vl.o osdep.o block.o monitor.o gdbstub.o \
ide.o ne2000.o pckbd.o vga.o sb16.o dma.o oss.o \ ide.o ne2000.o pckbd.o vga.o sb16.o dma.o oss.o \
fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o
ifeq ($(TARGET_ARCH), ppc) ifeq ($(TARGET_ARCH), ppc)
......
...@@ -611,8 +611,6 @@ void page_unprotect_range(uint8_t *data, unsigned long data_size); ...@@ -611,8 +611,6 @@ void page_unprotect_range(uint8_t *data, unsigned long data_size);
#endif /* SINGLE_CPU_DEFINES */ #endif /* SINGLE_CPU_DEFINES */
#define DEFAULT_GDBSTUB_PORT 1234
void cpu_abort(CPUState *env, const char *fmt, ...); void cpu_abort(CPUState *env, const char *fmt, ...);
extern CPUState *cpu_single_env; extern CPUState *cpu_single_env;
extern int code_copy_enabled; extern int code_copy_enabled;
...@@ -722,9 +720,4 @@ static inline void cpu_physical_memory_set_dirty(target_ulong addr) ...@@ -722,9 +720,4 @@ static inline void cpu_physical_memory_set_dirty(target_ulong addr)
void cpu_physical_memory_reset_dirty(target_ulong start, target_ulong end); void cpu_physical_memory_reset_dirty(target_ulong start, target_ulong end);
/* gdb stub API */
extern int gdbstub_fd;
CPUState *cpu_gdbstub_get_env(void *opaque);
int cpu_gdbstub(void *opaque, int (*main_loop)(void *opaque), int port);
#endif /* CPU_ALL_H */ #endif /* CPU_ALL_H */
...@@ -26,70 +26,36 @@ ...@@ -26,70 +26,36 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <signal.h> #include <signal.h>
#include <fcntl.h>
#include "cpu.h" #include "vl.h"
#include "exec-all.h"
//#define DEBUG_GDB //#define DEBUG_GDB
int gdbstub_fd = -1; enum RSState {
RS_IDLE,
RS_GETLINE,
RS_CHKSUM1,
RS_CHKSUM2,
};
/* return 0 if OK */ static int gdbserver_fd;
static int gdbstub_open(int port)
{
struct sockaddr_in sockaddr;
socklen_t len;
int fd, val, ret;
fd = socket(PF_INET, SOCK_STREAM, 0); typedef struct GDBState {
if (fd < 0) { enum RSState state;
perror("socket"); int fd;
return -1; char line_buf[4096];
} int line_buf_index;
int line_csum;
} GDBState;
/* allow fast reuse */ static int get_char(GDBState *s)
val = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
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;
}
ret = listen(fd, 0);
if (ret < 0) {
perror("listen");
return -1;
}
/* now wait for one connection */
for(;;) {
len = sizeof(sockaddr);
gdbstub_fd = accept(fd, (struct sockaddr *)&sockaddr, &len);
if (gdbstub_fd < 0 && errno != EINTR) {
perror("accept");
return -1;
} else if (gdbstub_fd >= 0) {
break;
}
}
/* set short latency */
val = 1;
setsockopt(gdbstub_fd, SOL_TCP, TCP_NODELAY, &val, sizeof(val));
return 0;
}
static int get_char(void)
{ {
uint8_t ch; uint8_t ch;
int ret; int ret;
for(;;) { for(;;) {
ret = read(gdbstub_fd, &ch, 1); ret = read(s->fd, &ch, 1);
if (ret < 0) { if (ret < 0) {
if (errno != EINTR && errno != EAGAIN) if (errno != EINTR && errno != EAGAIN)
return -1; return -1;
...@@ -102,12 +68,12 @@ static int get_char(void) ...@@ -102,12 +68,12 @@ static int get_char(void)
return ch; return ch;
} }
static void put_buffer(const uint8_t *buf, int len) static void put_buffer(GDBState *s, const uint8_t *buf, int len)
{ {
int ret; int ret;
while (len > 0) { while (len > 0) {
ret = write(gdbstub_fd, buf, len); ret = write(s->fd, buf, len);
if (ret < 0) { if (ret < 0) {
if (errno != EINTR && errno != EAGAIN) if (errno != EINTR && errno != EAGAIN)
return; return;
...@@ -161,59 +127,8 @@ static void hextomem(uint8_t *mem, const char *buf, int len) ...@@ -161,59 +127,8 @@ static void hextomem(uint8_t *mem, const char *buf, int len)
} }
} }
/* return -1 if error or EOF */
static int get_packet(char *buf, int buf_size)
{
int ch, len, csum, csum1;
char reply[1];
for(;;) {
for(;;) {
ch = get_char();
if (ch < 0)
return -1;
if (ch == '$')
break;
}
len = 0;
csum = 0;
for(;;) {
ch = get_char();
if (ch < 0)
return -1;
if (ch == '#')
break;
if (len > buf_size - 1)
return -1;
buf[len++] = ch;
csum += ch;
}
buf[len] = '\0';
ch = get_char();
if (ch < 0)
return -1;
csum1 = fromhex(ch) << 4;
ch = get_char();
if (ch < 0)
return -1;
csum1 |= fromhex(ch);
if ((csum & 0xff) != csum1) {
reply[0] = '-';
put_buffer(reply, 1);
} else {
reply[0] = '+';
put_buffer(reply, 1);
break;
}
}
#ifdef DEBUG_GDB
printf("command='%s'\n", buf);
#endif
return len;
}
/* return -1 if error, 0 if OK */ /* return -1 if error, 0 if OK */
static int put_packet(char *buf) static int put_packet(GDBState *s, char *buf)
{ {
char buf1[3]; char buf1[3];
int len, csum, ch, i; int len, csum, ch, i;
...@@ -224,9 +139,9 @@ static int put_packet(char *buf) ...@@ -224,9 +139,9 @@ static int put_packet(char *buf)
for(;;) { for(;;) {
buf1[0] = '$'; buf1[0] = '$';
put_buffer(buf1, 1); put_buffer(s, buf1, 1);
len = strlen(buf); len = strlen(buf);
put_buffer(buf, len); put_buffer(s, buf, len);
csum = 0; csum = 0;
for(i = 0; i < len; i++) { for(i = 0; i < len; i++) {
csum += buf[i]; csum += buf[i];
...@@ -235,9 +150,9 @@ static int put_packet(char *buf) ...@@ -235,9 +150,9 @@ static int put_packet(char *buf)
buf1[1] = tohex((csum >> 4) & 0xf); buf1[1] = tohex((csum >> 4) & 0xf);
buf1[2] = tohex((csum) & 0xf); buf1[2] = tohex((csum) & 0xf);
put_buffer(buf1, 3); put_buffer(s, buf1, 3);
ch = get_char(); ch = get_char(s);
if (ch < 0) if (ch < 0)
return -1; return -1;
if (ch == '+') if (ch == '+')
...@@ -387,149 +302,293 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) ...@@ -387,149 +302,293 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
#endif #endif
/* port = 0 means default port */ /* port = 0 means default port */
int cpu_gdbstub(void *opaque, int (*main_loop)(void *opaque), int port) static int gdb_handle_packet(GDBState *s, const char *line_buf)
{ {
CPUState *env; CPUState *env = cpu_single_env;
const char *p; const char *p;
int ret, ch, reg_size, type; int ch, reg_size, type;
char buf[4096]; char buf[4096];
uint8_t mem_buf[2000]; uint8_t mem_buf[2000];
uint32_t *registers; uint32_t *registers;
uint32_t addr, len; uint32_t addr, len;
printf("Waiting gdb connection on port %d\n", port); #ifdef DEBUG_GDB
if (gdbstub_open(port) < 0) printf("command='%s'\n", line_buf);
return -1; #endif
printf("Connected\n"); p = line_buf;
for(;;) { ch = *p++;
ret = get_packet(buf, sizeof(buf)); switch(ch) {
if (ret < 0) case '?':
break; snprintf(buf, sizeof(buf), "S%02x", SIGTRAP);
p = buf; put_packet(s, buf);
ch = *p++; break;
switch(ch) { case 'c':
case '?': if (*p != '\0') {
snprintf(buf, sizeof(buf), "S%02x", SIGTRAP); addr = strtoul(p, (char **)&p, 16);
put_packet(buf);
break;
case 'c':
if (*p != '\0') {
addr = strtoul(p, (char **)&p, 16);
env = cpu_gdbstub_get_env(opaque);
#if defined(TARGET_I386) #if defined(TARGET_I386)
env->eip = addr; env->eip = addr;
#elif defined (TARGET_PPC) #elif defined (TARGET_PPC)
env->nip = addr; env->nip = addr;
#endif #endif
} }
ret = main_loop(opaque); vm_start();
if (ret == EXCP_DEBUG) break;
ret = SIGTRAP; case 's':
else if (*p != '\0') {
ret = 0; addr = strtoul(p, (char **)&p, 16);
snprintf(buf, sizeof(buf), "S%02x", ret);
put_packet(buf);
break;
case 's':
env = cpu_gdbstub_get_env(opaque);
if (*p != '\0') {
addr = strtoul(p, (char **)&p, 16);
#if defined(TARGET_I386) #if defined(TARGET_I386)
env->eip = addr; env->eip = addr;
#elif defined (TARGET_PPC) #elif defined (TARGET_PPC)
env->nip = addr; env->nip = addr;
#endif #endif
}
cpu_single_step(env, 1);
vm_start();
break;
case 'g':
reg_size = cpu_gdb_read_registers(env, mem_buf);
memtohex(buf, mem_buf, reg_size);
put_packet(s, buf);
break;
case 'G':
registers = (void *)mem_buf;
len = strlen(p) / 2;
hextomem((uint8_t *)registers, p, len);
cpu_gdb_write_registers(env, mem_buf, len);
put_packet(s, "OK");
break;
case 'm':
addr = strtoul(p, (char **)&p, 16);
if (*p == ',')
p++;
len = strtoul(p, NULL, 16);
if (cpu_memory_rw_debug(env, addr, mem_buf, len, 0) != 0)
memset(mem_buf, 0, len);
memtohex(buf, mem_buf, len);
put_packet(s, buf);
break;
case 'M':
addr = strtoul(p, (char **)&p, 16);
if (*p == ',')
p++;
len = strtoul(p, (char **)&p, 16);
if (*p == ',')
p++;
hextomem(mem_buf, p, len);
if (cpu_memory_rw_debug(env, addr, mem_buf, len, 1) != 0)
put_packet(s, "ENN");
else
put_packet(s, "OK");
break;
case 'Z':
type = strtoul(p, (char **)&p, 16);
if (*p == ',')
p++;
addr = strtoul(p, (char **)&p, 16);
if (*p == ',')
p++;
len = strtoul(p, (char **)&p, 16);
if (type == 0 || type == 1) {
if (cpu_breakpoint_insert(env, addr) < 0)
goto breakpoint_error;
put_packet(s, "OK");
} else {
breakpoint_error:
put_packet(s, "ENN");
}
break;
case 'z':
type = strtoul(p, (char **)&p, 16);
if (*p == ',')
p++;
addr = strtoul(p, (char **)&p, 16);
if (*p == ',')
p++;
len = strtoul(p, (char **)&p, 16);
if (type == 0 || type == 1) {
cpu_breakpoint_remove(env, addr);
put_packet(s, "OK");
} else {
goto breakpoint_error;
}
break;
default:
// unknown_command:
/* put empty packet */
buf[0] = '\0';
put_packet(s, buf);
break;
}
return RS_IDLE;
}
static void gdb_vm_stopped(void *opaque, int reason)
{
GDBState *s = opaque;
char buf[256];
int ret;
/* disable single step if it was enable */
cpu_single_step(cpu_single_env, 0);
if (reason == EXCP_DEBUG)
ret = SIGTRAP;
else
ret = 0;
snprintf(buf, sizeof(buf), "S%02x", ret);
put_packet(s, buf);
}
static void gdb_read_byte(GDBState *s, int ch)
{
int i, csum;
char reply[1];
if (vm_running) {
/* when the CPU is running, we cannot do anything except stop
it when receiving a char */
vm_stop(EXCP_INTERRUPT);
} else {
switch(s->state) {
case RS_IDLE:
if (ch == '$') {
s->line_buf_index = 0;
s->state = RS_GETLINE;
} }
cpu_single_step(env, 1);
ret = main_loop(opaque);
cpu_single_step(env, 0);
if (ret == EXCP_DEBUG)
ret = SIGTRAP;
else
ret = 0;
snprintf(buf, sizeof(buf), "S%02x", ret);
put_packet(buf);
break;
case 'g':
env = cpu_gdbstub_get_env(opaque);
reg_size = cpu_gdb_read_registers(env, mem_buf);
memtohex(buf, mem_buf, reg_size);
put_packet(buf);
break; break;
case 'G': case RS_GETLINE:
env = cpu_gdbstub_get_env(opaque); if (ch == '#') {
registers = (void *)mem_buf; s->state = RS_CHKSUM1;
len = strlen(p) / 2; } else if (s->line_buf_index >= sizeof(s->line_buf) - 1) {
hextomem((uint8_t *)registers, p, len); s->state = RS_IDLE;
cpu_gdb_write_registers(env, mem_buf, len);
put_packet("OK");
break;
case 'm':
env = cpu_gdbstub_get_env(opaque);
addr = strtoul(p, (char **)&p, 16);
if (*p == ',')
p++;
len = strtoul(p, NULL, 16);
if (cpu_memory_rw_debug(env, addr, mem_buf, len, 0) != 0)
memset(mem_buf, 0, len);
memtohex(buf, mem_buf, len);
put_packet(buf);
break;
case 'M':
env = cpu_gdbstub_get_env(opaque);
addr = strtoul(p, (char **)&p, 16);
if (*p == ',')
p++;
len = strtoul(p, (char **)&p, 16);
if (*p == ',')
p++;
hextomem(mem_buf, p, len);
if (cpu_memory_rw_debug(env, addr, mem_buf, len, 1) != 0)
put_packet("ENN");
else
put_packet("OK");
break;
case 'Z':
type = strtoul(p, (char **)&p, 16);
if (*p == ',')
p++;
addr = strtoul(p, (char **)&p, 16);
if (*p == ',')
p++;
len = strtoul(p, (char **)&p, 16);
if (type == 0 || type == 1) {
env = cpu_gdbstub_get_env(opaque);
if (cpu_breakpoint_insert(env, addr) < 0)
goto breakpoint_error;
put_packet("OK");
} else { } else {
breakpoint_error: s->line_buf[s->line_buf_index++] = ch;
put_packet("ENN");
} }
break; break;
case 'z': case RS_CHKSUM1:
type = strtoul(p, (char **)&p, 16); s->line_buf[s->line_buf_index] = '\0';
if (*p == ',') s->line_csum = fromhex(ch) << 4;
p++; s->state = RS_CHKSUM2;
addr = strtoul(p, (char **)&p, 16); break;
if (*p == ',') case RS_CHKSUM2:
p++; s->line_csum |= fromhex(ch);
len = strtoul(p, (char **)&p, 16); csum = 0;
if (type == 0 || type == 1) { for(i = 0; i < s->line_buf_index; i++) {
env = cpu_gdbstub_get_env(opaque); csum += s->line_buf[i];
cpu_breakpoint_remove(env, addr); }
put_packet("OK"); if (s->line_csum != (csum & 0xff)) {
reply[0] = '-';
put_buffer(s, reply, 1);
s->state = RS_IDLE;
} else { } else {
goto breakpoint_error; reply[0] = '+';
put_buffer(s, reply, 1);
s->state = gdb_handle_packet(s, s->line_buf);
} }
break; break;
default: }
// unknown_command: }
/* put empty packet */ }
buf[0] = '\0';
put_packet(buf); static int gdb_can_read(void *opaque)
{
return 256;
}
static void gdb_read(void *opaque, const uint8_t *buf, int size)
{
GDBState *s = opaque;
int i;
if (size == 0) {
/* end of connection */
qemu_del_vm_stop_handler(gdb_vm_stopped, s);
qemu_del_fd_read_handler(s->fd);
qemu_free(s);
vm_start();
} else {
for(i = 0; i < size; i++)
gdb_read_byte(s, buf[i]);
}
}
static void gdb_accept(void *opaque, const uint8_t *buf, int size)
{
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; break;
} }
} }
/* set short latency */
val = 1;
setsockopt(fd, SOL_TCP, TCP_NODELAY, &val, sizeof(val));
s = qemu_mallocz(sizeof(GDBState));
if (!s) {
close(fd);
return;
}
s->fd = fd;
fcntl(fd, F_SETFL, O_NONBLOCK);
/* stop the VM */
vm_stop(EXCP_INTERRUPT);
/* start handling I/O */
qemu_add_fd_read_handler(s->fd, gdb_can_read, gdb_read, s);
/* when the VM is stopped, the following callback is called */
qemu_add_vm_stop_handler(gdb_vm_stopped, s);
}
static int gdbserver_open(int port)
{
struct sockaddr_in sockaddr;
int fd, val, ret;
fd = socket(PF_INET, SOCK_STREAM, 0);
if (fd < 0) {
perror("socket");
return -1;
}
/* allow fast reuse */
val = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
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;
}
ret = listen(fd, 0);
if (ret < 0) {
perror("listen");
return -1;
}
fcntl(fd, F_SETFL, O_NONBLOCK);
return fd;
}
int gdbserver_start(int port)
{
gdbserver_fd = gdbserver_open(port);
if (gdbserver_fd < 0)
return -1;
/* accept connections */
qemu_add_fd_read_handler(gdbserver_fd, NULL, gdb_accept, NULL);
return 0; return 0;
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册