From: "Edgar E. Iglesias" <edgar.igles...@xilinx.com> Makes it possible to request interruption of the GDB debug session.
Signed-off-by: Edgar E. Iglesias <edgar.igles...@xilinx.com> --- gdbstub.c | 68 ++++++++++++++++++++++++++++++++++++++---------- include/exec/gdbstub.h | 2 ++ 2 files changed, 56 insertions(+), 14 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index e80e1d3..84232f6 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -297,6 +297,7 @@ typedef struct GDBState { uint8_t last_packet[MAX_PACKET_LENGTH + 4]; int last_packet_len; int signal; + int client_connected; #ifdef CONFIG_USER_ONLY int fd; int running_state; @@ -2505,12 +2506,56 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) return RS_IDLE; } +static void gdb_output(GDBState *s, const char *msg, int len) +{ + char buf[MAX_PACKET_LENGTH]; + + buf[0] = 'O'; + if (len > (MAX_PACKET_LENGTH/2) - 1) { + len = (MAX_PACKET_LENGTH/2) - 1; + } + memtohex(buf + 1, (uint8_t *)msg, len); + put_packet(s, buf); +} + void gdb_set_stop_cpu(CPUArchState *env) { gdbserver_state->c_cpu = env; gdbserver_state->g_cpu = env; } +static int gdbserver_has_client(void) +{ + return gdbserver_state && gdbserver_state->client_connected; +} + +int gdbserver_break(const char *msg) +{ + + if (!gdbserver_has_client()) { + return 1; + } + + if (msg) { + gdb_output(gdbserver_state, msg, strlen(msg)); + } + + /* If there's a CPU running, break it's execution. */ + if (cpu_single_env) { + CPUState *cpu = ENV_GET_CPU(cpu_single_env); + cpu_single_env->exception_index = EXCP_DEBUG; + if (cpu->current_tb) { + /* Break out of current TB and request debug action. */ + cpu_loop_exit(cpu_single_env); + } + } +#ifndef CONFIG_USER_ONLY + /* Request global debug action. */ + qemu_system_debug_request(); +#endif + return 0; +} + #ifndef CONFIG_USER_ONLY static void gdb_vm_state_change(void *opaque, int running, RunState state) { @@ -2815,6 +2860,7 @@ gdb_handlesig (CPUArchState *env, int sig) { /* XXX: Connection closed. Should probably wait for another connection before continuing. */ + s->client_connected = false; return sig; } } @@ -2868,7 +2914,7 @@ static void gdb_accept(void) gdb_has_xml = 0; gdbserver_state = s; - + s->client_connected = true; fcntl(fd, F_SETFL, O_NONBLOCK); } @@ -2952,23 +2998,17 @@ static void gdb_chr_event(void *opaque, int event) case CHR_EVENT_OPENED: vm_stop(RUN_STATE_PAUSED); gdb_has_xml = 0; + gdbserver_state->client_connected = true; break; + case CHR_EVENT_CLOSED: { + gdbserver_state->client_connected = false; + break; + } default: break; } } -static void gdb_monitor_output(GDBState *s, const char *msg, int len) -{ - char buf[MAX_PACKET_LENGTH]; - - buf[0] = 'O'; - if (len > (MAX_PACKET_LENGTH/2) - 1) - len = (MAX_PACKET_LENGTH/2) - 1; - memtohex(buf + 1, (uint8_t *)msg, len); - put_packet(s, buf); -} - static int gdb_monitor_write(CharDriverState *chr, const uint8_t *buf, int len) { const char *p = (const char *)buf; @@ -2977,10 +3017,10 @@ static int gdb_monitor_write(CharDriverState *chr, const uint8_t *buf, int len) max_sz = (sizeof(gdbserver_state->last_packet) - 2) / 2; for (;;) { if (len <= max_sz) { - gdb_monitor_output(gdbserver_state, p, len); + gdb_output(gdbserver_state, p, len); break; } - gdb_monitor_output(gdbserver_state, p, max_sz); + gdb_output(gdbserver_state, p, max_sz); p += max_sz; len -= max_sz; } diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index ba20afa..6f29d2a 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -47,6 +47,8 @@ int gdbserver_start(int); int gdbserver_start(const char *port); #endif +int gdbserver_break(const char *msg); + /* in gdbstub-xml.c, generated by scripts/feature_to_c.sh */ extern const char *const xml_builtin[][2]; -- 1.7.10.4