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 "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,
|
||||||
|
Loading…
Reference in New Issue
Block a user