gdbstub: make GDBState static and have common init function

Instead of allocating make this entirely static. We shall reduce the
size of the structure in later commits and dynamically allocate parts
of it. We introduce an init and reset helper function to keep all the
manipulation in one place.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Damien Hedde <damien.hedde@greensocs.com>

Message-Id: <20200316172155.971-7-alex.bennee@linaro.org>
This commit is contained in:
Alex Bennée 2020-03-16 17:21:33 +00:00
parent 2dbd39c27d
commit 8d98c445dc
1 changed files with 81 additions and 87 deletions

168
gdbstub.c
View File

@ -342,6 +342,7 @@ enum RSState {
RS_CHKSUM2, RS_CHKSUM2,
}; };
typedef struct GDBState { typedef struct GDBState {
bool init; /* have we been initialised? */
CPUState *c_cpu; /* current CPU for step/continue ops */ CPUState *c_cpu; /* current CPU for step/continue ops */
CPUState *g_cpu; /* current CPU for other ops */ CPUState *g_cpu; /* current CPU for other ops */
CPUState *query_cpu; /* for q{f|s}ThreadInfo */ CPUState *query_cpu; /* for q{f|s}ThreadInfo */
@ -372,7 +373,23 @@ typedef struct GDBState {
*/ */
static int sstep_flags = SSTEP_ENABLE|SSTEP_NOIRQ|SSTEP_NOTIMER; static int sstep_flags = SSTEP_ENABLE|SSTEP_NOIRQ|SSTEP_NOTIMER;
static GDBState *gdbserver_state; static GDBState gdbserver_state;
static void init_gdbserver_state(void)
{
g_assert(!gdbserver_state.init);
memset(&gdbserver_state, 0, sizeof(GDBState));
gdbserver_state.init = true;
}
#ifndef CONFIG_USER_ONLY
static void reset_gdbserver_state(void)
{
g_free(gdbserver_state.processes);
gdbserver_state.processes = NULL;
gdbserver_state.process_num = 0;
}
#endif
bool gdb_has_xml; bool gdb_has_xml;
@ -425,8 +442,8 @@ int use_gdb_syscalls(void)
/* -semihosting-config target=auto */ /* -semihosting-config target=auto */
/* On the first call check if gdb is connected and remember. */ /* On the first call check if gdb is connected and remember. */
if (gdb_syscall_mode == GDB_SYS_UNKNOWN) { if (gdb_syscall_mode == GDB_SYS_UNKNOWN) {
gdb_syscall_mode = (gdbserver_state ? GDB_SYS_ENABLED gdb_syscall_mode = gdbserver_state.init ?
: GDB_SYS_DISABLED); GDB_SYS_ENABLED : GDB_SYS_DISABLED;
} }
return gdb_syscall_mode == GDB_SYS_ENABLED; return gdb_syscall_mode == GDB_SYS_ENABLED;
} }
@ -984,7 +1001,7 @@ static int gdb_breakpoint_insert(int type, target_ulong addr, target_ulong len)
int err = 0; int err = 0;
if (kvm_enabled()) { if (kvm_enabled()) {
return kvm_insert_breakpoint(gdbserver_state->c_cpu, addr, len, type); return kvm_insert_breakpoint(gdbserver_state.c_cpu, addr, len, type);
} }
switch (type) { switch (type) {
@ -1021,7 +1038,7 @@ static int gdb_breakpoint_remove(int type, target_ulong addr, target_ulong len)
int err = 0; int err = 0;
if (kvm_enabled()) { if (kvm_enabled()) {
return kvm_remove_breakpoint(gdbserver_state->c_cpu, addr, len, type); return kvm_remove_breakpoint(gdbserver_state.c_cpu, addr, len, type);
} }
switch (type) { switch (type) {
@ -1074,7 +1091,7 @@ static void gdb_breakpoint_remove_all(void)
CPUState *cpu; CPUState *cpu;
if (kvm_enabled()) { if (kvm_enabled()) {
kvm_remove_all_breakpoints(gdbserver_state->c_cpu); kvm_remove_all_breakpoints(gdbserver_state.c_cpu);
return; return;
} }
@ -2601,7 +2618,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
void gdb_set_stop_cpu(CPUState *cpu) void gdb_set_stop_cpu(CPUState *cpu)
{ {
GDBProcess *p = gdb_get_cpu_process(gdbserver_state, cpu); GDBProcess *p = gdb_get_cpu_process(&gdbserver_state, cpu);
if (!p->attached) { if (!p->attached) {
/* /*
@ -2611,14 +2628,14 @@ void gdb_set_stop_cpu(CPUState *cpu)
return; return;
} }
gdbserver_state->c_cpu = cpu; gdbserver_state.c_cpu = cpu;
gdbserver_state->g_cpu = cpu; gdbserver_state.g_cpu = cpu;
} }
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
static void gdb_vm_state_change(void *opaque, int running, RunState state) static void gdb_vm_state_change(void *opaque, int running, RunState state)
{ {
GDBState *s = gdbserver_state; GDBState *s = &gdbserver_state;
CPUState *cpu = s->c_cpu; CPUState *cpu = s->c_cpu;
char buf[256]; char buf[256];
char thread_id[16]; char thread_id[16];
@ -2722,17 +2739,16 @@ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va)
char *p_end; char *p_end;
target_ulong addr; target_ulong addr;
uint64_t i64; uint64_t i64;
GDBState *s;
s = gdbserver_state; if (!gdbserver_state.init)
if (!s)
return; return;
s->current_syscall_cb = cb;
gdbserver_state.current_syscall_cb = cb;
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
vm_stop(RUN_STATE_DEBUG); vm_stop(RUN_STATE_DEBUG);
#endif #endif
p = s->syscall_buf; p = &gdbserver_state.syscall_buf[0];
p_end = &s->syscall_buf[sizeof(s->syscall_buf)]; p_end = &gdbserver_state.syscall_buf[sizeof(gdbserver_state.syscall_buf)];
*(p++) = 'F'; *(p++) = 'F';
while (*fmt) { while (*fmt) {
if (*fmt == '%') { if (*fmt == '%') {
@ -2765,14 +2781,14 @@ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va)
} }
*p = 0; *p = 0;
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
put_packet(s, s->syscall_buf); put_packet(&gdbserver_state, gdbserver_state.syscall_buf);
/* Return control to gdb for it to process the syscall request. /* Return control to gdb for it to process the syscall request.
* Since the protocol requires that gdb hands control back to us * Since the protocol requires that gdb hands control back to us
* using a "here are the results" F packet, we don't need to check * using a "here are the results" F packet, we don't need to check
* gdb_handlesig's return value (which is the signal to deliver if * gdb_handlesig's return value (which is the signal to deliver if
* execution was resumed via a continue packet). * execution was resumed via a continue packet).
*/ */
gdb_handlesig(s->c_cpu, 0); gdb_handlesig(gdbserver_state.c_cpu, 0);
#else #else
/* In this case wait to send the syscall packet until notification that /* In this case wait to send the syscall packet until notification that
the CPU has stopped. This must be done because if the packet is sent the CPU has stopped. This must be done because if the packet is sent
@ -2780,7 +2796,7 @@ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va)
is still in the running state, which can cause packets to be dropped is still in the running state, which can cause packets to be dropped
and state transition 'T' packets to be sent while the syscall is still and state transition 'T' packets to be sent while the syscall is still
being processed. */ being processed. */
qemu_cpu_kick(s->c_cpu); qemu_cpu_kick(gdbserver_state.c_cpu);
#endif #endif
} }
@ -2941,15 +2957,13 @@ static void gdb_read_byte(GDBState *s, uint8_t ch)
/* Tell the remote gdb that the process has exited. */ /* Tell the remote gdb that the process has exited. */
void gdb_exit(CPUArchState *env, int code) void gdb_exit(CPUArchState *env, int code)
{ {
GDBState *s;
char buf[4]; char buf[4];
s = gdbserver_state; if (!gdbserver_state.init) {
if (!s) {
return; return;
} }
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
if (gdbserver_fd < 0 || s->fd < 0) { if (gdbserver_fd < 0 || gdbserver_state.fd < 0) {
return; return;
} }
#endif #endif
@ -2957,10 +2971,10 @@ void gdb_exit(CPUArchState *env, int code)
trace_gdbstub_op_exiting((uint8_t)code); trace_gdbstub_op_exiting((uint8_t)code);
snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code); snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code);
put_packet(s, buf); put_packet(&gdbserver_state, buf);
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
qemu_chr_fe_deinit(&s->chr, true); qemu_chr_fe_deinit(&gdbserver_state.chr, true);
#endif #endif
} }
@ -2993,12 +3007,10 @@ static void create_default_process(GDBState *s)
int int
gdb_handlesig(CPUState *cpu, int sig) gdb_handlesig(CPUState *cpu, int sig)
{ {
GDBState *s;
char buf[256]; char buf[256];
int n; int n;
s = gdbserver_state; if (gdbserver_fd < 0 || gdbserver_state.fd < 0) {
if (gdbserver_fd < 0 || s->fd < 0) {
return sig; return sig;
} }
@ -3008,58 +3020,55 @@ gdb_handlesig(CPUState *cpu, int sig)
if (sig != 0) { if (sig != 0) {
snprintf(buf, sizeof(buf), "S%02x", target_signal_to_gdb(sig)); snprintf(buf, sizeof(buf), "S%02x", target_signal_to_gdb(sig));
put_packet(s, buf); put_packet(&gdbserver_state, buf);
} }
/* put_packet() might have detected that the peer terminated the /* put_packet() might have detected that the peer terminated the
connection. */ connection. */
if (s->fd < 0) { if (gdbserver_state.fd < 0) {
return sig; return sig;
} }
sig = 0; sig = 0;
s->state = RS_IDLE; gdbserver_state.state = RS_IDLE;
s->running_state = 0; gdbserver_state.running_state = 0;
while (s->running_state == 0) { while (gdbserver_state.running_state == 0) {
n = read(s->fd, buf, 256); n = read(gdbserver_state.fd, buf, 256);
if (n > 0) { if (n > 0) {
int i; int i;
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
gdb_read_byte(s, buf[i]); gdb_read_byte(&gdbserver_state, buf[i]);
} }
} else { } else {
/* XXX: Connection closed. Should probably wait for another /* XXX: Connection closed. Should probably wait for another
connection before continuing. */ connection before continuing. */
if (n == 0) { if (n == 0) {
close(s->fd); close(gdbserver_state.fd);
} }
s->fd = -1; gdbserver_state.fd = -1;
return sig; return sig;
} }
} }
sig = s->signal; sig = gdbserver_state.signal;
s->signal = 0; gdbserver_state.signal = 0;
return sig; return sig;
} }
/* Tell the remote gdb that the process has exited due to SIG. */ /* Tell the remote gdb that the process has exited due to SIG. */
void gdb_signalled(CPUArchState *env, int sig) void gdb_signalled(CPUArchState *env, int sig)
{ {
GDBState *s;
char buf[4]; char buf[4];
s = gdbserver_state; if (gdbserver_fd < 0 || gdbserver_state.fd < 0) {
if (gdbserver_fd < 0 || s->fd < 0) {
return; return;
} }
snprintf(buf, sizeof(buf), "X%02x", target_signal_to_gdb(sig)); snprintf(buf, sizeof(buf), "X%02x", target_signal_to_gdb(sig));
put_packet(s, buf); put_packet(&gdbserver_state, buf);
} }
static bool gdb_accept(void) static bool gdb_accept(void)
{ {
GDBState *s;
struct sockaddr_in sockaddr; struct sockaddr_in sockaddr;
socklen_t len; socklen_t len;
int fd; int fd;
@ -3083,15 +3092,13 @@ static bool gdb_accept(void)
return false; return false;
} }
s = g_malloc0(sizeof(GDBState)); init_gdbserver_state();
create_default_process(s); create_default_process(&gdbserver_state);
s->processes[0].attached = true; gdbserver_state.processes[0].attached = true;
s->c_cpu = gdb_first_attached_cpu(s); gdbserver_state.c_cpu = gdb_first_attached_cpu(&gdbserver_state);
s->g_cpu = s->c_cpu; gdbserver_state.g_cpu = gdbserver_state.c_cpu;
s->fd = fd; gdbserver_state.fd = fd;
gdb_has_xml = false; gdb_has_xml = false;
gdbserver_state = s;
return true; return true;
} }
@ -3144,13 +3151,11 @@ int gdbserver_start(int port)
/* Disable gdb stub for child processes. */ /* Disable gdb stub for child processes. */
void gdbserver_fork(CPUState *cpu) void gdbserver_fork(CPUState *cpu)
{ {
GDBState *s = gdbserver_state; if (gdbserver_fd < 0 || gdbserver_state.fd < 0) {
if (gdbserver_fd < 0 || s->fd < 0) {
return; return;
} }
close(s->fd); close(gdbserver_state.fd);
s->fd = -1; gdbserver_state.fd = -1;
cpu_breakpoint_remove_all(cpu, BP_GDB); cpu_breakpoint_remove_all(cpu, BP_GDB);
cpu_watchpoint_remove_all(cpu, BP_GDB); cpu_watchpoint_remove_all(cpu, BP_GDB);
} }
@ -3167,7 +3172,7 @@ static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size)
int i; int i;
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
gdb_read_byte(gdbserver_state, buf[i]); gdb_read_byte(&gdbserver_state, buf[i]);
} }
} }
@ -3210,13 +3215,13 @@ static int gdb_monitor_write(Chardev *chr, const uint8_t *buf, int len)
const char *p = (const char *)buf; const char *p = (const char *)buf;
int max_sz; int max_sz;
max_sz = (sizeof(gdbserver_state->last_packet) - 2) / 2; max_sz = (sizeof(gdbserver_state.last_packet) - 2) / 2;
for (;;) { for (;;) {
if (len <= max_sz) { if (len <= max_sz) {
gdb_monitor_output(gdbserver_state, p, len); gdb_monitor_output(&gdbserver_state, p, len);
break; break;
} }
gdb_monitor_output(gdbserver_state, p, max_sz); gdb_monitor_output(&gdbserver_state, p, max_sz);
p += max_sz; p += max_sz;
len -= max_sz; len -= max_sz;
} }
@ -3308,18 +3313,10 @@ static void create_processes(GDBState *s)
create_default_process(s); create_default_process(s);
} }
static void cleanup_processes(GDBState *s)
{
g_free(s->processes);
s->process_num = 0;
s->processes = NULL;
}
int gdbserver_start(const char *device) int gdbserver_start(const char *device)
{ {
trace_gdbstub_op_start(device); trace_gdbstub_op_start(device);
GDBState *s;
char gdbstub_device_name[128]; char gdbstub_device_name[128];
Chardev *chr = NULL; Chardev *chr = NULL;
Chardev *mon_chr; Chardev *mon_chr;
@ -3357,10 +3354,8 @@ int gdbserver_start(const char *device)
return -1; return -1;
} }
s = gdbserver_state; if (!gdbserver_state.init) {
if (!s) { init_gdbserver_state();
s = g_malloc0(sizeof(GDBState));
gdbserver_state = s;
qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL); qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL);
@ -3369,31 +3364,30 @@ int gdbserver_start(const char *device)
NULL, NULL, &error_abort); NULL, NULL, &error_abort);
monitor_init_hmp(mon_chr, false, &error_abort); monitor_init_hmp(mon_chr, false, &error_abort);
} else { } else {
qemu_chr_fe_deinit(&s->chr, true); qemu_chr_fe_deinit(&gdbserver_state.chr, true);
mon_chr = s->mon_chr; mon_chr = gdbserver_state.mon_chr;
cleanup_processes(s); reset_gdbserver_state();
memset(s, 0, sizeof(GDBState));
s->mon_chr = mon_chr;
} }
create_processes(s); create_processes(&gdbserver_state);
if (chr) { if (chr) {
qemu_chr_fe_init(&s->chr, chr, &error_abort); qemu_chr_fe_init(&gdbserver_state.chr, chr, &error_abort);
qemu_chr_fe_set_handlers(&s->chr, gdb_chr_can_receive, gdb_chr_receive, qemu_chr_fe_set_handlers(&gdbserver_state.chr, gdb_chr_can_receive,
gdb_chr_event, NULL, s, NULL, true); gdb_chr_receive, gdb_chr_event,
NULL, &gdbserver_state, NULL, true);
} }
s->state = chr ? RS_IDLE : RS_INACTIVE; gdbserver_state.state = chr ? RS_IDLE : RS_INACTIVE;
s->mon_chr = mon_chr; gdbserver_state.mon_chr = mon_chr;
s->current_syscall_cb = NULL; gdbserver_state.current_syscall_cb = NULL;
return 0; return 0;
} }
void gdbserver_cleanup(void) void gdbserver_cleanup(void)
{ {
if (gdbserver_state) { if (gdbserver_state.init) {
put_packet(gdbserver_state, "W00"); put_packet(&gdbserver_state, "W00");
} }
} }