X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=gdbstub.c;h=bc774ae992588b9659db7e5b5e963bbae82a1d0c;hb=19e8ff485a25001f48d7cbfaed24663dae001df7;hp=1ba7aa6a28a30b40680dc87681ecc00d20c2c406;hpb=8dbbe9ac7f9c838d80fdf06b133daa12ffc3f264;p=mirror_qemu.git diff --git a/gdbstub.c b/gdbstub.c index 1ba7aa6a28..bc774ae992 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -300,6 +300,8 @@ typedef struct GDBRegisterState { typedef struct GDBProcess { uint32_t pid; bool attached; + + char target_xml[1024]; } GDBProcess; enum RSState { @@ -642,50 +644,12 @@ static int memtox(char *buf, const char *mem, int len) static uint32_t gdb_get_cpu_pid(const GDBState *s, CPUState *cpu) { -#ifndef CONFIG_USER_ONLY - gchar *path, *name = NULL; - Object *obj; - CPUClusterState *cluster; - uint32_t ret; - - path = object_get_canonical_path(OBJECT(cpu)); - - if (path == NULL) { - /* Return the default process' PID */ - ret = s->processes[s->process_num - 1].pid; - goto out; - } - - name = object_get_canonical_path_component(OBJECT(cpu)); - assert(name != NULL); - - /* - * Retrieve the CPU parent path by removing the last '/' and the CPU name - * from the CPU canonical path. - */ - path[strlen(path) - strlen(name) - 1] = '\0'; - - obj = object_resolve_path_type(path, TYPE_CPU_CLUSTER, NULL); - - if (obj == NULL) { + /* TODO: In user mode, we should use the task state PID */ + if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) { /* Return the default process' PID */ - ret = s->processes[s->process_num - 1].pid; - goto out; + return s->processes[s->process_num - 1].pid; } - - cluster = CPU_CLUSTER(obj); - ret = cluster->cluster_id + 1; - -out: - g_free(name); - g_free(path); - - return ret; - -#else - /* TODO: In user mode, we should use the task state PID */ - return s->processes[s->process_num - 1].pid; -#endif + return cpu->cluster_index + 1; } static GDBProcess *gdb_get_process(const GDBState *s, uint32_t pid) @@ -754,35 +718,6 @@ static CPUState *gdb_next_cpu_in_process(const GDBState *s, CPUState *cpu) return cpu; } -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; -} - /* Return the cpu following @cpu, while ignoring unattached processes. */ static CPUState *gdb_next_attached_cpu(const GDBState *s, CPUState *cpu) { @@ -812,13 +747,57 @@ static CPUState *gdb_first_attached_cpu(const GDBState *s) return cpu; } -static const char *get_feature_xml(const char *p, const char **newp, - CPUClass *cc) +static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) +{ + GDBProcess *process; + CPUState *cpu; + + if (!pid && !tid) { + /* 0 means any process/thread, we take the first attached one */ + return gdb_first_attached_cpu(s); + } else if (pid && !tid) { + /* any thread in a specific process */ + process = gdb_get_process(s, pid); + + if (process == NULL) { + return NULL; + } + + if (!process->attached) { + return NULL; + } + + return get_first_cpu_in_process(s, process); + } else { + /* a specific thread */ + cpu = find_cpu(tid); + + if (cpu == NULL) { + return NULL; + } + + process = gdb_get_cpu_process(s, cpu); + + if (pid && process->pid != pid) { + return NULL; + } + + if (!process->attached) { + return NULL; + } + + return cpu; + } +} + +static const char *get_feature_xml(const GDBState *s, const char *p, + const char **newp, GDBProcess *process) { size_t len; int i; const char *name; - static char target_xml[1024]; + CPUState *cpu = get_first_cpu_in_process(s, process); + CPUClass *cc = CPU_GET_CLASS(cpu); len = 0; while (p[len] && p[len] != ':') @@ -827,36 +806,37 @@ static const char *get_feature_xml(const char *p, const char **newp, name = NULL; if (strncmp(p, "target.xml", len) == 0) { + char *buf = process->target_xml; + const size_t buf_sz = sizeof(process->target_xml); + /* Generate the XML description for this CPU. */ - if (!target_xml[0]) { + if (!buf[0]) { GDBRegisterState *r; - CPUState *cpu = first_cpu; - pstrcat(target_xml, sizeof(target_xml), + pstrcat(buf, buf_sz, "" "" ""); if (cc->gdb_arch_name) { gchar *arch = cc->gdb_arch_name(cpu); - pstrcat(target_xml, sizeof(target_xml), ""); - pstrcat(target_xml, sizeof(target_xml), arch); - pstrcat(target_xml, sizeof(target_xml), ""); + pstrcat(buf, buf_sz, ""); + pstrcat(buf, buf_sz, arch); + pstrcat(buf, buf_sz, ""); g_free(arch); } - pstrcat(target_xml, sizeof(target_xml), "gdb_core_xml_file); - pstrcat(target_xml, sizeof(target_xml), "\"/>"); + pstrcat(buf, buf_sz, "gdb_core_xml_file); + pstrcat(buf, buf_sz, "\"/>"); for (r = cpu->gdb_regs; r; r = r->next) { - pstrcat(target_xml, sizeof(target_xml), "xml); - pstrcat(target_xml, sizeof(target_xml), "\"/>"); + pstrcat(buf, buf_sz, "xml); + pstrcat(buf, buf_sz, "\"/>"); } - pstrcat(target_xml, sizeof(target_xml), ""); + pstrcat(buf, buf_sz, ""); } - return target_xml; + return buf; } if (cc->gdb_get_dynamic_xml) { - CPUState *cpu = first_cpu; char *xmlname = g_strndup(p, len); const char *xml = cc->gdb_get_dynamic_xml(cpu, xmlname); @@ -1043,6 +1023,24 @@ static int gdb_breakpoint_remove(target_ulong addr, target_ulong len, int type) } } +static inline void gdb_cpu_breakpoint_remove_all(CPUState *cpu) +{ + cpu_breakpoint_remove_all(cpu, BP_GDB); +#ifndef CONFIG_USER_ONLY + cpu_watchpoint_remove_all(cpu, BP_GDB); +#endif +} + +static void gdb_process_breakpoint_remove_all(const GDBState *s, GDBProcess *p) +{ + CPUState *cpu = get_first_cpu_in_process(s, p); + + while (cpu) { + gdb_cpu_breakpoint_remove_all(cpu); + cpu = gdb_next_cpu_in_process(s, cpu); + } +} + static void gdb_breakpoint_remove_all(void) { CPUState *cpu; @@ -1053,10 +1051,7 @@ static void gdb_breakpoint_remove_all(void) } CPU_FOREACH(cpu) { - cpu_breakpoint_remove_all(cpu, BP_GDB); -#ifndef CONFIG_USER_ONLY - cpu_watchpoint_remove_all(cpu, BP_GDB); -#endif + gdb_cpu_breakpoint_remove_all(cpu); } } @@ -1266,9 +1261,9 @@ out: static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; + GDBProcess *process; 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]; @@ -1283,6 +1278,9 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) p = line_buf; ch = *p++; switch(ch) { + case '!': + put_packet(s, "OK"); + break; case '?': /* TODO: Make this return the correct value for user-mode. */ snprintf(buf, sizeof(buf), "T%02xthread:%s;", GDB_SIGNAL_TRAP, @@ -1326,6 +1324,46 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) goto unknown_command; } break; + } else if (strncmp(p, "Attach;", 7) == 0) { + unsigned long pid; + + p += 7; + + if (qemu_strtoul(p, &p, 16, &pid)) { + put_packet(s, "E22"); + break; + } + + process = gdb_get_process(s, pid); + + if (process == NULL) { + put_packet(s, "E22"); + break; + } + + cpu = get_first_cpu_in_process(s, process); + + if (cpu == NULL) { + /* Refuse to attach an empty process */ + put_packet(s, "E22"); + break; + } + + process->attached = true; + + s->g_cpu = cpu; + s->c_cpu = cpu; + + snprintf(buf, sizeof(buf), "T%02xthread:%s;", GDB_SIGNAL_TRAP, + gdb_fmt_thread_id(s, cpu, thread_id, sizeof(thread_id))); + + put_packet(s, buf); + break; + } else if (strncmp(p, "Kill;", 5) == 0) { + /* Kill the target */ + put_packet(s, "OK"); + error_report("QEMU: Terminated via GDBstub"); + exit(0); } else { goto unknown_command; } @@ -1335,9 +1373,40 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) exit(0); case 'D': /* Detach packet */ - gdb_breakpoint_remove_all(); - gdb_syscall_mode = GDB_SYS_DISABLED; - gdb_continue(s); + pid = 1; + + if (s->multiprocess) { + unsigned long lpid; + if (*p != ';') { + put_packet(s, "E22"); + break; + } + + if (qemu_strtoul(p + 1, &p, 16, &lpid)) { + put_packet(s, "E22"); + break; + } + + pid = lpid; + } + + process = gdb_get_process(s, pid); + gdb_process_breakpoint_remove_all(s, process); + process->attached = false; + + if (pid == gdb_get_cpu_pid(s, s->c_cpu)) { + s->c_cpu = gdb_first_attached_cpu(s); + } + + if (pid == gdb_get_cpu_pid(s, s->g_cpu)) { + s->g_cpu = gdb_first_attached_cpu(s); + } + + if (s->c_cpu == NULL) { + /* No more process attached */ + gdb_syscall_mode = GDB_SYS_DISABLED; + gdb_continue(s); + } put_packet(s, "OK"); break; case 's': @@ -1565,26 +1634,44 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) put_packet(s, buf); break; } else if (strcmp(p,"fThreadInfo") == 0) { - s->query_cpu = first_cpu; + s->query_cpu = gdb_first_attached_cpu(s); goto report_cpuinfo; } else if (strcmp(p,"sThreadInfo") == 0) { report_cpuinfo: if (s->query_cpu) { - snprintf(buf, sizeof(buf), "m%x", cpu_gdb_index(s->query_cpu)); + snprintf(buf, sizeof(buf), "m%s", + gdb_fmt_thread_id(s, s->query_cpu, + thread_id, sizeof(thread_id))); put_packet(s, buf); - s->query_cpu = CPU_NEXT(s->query_cpu); + s->query_cpu = gdb_next_attached_cpu(s, s->query_cpu); } else put_packet(s, "l"); break; } else if (strncmp(p,"ThreadExtraInfo,", 16) == 0) { - thread = strtoull(p+16, (char **)&p, 16); - cpu = find_cpu(thread); + if (read_thread_id(p + 16, &p, &pid, &tid) == GDB_READ_THREAD_ERR) { + put_packet(s, "E22"); + break; + } + cpu = gdb_get_cpu(s, pid, tid); if (cpu != NULL) { cpu_synchronize_state(cpu); - /* memtohex() doubles the required space */ - len = snprintf((char *)mem_buf, sizeof(buf) / 2, - "CPU#%d [%s]", cpu->cpu_index, - cpu->halted ? "halted " : "running"); + + if (s->multiprocess && (s->process_num > 1)) { + /* Print the CPU model and name in multiprocess mode */ + ObjectClass *oc = object_get_class(OBJECT(cpu)); + const char *cpu_model = object_class_get_name(oc); + char *cpu_name = + object_get_canonical_path_component(OBJECT(cpu)); + len = snprintf((char *)mem_buf, sizeof(buf) / 2, + "%s %s [%s]", cpu_model, cpu_name, + cpu->halted ? "halted " : "running"); + g_free(cpu_name); + } else { + /* memtohex() doubles the required space */ + len = snprintf((char *)mem_buf, sizeof(buf) / 2, + "CPU#%d [%s]", cpu->cpu_index, + cpu->halted ? "halted " : "running"); + } trace_gdbstub_op_extra_info((char *)mem_buf); memtohex(buf, mem_buf, len); put_packet(s, buf); @@ -1626,6 +1713,12 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) if (cc->gdb_core_xml_file != NULL) { pstrcat(buf, sizeof(buf), ";qXfer:features:read+"); } + + if (strstr(p, "multiprocess+")) { + s->multiprocess = true; + } + pstrcat(buf, sizeof(buf), ";multiprocess+"); + put_packet(s, buf); break; } @@ -1633,14 +1726,15 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) const char *xml; target_ulong total_len; - cc = CPU_GET_CLASS(first_cpu); + process = gdb_get_cpu_process(s, s->g_cpu); + cc = CPU_GET_CLASS(s->g_cpu); if (cc->gdb_core_xml_file == NULL) { goto unknown_command; } gdb_has_xml = true; p += 19; - xml = get_feature_xml(p, &p, cc); + xml = get_feature_xml(s, p, &p, process); if (!xml) { snprintf(buf, sizeof(buf), "E00"); put_packet(s, buf); @@ -1691,6 +1785,16 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) void gdb_set_stop_cpu(CPUState *cpu) { + GDBProcess *p = gdb_get_cpu_process(gdbserver_state, cpu); + + if (!p->attached) { + /* + * Having a stop CPU corresponding to a process that is not attached + * confuses GDB. So we ignore the request. + */ + return; + } + gdbserver_state->c_cpu = cpu; gdbserver_state->g_cpu = cpu; } @@ -1701,6 +1805,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) GDBState *s = gdbserver_state; CPUState *cpu = s->c_cpu; char buf[256]; + char thread_id[16]; const char *type; int ret; @@ -1712,6 +1817,14 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) put_packet(s, s->syscall_buf); return; } + + if (cpu == NULL) { + /* No process attached */ + return; + } + + gdb_fmt_thread_id(s, cpu, thread_id, sizeof(thread_id)); + switch (state) { case RUN_STATE_DEBUG: if (cpu->watchpoint_hit) { @@ -1729,8 +1842,8 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) trace_gdbstub_hit_watchpoint(type, cpu_gdb_index(cpu), (target_ulong)cpu->watchpoint_hit->vaddr); snprintf(buf, sizeof(buf), - "T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";", - GDB_SIGNAL_TRAP, cpu_gdb_index(cpu), type, + "T%02xthread:%s;%swatch:" TARGET_FMT_lx ";", + GDB_SIGNAL_TRAP, thread_id, type, (target_ulong)cpu->watchpoint_hit->vaddr); cpu->watchpoint_hit = NULL; goto send_packet; @@ -1772,7 +1885,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) break; } gdb_set_stop_cpu(cpu); - snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, cpu_gdb_index(cpu)); + snprintf(buf, sizeof(buf), "T%02xthread:%s;", ret, thread_id); send_packet: put_packet(s, buf); @@ -2053,6 +2166,7 @@ static void create_default_process(GDBState *s) process->pid = max_pid + 1; process->attached = false; + process->target_xml[0] = '\0'; } #ifdef CONFIG_USER_ONLY @@ -2150,9 +2264,10 @@ 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->processes[0].attached = true; + s->c_cpu = gdb_first_attached_cpu(s); + s->g_cpu = s->c_cpu; s->fd = fd; gdb_has_xml = false; @@ -2238,8 +2353,19 @@ static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size) static void gdb_chr_event(void *opaque, int event) { + int i; + GDBState *s = (GDBState *) opaque; + switch (event) { case CHR_EVENT_OPENED: + /* Start with first process attached, others detached */ + for (i = 0; i < s->process_num; i++) { + s->processes[i].attached = !i; + } + + s->c_cpu = gdb_first_attached_cpu(s); + s->g_cpu = s->c_cpu; + vm_stop(RUN_STATE_PAUSED); gdb_has_xml = false; break; @@ -2328,6 +2454,7 @@ static int find_cpu_clusters(Object *child, void *opaque) assert(cluster->cluster_id != UINT32_MAX); process->pid = cluster->cluster_id + 1; process->attached = false; + process->target_xml[0] = '\0'; return 0; } @@ -2405,7 +2532,7 @@ int gdbserver_start(const char *device) * FIXME: it's a bit weird to allow using a mux chardev here * and implicitly setup a monitor. We may want to break this. */ - chr = qemu_chr_new_noreplay("gdb", device, true); + chr = qemu_chr_new_noreplay("gdb", device, true, NULL); if (!chr) return -1; } @@ -2419,7 +2546,7 @@ int gdbserver_start(const char *device) /* Initialize a monitor terminal for gdb */ mon_chr = qemu_chardev_new(NULL, TYPE_CHARDEV_GDB, - NULL, &error_abort); + NULL, NULL, &error_abort); monitor_init(mon_chr, 0); } else { qemu_chr_fe_deinit(&s->chr, true); @@ -2428,15 +2555,13 @@ int gdbserver_start(const char *device) 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, - gdb_chr_event, NULL, NULL, NULL, true); + gdb_chr_event, NULL, s, NULL, true); } s->state = chr ? RS_IDLE : RS_INACTIVE; s->mon_chr = mon_chr;