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:
parent
1a22733697
commit
7d8c87da79
154
gdbstub.c
154
gdbstub.c
@ -688,6 +688,71 @@ out:
|
||||
#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,
|
||||
CPUClass *cc)
|
||||
{
|
||||
@ -944,19 +1009,6 @@ static void gdb_set_cpu_pc(GDBState *s, target_ulong 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,
|
||||
char *buf, size_t buf_size)
|
||||
{
|
||||
@ -970,6 +1022,60 @@ static char *gdb_fmt_thread_id(const GDBState *s, CPUState *cpu,
|
||||
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)
|
||||
{
|
||||
unsigned int query_len = strlen(query);
|
||||
@ -1078,12 +1184,14 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
|
||||
CPUClass *cc;
|
||||
const char *p;
|
||||
uint32_t thread;
|
||||
uint32_t pid, tid;
|
||||
int ch, reg_size, type, res;
|
||||
uint8_t mem_buf[MAX_PACKET_LENGTH];
|
||||
char buf[sizeof(mem_buf) + 1 /* trailing NUL */];
|
||||
char thread_id[16];
|
||||
uint8_t *registers;
|
||||
target_ulong addr, len;
|
||||
GDBThreadIdKind thread_kind;
|
||||
|
||||
trace_gdbstub_io_command(line_buf);
|
||||
|
||||
@ -1291,12 +1399,18 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
|
||||
break;
|
||||
case 'H':
|
||||
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");
|
||||
break;
|
||||
}
|
||||
cpu = find_cpu(thread);
|
||||
cpu = gdb_get_cpu(s, pid, tid);
|
||||
if (cpu == NULL) {
|
||||
put_packet(s, "E22");
|
||||
break;
|
||||
@ -1316,8 +1430,12 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
|
||||
}
|
||||
break;
|
||||
case 'T':
|
||||
thread = strtoull(p, (char **)&p, 16);
|
||||
cpu = find_cpu(thread);
|
||||
thread_kind = read_thread_id(p, &p, &pid, &tid);
|
||||
if (thread_kind == GDB_READ_THREAD_ERR) {
|
||||
put_packet(s, "E22");
|
||||
break;
|
||||
}
|
||||
cpu = gdb_get_cpu(s, pid, tid);
|
||||
|
||||
if (cpu != NULL) {
|
||||
put_packet(s, "OK");
|
||||
|
Loading…
Reference in New Issue
Block a user