mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-23 11:39:53 +00:00
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:
parent
335d52f457
commit
8f46863618
97
gdbstub.c
97
gdbstub.c
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user