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: Luc Michel <luc.michel@greensocs.com>
Acked-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
Reviewed-by: Philippe 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: 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 335d52f457
commit 8f46863618

View File

@ -29,6 +29,7 @@
#include "chardev/char-fe.h" #include "chardev/char-fe.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "exec/gdbstub.h" #include "exec/gdbstub.h"
#include "hw/cpu/cluster.h"
#endif #endif
#define MAX_PACKET_LENGTH 4096 #define MAX_PACKET_LENGTH 4096
@ -296,6 +297,11 @@ typedef struct GDBRegisterState {
struct GDBRegisterState *next; struct GDBRegisterState *next;
} GDBRegisterState; } GDBRegisterState;
typedef struct GDBProcess {
uint32_t pid;
bool attached;
} GDBProcess;
enum RSState { enum RSState {
RS_INACTIVE, RS_INACTIVE,
RS_IDLE, RS_IDLE,
@ -324,6 +330,9 @@ typedef struct GDBState {
CharBackend chr; CharBackend chr;
Chardev *mon_chr; Chardev *mon_chr;
#endif #endif
bool multiprocess;
GDBProcess *processes;
int process_num;
char syscall_buf[256]; char syscall_buf[256];
gdb_syscall_complete_cb current_syscall_cb; gdb_syscall_complete_cb current_syscall_cb;
} GDBState; } GDBState;
@ -1751,6 +1760,30 @@ void gdb_exit(CPUArchState *env, int code)
#endif #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 #ifdef CONFIG_USER_ONLY
int int
gdb_handlesig(CPUState *cpu, int sig) gdb_handlesig(CPUState *cpu, int sig)
@ -1848,6 +1881,7 @@ static bool gdb_accept(void)
s = g_malloc0(sizeof(GDBState)); s = g_malloc0(sizeof(GDBState));
s->c_cpu = first_cpu; s->c_cpu = first_cpu;
s->g_cpu = first_cpu; s->g_cpu = first_cpu;
create_default_process(s);
s->fd = fd; s->fd = fd;
gdb_has_xml = false; gdb_has_xml = false;
@ -2004,6 +2038,65 @@ static const TypeInfo char_gdb_type_info = {
.class_init = char_gdb_class_init, .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) int gdbserver_start(const char *device)
{ {
trace_gdbstub_op_start(device); trace_gdbstub_op_start(device);
@ -2060,11 +2153,15 @@ int gdbserver_start(const char *device)
} else { } else {
qemu_chr_fe_deinit(&s->chr, true); qemu_chr_fe_deinit(&s->chr, true);
mon_chr = s->mon_chr; mon_chr = s->mon_chr;
cleanup_processes(s);
memset(s, 0, sizeof(GDBState)); memset(s, 0, sizeof(GDBState));
s->mon_chr = mon_chr; s->mon_chr = mon_chr;
} }
s->c_cpu = first_cpu; s->c_cpu = first_cpu;
s->g_cpu = first_cpu; s->g_cpu = first_cpu;
create_processes(s);
if (chr) { if (chr) {
qemu_chr_fe_init(&s->chr, chr, &error_abort); qemu_chr_fe_init(&s->chr, chr, &error_abort);
qemu_chr_fe_set_handlers(&s->chr, gdb_chr_can_receive, gdb_chr_receive, qemu_chr_fe_set_handlers(&s->chr, gdb_chr_can_receive, gdb_chr_receive,