gdbstub: add multiprocess support to 'H' and 'T' packets

Add a couple of helper functions to cope with GDB threads and processes.

The gdb_get_process() function looks for a process given a pid.

The gdb_get_cpu() function returns the CPU corresponding to the (pid,
tid) pair given as parameters.

The read_thread_id() function parses the thread-id sent by the peer.
This function supports the multiprocess extension thread-id syntax.  The
return value specifies if the parsing failed, or if a special case was
encountered (all processes or all threads).

Use them in 'H' and 'T' packets handling to support the multiprocess
extension.

Signed-off-by: Luc Michel <luc.michel@greensocs.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
Acked-by: Alistair Francis <alistair.francis@wdc.com>
Message-id: 20181207090135.7651-5-luc.michel@greensocs.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Luc Michel 2019-01-07 15:23:45 +00:00 committed by Peter Maydell
parent 1a22733697
commit 7d8c87da79

154
gdbstub.c
View File

@ -688,6 +688,71 @@ out:
#endif #endif
} }
static GDBProcess *gdb_get_process(const GDBState *s, uint32_t pid)
{
int i;
if (!pid) {
/* 0 means any process, we take the first one */
return &s->processes[0];
}
for (i = 0; i < s->process_num; i++) {
if (s->processes[i].pid == pid) {
return &s->processes[i];
}
}
return NULL;
}
static GDBProcess *gdb_get_cpu_process(const GDBState *s, CPUState *cpu)
{
return gdb_get_process(s, gdb_get_cpu_pid(s, cpu));
}
static CPUState *find_cpu(uint32_t thread_id)
{
CPUState *cpu;
CPU_FOREACH(cpu) {
if (cpu_gdb_index(cpu) == thread_id) {
return cpu;
}
}
return NULL;
}
static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid)
{
GDBProcess *process;
CPUState *cpu;
if (!tid) {
/* 0 means any thread, we take the first one */
tid = 1;
}
cpu = find_cpu(tid);
if (cpu == NULL) {
return NULL;
}
process = gdb_get_cpu_process(s, cpu);
if (process->pid != pid) {
return NULL;
}
if (!process->attached) {
return NULL;
}
return cpu;
}
static const char *get_feature_xml(const char *p, const char **newp, static const char *get_feature_xml(const char *p, const char **newp,
CPUClass *cc) CPUClass *cc)
{ {
@ -944,19 +1009,6 @@ static void gdb_set_cpu_pc(GDBState *s, target_ulong pc)
cpu_set_pc(cpu, pc); cpu_set_pc(cpu, pc);
} }
static CPUState *find_cpu(uint32_t thread_id)
{
CPUState *cpu;
CPU_FOREACH(cpu) {
if (cpu_gdb_index(cpu) == thread_id) {
return cpu;
}
}
return NULL;
}
static char *gdb_fmt_thread_id(const GDBState *s, CPUState *cpu, static char *gdb_fmt_thread_id(const GDBState *s, CPUState *cpu,
char *buf, size_t buf_size) char *buf, size_t buf_size)
{ {
@ -970,6 +1022,60 @@ static char *gdb_fmt_thread_id(const GDBState *s, CPUState *cpu,
return buf; return buf;
} }
typedef enum GDBThreadIdKind {
GDB_ONE_THREAD = 0,
GDB_ALL_THREADS, /* One process, all threads */
GDB_ALL_PROCESSES,
GDB_READ_THREAD_ERR
} GDBThreadIdKind;
static GDBThreadIdKind read_thread_id(const char *buf, const char **end_buf,
uint32_t *pid, uint32_t *tid)
{
unsigned long p, t;
int ret;
if (*buf == 'p') {
buf++;
ret = qemu_strtoul(buf, &buf, 16, &p);
if (ret) {
return GDB_READ_THREAD_ERR;
}
/* Skip '.' */
buf++;
} else {
p = 1;
}
ret = qemu_strtoul(buf, &buf, 16, &t);
if (ret) {
return GDB_READ_THREAD_ERR;
}
*end_buf = buf;
if (p == -1) {
return GDB_ALL_PROCESSES;
}
if (pid) {
*pid = p;
}
if (t == -1) {
return GDB_ALL_THREADS;
}
if (tid) {
*tid = t;
}
return GDB_ONE_THREAD;
}
static int is_query_packet(const char *p, const char *query, char separator) static int is_query_packet(const char *p, const char *query, char separator)
{ {
unsigned int query_len = strlen(query); unsigned int query_len = strlen(query);
@ -1078,12 +1184,14 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
CPUClass *cc; CPUClass *cc;
const char *p; const char *p;
uint32_t thread; uint32_t thread;
uint32_t pid, tid;
int ch, reg_size, type, res; int ch, reg_size, type, res;
uint8_t mem_buf[MAX_PACKET_LENGTH]; uint8_t mem_buf[MAX_PACKET_LENGTH];
char buf[sizeof(mem_buf) + 1 /* trailing NUL */]; char buf[sizeof(mem_buf) + 1 /* trailing NUL */];
char thread_id[16]; char thread_id[16];
uint8_t *registers; uint8_t *registers;
target_ulong addr, len; target_ulong addr, len;
GDBThreadIdKind thread_kind;
trace_gdbstub_io_command(line_buf); trace_gdbstub_io_command(line_buf);
@ -1291,12 +1399,18 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
break; break;
case 'H': case 'H':
type = *p++; type = *p++;
thread = strtoull(p, (char **)&p, 16);
if (thread == -1 || thread == 0) { thread_kind = read_thread_id(p, &p, &pid, &tid);
if (thread_kind == GDB_READ_THREAD_ERR) {
put_packet(s, "E22");
break;
}
if (thread_kind != GDB_ONE_THREAD) {
put_packet(s, "OK"); put_packet(s, "OK");
break; break;
} }
cpu = find_cpu(thread); cpu = gdb_get_cpu(s, pid, tid);
if (cpu == NULL) { if (cpu == NULL) {
put_packet(s, "E22"); put_packet(s, "E22");
break; break;
@ -1316,8 +1430,12 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
} }
break; break;
case 'T': case 'T':
thread = strtoull(p, (char **)&p, 16); thread_kind = read_thread_id(p, &p, &pid, &tid);
cpu = find_cpu(thread); if (thread_kind == GDB_READ_THREAD_ERR) {
put_packet(s, "E22");
break;
}
cpu = gdb_get_cpu(s, pid, tid);
if (cpu != NULL) { if (cpu != NULL) {
put_packet(s, "OK"); put_packet(s, "OK");