gdbstub: move str_buf to GDBState and use GString

Rather than having a static buffer replace str_buf with a GString
which we know can grow on demand. Convert the internal functions to
take a GString instead of a char * and length.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Damien Hedde <damien.hedde@greensocs.com>
Tested-by: Damien Hedde <damien.hedde@greensocs.com>
Message-Id: <20200316172155.971-9-alex.bennee@linaro.org>
This commit is contained in:
Alex Bennée 2020-03-16 17:21:35 +00:00
parent a346af3e35
commit 308f9e88b2
1 changed files with 91 additions and 106 deletions

197
gdbstub.c
View File

@ -366,6 +366,7 @@ typedef struct GDBState {
int process_num; 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;
GString *str_buf;
} GDBState; } GDBState;
/* By default use no IRQs and no timers while single stepping so as to /* By default use no IRQs and no timers while single stepping so as to
@ -380,6 +381,7 @@ static void init_gdbserver_state(void)
g_assert(!gdbserver_state.init); g_assert(!gdbserver_state.init);
memset(&gdbserver_state, 0, sizeof(GDBState)); memset(&gdbserver_state, 0, sizeof(GDBState));
gdbserver_state.init = true; gdbserver_state.init = true;
gdbserver_state.str_buf = g_string_new(NULL);
} }
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
@ -563,17 +565,15 @@ static inline int tohex(int v)
} }
/* writes 2*len+1 bytes in buf */ /* writes 2*len+1 bytes in buf */
static void memtohex(char *buf, const uint8_t *mem, int len) static void memtohex(GString *buf, const uint8_t *mem, int len)
{ {
int i, c; int i, c;
char *q;
q = buf;
for(i = 0; i < len; i++) { for(i = 0; i < len; i++) {
c = mem[i]; c = mem[i];
*q++ = tohex(c >> 4); g_string_append_c(buf, tohex(c >> 4));
*q++ = tohex(c & 0xf); g_string_append_c(buf, tohex(c & 0xf));
} }
*q = '\0'; g_string_append_c(buf, '\0');
} }
static void hextomem(uint8_t *mem, const char *buf, int len) static void hextomem(uint8_t *mem, const char *buf, int len)
@ -667,25 +667,28 @@ static int put_packet(const char *buf)
return put_packet_binary(buf, strlen(buf), false); return put_packet_binary(buf, strlen(buf), false);
} }
/* Encode data using the encoding for 'x' packets. */ static void put_strbuf(void)
static int memtox(char *buf, const char *mem, int len) {
put_packet(gdbserver_state.str_buf->str);
}
/* Encode data using the encoding for 'x' packets. */
static void memtox(GString *buf, const char *mem, int len)
{ {
char *p = buf;
char c; char c;
while (len--) { while (len--) {
c = *(mem++); c = *(mem++);
switch (c) { switch (c) {
case '#': case '$': case '*': case '}': case '#': case '$': case '*': case '}':
*(p++) = '}'; g_string_append_c(buf, '}');
*(p++) = c ^ 0x20; g_string_append_c(buf, c ^ 0x20);
break; break;
default: default:
*(p++) = c; g_string_append_c(buf, c);
break; break;
} }
} }
return p - buf;
} }
static uint32_t gdb_get_cpu_pid(CPUState *cpu) static uint32_t gdb_get_cpu_pid(CPUState *cpu)
@ -1109,16 +1112,14 @@ static void gdb_set_cpu_pc(target_ulong pc)
cpu_set_pc(cpu, pc); cpu_set_pc(cpu, pc);
} }
static char *gdb_fmt_thread_id(CPUState *cpu, char *buf, size_t buf_size) static void gdb_append_thread_id(CPUState *cpu, GString *buf)
{ {
if (gdbserver_state.multiprocess) { if (gdbserver_state.multiprocess) {
snprintf(buf, buf_size, "p%02x.%02x", g_string_append_printf(buf, "p%02x.%02x",
gdb_get_cpu_pid(cpu), cpu_gdb_index(cpu)); gdb_get_cpu_pid(cpu), cpu_gdb_index(cpu));
} else { } else {
snprintf(buf, buf_size, "%02x", cpu_gdb_index(cpu)); g_string_append_printf(buf, "%02x", cpu_gdb_index(cpu));
} }
return buf;
} }
typedef enum GDBThreadIdKind { typedef enum GDBThreadIdKind {
@ -1412,7 +1413,6 @@ typedef struct GdbCmdContext {
GdbCmdVariant *params; GdbCmdVariant *params;
int num_params; int num_params;
uint8_t mem_buf[MAX_PACKET_LENGTH]; uint8_t mem_buf[MAX_PACKET_LENGTH];
char str_buf[MAX_PACKET_LENGTH + 1];
} GdbCmdContext; } GdbCmdContext;
typedef void (*GdbCmdHandler)(GdbCmdContext *gdb_ctx, void *user_ctx); typedef void (*GdbCmdHandler)(GdbCmdContext *gdb_ctx, void *user_ctx);
@ -1502,6 +1502,8 @@ static void run_cmd_parser(const char *data, const GdbCmdParseEntry *cmd)
return; return;
} }
g_string_set_size(gdbserver_state.str_buf, 0);
/* In case there was an error during the command parsing we must /* In case there was an error during the command parsing we must
* send a NULL packet to indicate the command is not supported */ * send a NULL packet to indicate the command is not supported */
if (process_string_cmd(NULL, data, cmd, 1)) { if (process_string_cmd(NULL, data, cmd, 1)) {
@ -1740,8 +1742,8 @@ static void handle_get_reg(GdbCmdContext *gdb_ctx, void *user_ctx)
return; return;
} }
memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, reg_size); memtohex(gdbserver_state.str_buf, gdb_ctx->mem_buf, reg_size);
put_packet(gdb_ctx->str_buf); put_strbuf();
} }
static void handle_write_mem(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_write_mem(GdbCmdContext *gdb_ctx, void *user_ctx)
@ -1789,8 +1791,8 @@ static void handle_read_mem(GdbCmdContext *gdb_ctx, void *user_ctx)
return; return;
} }
memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, gdb_ctx->params[1].val_ull); memtohex(gdbserver_state.str_buf, gdb_ctx->mem_buf, gdb_ctx->params[1].val_ull);
put_packet(gdb_ctx->str_buf); put_strbuf();
} }
static void handle_write_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_write_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx)
@ -1827,8 +1829,8 @@ static void handle_read_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx)
addr); addr);
} }
memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, len); memtohex(gdbserver_state.str_buf, gdb_ctx->mem_buf, len);
put_packet(gdb_ctx->str_buf); put_strbuf();
} }
static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx)
@ -1889,9 +1891,8 @@ static void handle_v_attach(GdbCmdContext *gdb_ctx, void *user_ctx)
{ {
GDBProcess *process; GDBProcess *process;
CPUState *cpu; CPUState *cpu;
char thread_id[16];
pstrcpy(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "E22"); g_string_assign(gdbserver_state.str_buf, "E22");
if (!gdb_ctx->num_params) { if (!gdb_ctx->num_params) {
goto cleanup; goto cleanup;
} }
@ -1910,11 +1911,11 @@ static void handle_v_attach(GdbCmdContext *gdb_ctx, void *user_ctx)
gdbserver_state.g_cpu = cpu; gdbserver_state.g_cpu = cpu;
gdbserver_state.c_cpu = cpu; gdbserver_state.c_cpu = cpu;
gdb_fmt_thread_id(cpu, thread_id, sizeof(thread_id)); g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP);
snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "T%02xthread:%s;", gdb_append_thread_id(cpu, gdbserver_state.str_buf);
GDB_SIGNAL_TRAP, thread_id); g_string_append_c(gdbserver_state.str_buf, ';');
cleanup: cleanup:
put_packet(gdb_ctx->str_buf); put_strbuf();
} }
static void handle_v_kill(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_v_kill(GdbCmdContext *gdb_ctx, void *user_ctx)
@ -1966,10 +1967,9 @@ static void handle_v_commands(GdbCmdContext *gdb_ctx, void *user_ctx)
static void handle_query_qemu_sstepbits(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_qemu_sstepbits(GdbCmdContext *gdb_ctx, void *user_ctx)
{ {
snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), g_string_printf(gdbserver_state.str_buf, "ENABLE=%x,NOIRQ=%x,NOTIMER=%x",
"ENABLE=%x,NOIRQ=%x,NOTIMER=%x", SSTEP_ENABLE, SSTEP_ENABLE, SSTEP_NOIRQ, SSTEP_NOTIMER);
SSTEP_NOIRQ, SSTEP_NOTIMER); put_strbuf();
put_packet(gdb_ctx->str_buf);
} }
static void handle_set_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_set_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx)
@ -1984,15 +1984,14 @@ static void handle_set_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx)
static void handle_query_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx)
{ {
snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "0x%x", sstep_flags); g_string_printf(gdbserver_state.str_buf, "0x%x", sstep_flags);
put_packet(gdb_ctx->str_buf); put_strbuf();
} }
static void handle_query_curr_tid(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_curr_tid(GdbCmdContext *gdb_ctx, void *user_ctx)
{ {
CPUState *cpu; CPUState *cpu;
GDBProcess *process; GDBProcess *process;
char thread_id[16];
/* /*
* "Current thread" remains vague in the spec, so always return * "Current thread" remains vague in the spec, so always return
@ -2001,24 +2000,21 @@ static void handle_query_curr_tid(GdbCmdContext *gdb_ctx, void *user_ctx)
*/ */
process = gdb_get_cpu_process(gdbserver_state.g_cpu); process = gdb_get_cpu_process(gdbserver_state.g_cpu);
cpu = get_first_cpu_in_process(process); cpu = get_first_cpu_in_process(process);
gdb_fmt_thread_id(cpu, thread_id, sizeof(thread_id)); g_string_assign(gdbserver_state.str_buf, "QC");
snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "QC%s", thread_id); gdb_append_thread_id(cpu, gdbserver_state.str_buf);
put_packet(gdb_ctx->str_buf); put_strbuf();
} }
static void handle_query_threads(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_threads(GdbCmdContext *gdb_ctx, void *user_ctx)
{ {
char thread_id[16];
if (!gdbserver_state.query_cpu) { if (!gdbserver_state.query_cpu) {
put_packet("l"); put_packet("l");
return; return;
} }
gdb_fmt_thread_id(gdbserver_state.query_cpu, thread_id, g_string_assign(gdbserver_state.str_buf, "m");
sizeof(thread_id)); gdb_append_thread_id(gdbserver_state.query_cpu, gdbserver_state.str_buf);
snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "m%s", thread_id); put_strbuf();
put_packet(gdb_ctx->str_buf);
gdbserver_state.query_cpu = gdb_next_attached_cpu(gdbserver_state.query_cpu); gdbserver_state.query_cpu = gdb_next_attached_cpu(gdbserver_state.query_cpu);
} }
@ -2030,8 +2026,8 @@ static void handle_query_first_threads(GdbCmdContext *gdb_ctx, void *user_ctx)
static void handle_query_thread_extra(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_thread_extra(GdbCmdContext *gdb_ctx, void *user_ctx)
{ {
g_autoptr(GString) rs = g_string_new(NULL);
CPUState *cpu; CPUState *cpu;
int len;
if (!gdb_ctx->num_params || if (!gdb_ctx->num_params ||
gdb_ctx->params[0].thread_id.kind == GDB_READ_THREAD_ERR) { gdb_ctx->params[0].thread_id.kind == GDB_READ_THREAD_ERR) {
@ -2051,20 +2047,17 @@ static void handle_query_thread_extra(GdbCmdContext *gdb_ctx, void *user_ctx)
/* Print the CPU model and name in multiprocess mode */ /* Print the CPU model and name in multiprocess mode */
ObjectClass *oc = object_get_class(OBJECT(cpu)); ObjectClass *oc = object_get_class(OBJECT(cpu));
const char *cpu_model = object_class_get_name(oc); const char *cpu_model = object_class_get_name(oc);
char *cpu_name = object_get_canonical_path_component(OBJECT(cpu)); g_autofree char *cpu_name;
len = snprintf((char *)gdb_ctx->mem_buf, sizeof(gdb_ctx->str_buf) / 2, cpu_name = object_get_canonical_path_component(OBJECT(cpu));
"%s %s [%s]", cpu_model, cpu_name, g_string_printf(rs, "%s %s [%s]", cpu_model, cpu_name,
cpu->halted ? "halted " : "running"); cpu->halted ? "halted " : "running");
g_free(cpu_name);
} else { } else {
/* memtohex() doubles the required space */ g_string_printf(rs, "CPU#%d [%s]", cpu->cpu_index,
len = snprintf((char *)gdb_ctx->mem_buf, sizeof(gdb_ctx->str_buf) / 2,
"CPU#%d [%s]", cpu->cpu_index,
cpu->halted ? "halted " : "running"); cpu->halted ? "halted " : "running");
} }
trace_gdbstub_op_extra_info((char *)gdb_ctx->mem_buf); trace_gdbstub_op_extra_info(rs->str);
memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, len); memtohex(gdbserver_state.str_buf, (uint8_t *)rs->str, rs->len);
put_packet(gdb_ctx->str_buf); put_strbuf();
} }
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
@ -2073,13 +2066,14 @@ static void handle_query_offsets(GdbCmdContext *gdb_ctx, void *user_ctx)
TaskState *ts; TaskState *ts;
ts = gdbserver_state.c_cpu->opaque; ts = gdbserver_state.c_cpu->opaque;
snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), g_string_printf(gdbserver_state.str_buf,
"Text=" TARGET_ABI_FMT_lx ";Data=" TARGET_ABI_FMT_lx "Text=" TARGET_ABI_FMT_lx
";Bss=" TARGET_ABI_FMT_lx, ";Data=" TARGET_ABI_FMT_lx
ts->info->code_offset, ";Bss=" TARGET_ABI_FMT_lx,
ts->info->data_offset, ts->info->code_offset,
ts->info->data_offset); ts->info->data_offset,
put_packet(gdb_ctx->str_buf); ts->info->data_offset);
put_strbuf();
} }
#else #else
static void handle_query_rcmd(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_rcmd(GdbCmdContext *gdb_ctx, void *user_ctx)
@ -2110,12 +2104,10 @@ static void handle_query_supported(GdbCmdContext *gdb_ctx, void *user_ctx)
{ {
CPUClass *cc; CPUClass *cc;
snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "PacketSize=%x", g_string_printf(gdbserver_state.str_buf, "PacketSize=%x", MAX_PACKET_LENGTH);
MAX_PACKET_LENGTH);
cc = CPU_GET_CLASS(first_cpu); cc = CPU_GET_CLASS(first_cpu);
if (cc->gdb_core_xml_file) { if (cc->gdb_core_xml_file) {
pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+");
";qXfer:features:read+");
} }
if (gdb_ctx->num_params && if (gdb_ctx->num_params &&
@ -2123,8 +2115,8 @@ static void handle_query_supported(GdbCmdContext *gdb_ctx, void *user_ctx)
gdbserver_state.multiprocess = true; gdbserver_state.multiprocess = true;
} }
pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), ";multiprocess+"); g_string_append(gdbserver_state.str_buf, ";multiprocess+");
put_packet(gdb_ctx->str_buf); put_strbuf();
} }
static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx)
@ -2168,14 +2160,15 @@ static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx)
} }
if (len < total_len - addr) { if (len < total_len - addr) {
gdb_ctx->str_buf[0] = 'm'; g_string_assign(gdbserver_state.str_buf, "m");
len = memtox(gdb_ctx->str_buf + 1, xml + addr, len); memtox(gdbserver_state.str_buf, xml + addr, len);
} else { } else {
gdb_ctx->str_buf[0] = 'l'; g_string_assign(gdbserver_state.str_buf, "l");
len = memtox(gdb_ctx->str_buf + 1, xml + addr, total_len - addr); memtox(gdbserver_state.str_buf, xml + addr, total_len - addr);
} }
put_packet_binary(gdb_ctx->str_buf, len + 1, true); put_packet_binary(gdbserver_state.str_buf->str,
gdbserver_state.str_buf->len, true);
} }
static void handle_query_attached(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_attached(GdbCmdContext *gdb_ctx, void *user_ctx)
@ -2185,19 +2178,19 @@ static void handle_query_attached(GdbCmdContext *gdb_ctx, void *user_ctx)
static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, void *user_ctx)
{ {
snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "sstepbits;sstep"); g_string_printf(gdbserver_state.str_buf, "sstepbits;sstep");
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), ";PhyMemMode"); g_string_append(gdbserver_state.str_buf, ";PhyMemMode");
#endif #endif
put_packet(gdb_ctx->str_buf); put_strbuf();
} }
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
static void handle_query_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, static void handle_query_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx,
void *user_ctx) void *user_ctx)
{ {
snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "%d", phy_memory_mode); g_string_printf(gdbserver_state.str_buf, "%d", phy_memory_mode);
put_packet(gdb_ctx->str_buf); put_strbuf();
} }
static void handle_set_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_set_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx)
@ -2362,13 +2355,10 @@ static void handle_gen_set(GdbCmdContext *gdb_ctx, void *user_ctx)
static void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx)
{ {
char thread_id[16]; g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP);
gdb_append_thread_id(gdbserver_state.c_cpu, gdbserver_state.str_buf);
gdb_fmt_thread_id(gdbserver_state.c_cpu, thread_id, g_string_append_c(gdbserver_state.str_buf, ';');
sizeof(thread_id)); put_strbuf();
snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "T%02xthread:%s;",
GDB_SIGNAL_TRAP, thread_id);
put_packet(gdb_ctx->str_buf);
/* /*
* Remove all the breakpoints when this query is issued, * Remove all the breakpoints when this query is issued,
* because gdb is doing an initial connect and the state * because gdb is doing an initial connect and the state
@ -2631,8 +2621,8 @@ void gdb_set_stop_cpu(CPUState *cpu)
static void gdb_vm_state_change(void *opaque, int running, RunState state) static void gdb_vm_state_change(void *opaque, int running, RunState state)
{ {
CPUState *cpu = gdbserver_state.c_cpu; CPUState *cpu = gdbserver_state.c_cpu;
char buf[256]; g_autoptr(GString) buf = g_string_new(NULL);
char thread_id[16]; g_autoptr(GString) tid = g_string_new(NULL);
const char *type; const char *type;
int ret; int ret;
@ -2650,7 +2640,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state)
return; return;
} }
gdb_fmt_thread_id(cpu, thread_id, sizeof(thread_id)); gdb_append_thread_id(cpu, tid);
switch (state) { switch (state) {
case RUN_STATE_DEBUG: case RUN_STATE_DEBUG:
@ -2668,10 +2658,9 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state)
} }
trace_gdbstub_hit_watchpoint(type, cpu_gdb_index(cpu), trace_gdbstub_hit_watchpoint(type, cpu_gdb_index(cpu),
(target_ulong)cpu->watchpoint_hit->vaddr); (target_ulong)cpu->watchpoint_hit->vaddr);
snprintf(buf, sizeof(buf), g_string_printf(buf, "T%02xthread:%s;%swatch:" TARGET_FMT_lx ";",
"T%02xthread:%s;%swatch:" TARGET_FMT_lx ";", GDB_SIGNAL_TRAP, tid->str, type,
GDB_SIGNAL_TRAP, thread_id, type, (target_ulong)cpu->watchpoint_hit->vaddr);
(target_ulong)cpu->watchpoint_hit->vaddr);
cpu->watchpoint_hit = NULL; cpu->watchpoint_hit = NULL;
goto send_packet; goto send_packet;
} else { } else {
@ -2712,10 +2701,10 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state)
break; break;
} }
gdb_set_stop_cpu(cpu); gdb_set_stop_cpu(cpu);
snprintf(buf, sizeof(buf), "T%02xthread:%s;", ret, thread_id); g_string_printf(buf, "T%02xthread:%s;", ret, tid->str);
send_packet: send_packet:
put_packet(buf); put_packet(buf->str);
/* disable single step if it was enabled */ /* disable single step if it was enabled */
cpu_single_step(cpu, 0); cpu_single_step(cpu, 0);
@ -3196,13 +3185,9 @@ static void gdb_chr_event(void *opaque, QEMUChrEvent event)
static void gdb_monitor_output(const char *msg, int len) static void gdb_monitor_output(const char *msg, int len)
{ {
char buf[MAX_PACKET_LENGTH]; g_autoptr(GString) buf = g_string_new("O");
memtohex(buf, (uint8_t *)msg, len);
buf[0] = 'O'; put_packet(buf->str);
if (len > (MAX_PACKET_LENGTH/2) - 1)
len = (MAX_PACKET_LENGTH/2) - 1;
memtohex(buf + 1, (uint8_t *)msg, len);
put_packet(buf);
} }
static int gdb_monitor_write(Chardev *chr, const uint8_t *buf, int len) static int gdb_monitor_write(Chardev *chr, const uint8_t *buf, int len)