Commit 7d8c87da authored by Luc Michel's avatar Luc Michel Committed by Peter Maydell
Browse files

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: default avatarLuc Michel <luc.michel@greensocs.com>
Reviewed-by: default avatarPhilippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: default avatarEdgar E. Iglesias <edgar.iglesias@xilinx.com>
Acked-by: default avatarAlistair Francis <alistair.francis@wdc.com>
Message-id: 20181207090135.7651-5-luc.michel@greensocs.com
Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parent 1a227336
Loading
Loading
Loading
Loading
+136 −18
Original line number Diff line number Diff line
@@ -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");