Commit 8f468636 authored by Luc Michel's avatar Luc Michel Committed by Peter Maydell
Browse files

gdbstub: introduce GDB processes



Add a structure GDBProcess that represents processes from the GDB
semantic point of view.

CPUs can be split into different processes, by grouping them under
different cpu-cluster objects.  Each occurrence of a cpu-cluster object
implies the existence of the corresponding process in the GDB stub. The
GDB process ID is derived from the corresponding cluster ID as follows:

  GDB PID = cluster ID + 1

This is because PIDs -1 and 0 are reserved in GDB and cannot be used by
processes.

A default process is created to handle CPUs that are not in a cluster.
This process gets the PID of the last process PID + 1.

Signed-off-by: default avatarLuc Michel <luc.michel@greensocs.com>
Acked-by: default avatarAlistair Francis <alistair.francis@wdc.com>
Reviewed-by: default avatarEdgar E. Iglesias <edgar.iglesias@xilinx.com>
Reviewed-by: default avatarPhilippe Mathieu-Daudé <philmd@redhat.com>
Message-id: 20181207090135.7651-3-luc.michel@greensocs.com
[PMM: fixed checkpatch nit about block comment style]
Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parent 335d52f4
Loading
Loading
Loading
Loading
+97 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include "chardev/char-fe.h"
#include "sysemu/sysemu.h"
#include "exec/gdbstub.h"
#include "hw/cpu/cluster.h"
#endif

#define MAX_PACKET_LENGTH 4096
@@ -296,6 +297,11 @@ typedef struct GDBRegisterState {
    struct GDBRegisterState *next;
} GDBRegisterState;

typedef struct GDBProcess {
    uint32_t pid;
    bool attached;
} GDBProcess;

enum RSState {
    RS_INACTIVE,
    RS_IDLE,
@@ -324,6 +330,9 @@ typedef struct GDBState {
    CharBackend chr;
    Chardev *mon_chr;
#endif
    bool multiprocess;
    GDBProcess *processes;
    int process_num;
    char syscall_buf[256];
    gdb_syscall_complete_cb current_syscall_cb;
} GDBState;
@@ -1751,6 +1760,30 @@ void gdb_exit(CPUArchState *env, int code)
#endif
}

/*
 * Create the process that will contain all the "orphan" CPUs (that are not
 * part of a CPU cluster). Note that if this process contains no CPUs, it won't
 * be attachable and thus will be invisible to the user.
 */
static void create_default_process(GDBState *s)
{
    GDBProcess *process;
    int max_pid = 0;

    if (s->process_num) {
        max_pid = s->processes[s->process_num - 1].pid;
    }

    s->processes = g_renew(GDBProcess, s->processes, ++s->process_num);
    process = &s->processes[s->process_num - 1];

    /* We need an available PID slot for this process */
    assert(max_pid < UINT32_MAX);

    process->pid = max_pid + 1;
    process->attached = false;
}

#ifdef CONFIG_USER_ONLY
int
gdb_handlesig(CPUState *cpu, int sig)
@@ -1848,6 +1881,7 @@ static bool gdb_accept(void)
    s = g_malloc0(sizeof(GDBState));
    s->c_cpu = first_cpu;
    s->g_cpu = first_cpu;
    create_default_process(s);
    s->fd = fd;
    gdb_has_xml = false;

@@ -2004,6 +2038,65 @@ static const TypeInfo char_gdb_type_info = {
    .class_init = char_gdb_class_init,
};

static int find_cpu_clusters(Object *child, void *opaque)
{
    if (object_dynamic_cast(child, TYPE_CPU_CLUSTER)) {
        GDBState *s = (GDBState *) opaque;
        CPUClusterState *cluster = CPU_CLUSTER(child);
        GDBProcess *process;

        s->processes = g_renew(GDBProcess, s->processes, ++s->process_num);

        process = &s->processes[s->process_num - 1];

        /*
         * GDB process IDs -1 and 0 are reserved. To avoid subtle errors at
         * runtime, we enforce here that the machine does not use a cluster ID
         * that would lead to PID 0.
         */
        assert(cluster->cluster_id != UINT32_MAX);
        process->pid = cluster->cluster_id + 1;
        process->attached = false;

        return 0;
    }

    return object_child_foreach(child, find_cpu_clusters, opaque);
}

static int pid_order(const void *a, const void *b)
{
    GDBProcess *pa = (GDBProcess *) a;
    GDBProcess *pb = (GDBProcess *) b;

    if (pa->pid < pb->pid) {
        return -1;
    } else if (pa->pid > pb->pid) {
        return 1;
    } else {
        return 0;
    }
}

static void create_processes(GDBState *s)
{
    object_child_foreach(object_get_root(), find_cpu_clusters, s);

    if (s->processes) {
        /* Sort by PID */
        qsort(s->processes, s->process_num, sizeof(s->processes[0]), pid_order);
    }

    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)
{
    trace_gdbstub_op_start(device);
@@ -2060,11 +2153,15 @@ int gdbserver_start(const char *device)
    } else {
        qemu_chr_fe_deinit(&s->chr, true);
        mon_chr = s->mon_chr;
        cleanup_processes(s);
        memset(s, 0, sizeof(GDBState));
        s->mon_chr = mon_chr;
    }
    s->c_cpu = first_cpu;
    s->g_cpu = first_cpu;

    create_processes(s);

    if (chr) {
        qemu_chr_fe_init(&s->chr, chr, &error_abort);
        qemu_chr_fe_set_handlers(&s->chr, gdb_chr_can_receive, gdb_chr_receive,