From: Brendan Hansen Date: Tue, 3 Jan 2023 03:02:28 +0000 (-0600) Subject: added memory read/write to debugger X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=4ac49c0f4378ef4ff5671be24c290c6fe35c6b4e;p=onyx.git added memory read/write to debugger --- diff --git a/interpreter/include/ovm_debug.h b/interpreter/include/ovm_debug.h index 07501a54..4302d086 100644 --- a/interpreter/include/ovm_debug.h +++ b/interpreter/include/ovm_debug.h @@ -343,7 +343,6 @@ typedef struct debug_runtime_value_builder_t { u32 it_type; char *it_name; bool it_has_children; - } debug_runtime_value_builder_t; void debug_runtime_value_build_init(debug_runtime_value_builder_t *builder, bh_allocator alloc); @@ -351,6 +350,7 @@ void debug_runtime_value_build_set_location(debug_runtime_value_builder_t *build void debug_runtime_value_build_descend(debug_runtime_value_builder_t *builder, u32 index); bool debug_runtime_value_build_step(debug_runtime_value_builder_t *builder); void debug_runtime_value_build_string(debug_runtime_value_builder_t *builder); +u32 debug_runtime_value_get_it_addr(debug_runtime_value_builder_t *builder); void debug_runtime_value_build_clear(debug_runtime_value_builder_t *builder); void debug_runtime_value_build_free(debug_runtime_value_builder_t *builder); diff --git a/interpreter/src/debug/debug_runtime_values.c b/interpreter/src/debug/debug_runtime_values.c index a11abcd2..bed7fb85 100644 --- a/interpreter/src/debug/debug_runtime_values.c +++ b/interpreter/src/debug/debug_runtime_values.c @@ -735,6 +735,36 @@ bool debug_runtime_value_build_step(debug_runtime_value_builder_t *builder) { return true; } +u32 debug_runtime_value_get_it_addr(debug_runtime_value_builder_t *builder) { + if (builder->it_loc_kind == debug_sym_loc_register) { + debug_type_info_t *type = &builder->info->types[builder->it_type]; + + if (type->kind == debug_type_kind_slice) { + ovm_value_t value; + if (lookup_register_in_frame(builder->ovm_state, builder->ovm_frame, builder->it_loc, &value)) { + return value.u32; + } + } + + return 0; + } + + if (builder->it_loc_kind == debug_sym_loc_stack) { + u32 stack_ptr; + if (!lookup_stack_pointer(builder, &stack_ptr)) { + return 0; + } + + return builder->it_loc + stack_ptr; + } + + if (builder->it_loc_kind == debug_sym_loc_global) { + return builder->it_loc; + } + + return 0; +} + void debug_runtime_value_build_string(debug_runtime_value_builder_t *builder) { if (builder->it_loc_kind == debug_sym_loc_register) { append_value_from_register(builder, builder->it_loc, builder->it_type); diff --git a/interpreter/src/debug/debug_thread.c b/interpreter/src/debug/debug_thread.c index 17695647..52e8d948 100644 --- a/interpreter/src/debug/debug_thread.c +++ b/interpreter/src/debug/debug_thread.c @@ -18,6 +18,8 @@ #define CMD_TRACE 6 #define CMD_THREADS 7 #define CMD_VARS 8 +#define CMD_MEM_R 9 +#define CMD_MEM_W 10 #define EVT_NOP 0 #define EVT_BRK_HIT 1 @@ -67,12 +69,12 @@ static int parse_int(debug_state_t *debug, struct msg_parse_ctx_t *ctx) { return i; } -static char *parse_bytes(debug_state_t *debug, struct msg_parse_ctx_t *ctx) { - int len = parse_int(debug, ctx); +static char *parse_bytes(debug_state_t *debug, struct msg_parse_ctx_t *ctx, u32 *len) { + *len = parse_int(debug, ctx); - char *buf = bh_alloc_array(debug->tmp_alloc, char, len); - memcpy(buf, &ctx->data[ctx->offset], len); - ctx->offset += len; + char *buf = bh_alloc_array(debug->tmp_alloc, char, *len); + memcpy(buf, &ctx->data[ctx->offset], *len); + ctx->offset += *len; return buf; } @@ -114,330 +116,378 @@ static void get_stack_frame_location(debug_state_t *debug, assert(debug_info_lookup_file(debug->info, loc_info->file_id, file_info)); } -static void process_command(debug_state_t *debug, struct msg_parse_ctx_t *ctx) { + + +// +// Command handling +// + +#define DEBUG_COMMAND_HANDLER(name) \ + void name(debug_state_t *debug, struct msg_parse_ctx_t *ctx, u32 msg_id) + +typedef DEBUG_COMMAND_HANDLER((* debug_command_handler_t)); + #define ON_THREAD(tid) \ bh_arr_each(debug_thread_state_t *, thread, debug->threads) \ if ((*thread)->id == tid) - u32 msg_id = parse_int(debug, ctx); - u32 command_id = parse_int(debug, ctx); - // printf("[INFO ] Recv command: %d\n", command_id); +static DEBUG_COMMAND_HANDLER(debug_command_nop) { + send_response_header(debug, msg_id); +} - switch (command_id) { - case CMD_NOP: { - send_response_header(debug, msg_id); - break; +static DEBUG_COMMAND_HANDLER(debug_command_res) { + u32 thread_id = parse_int(debug, ctx); + + bool resumed_a_thread = false; + + // Release the thread(s) + bh_arr_each(debug_thread_state_t *, thread, debug->threads) { + if (thread_id == 0xffffffff || (*thread)->id == thread_id) { + resume_thread(*thread); + resumed_a_thread = true; } + } - case CMD_RES: { - u32 thread_id = parse_int(debug, ctx); + send_response_header(debug, msg_id); + send_bool(debug, resumed_a_thread); +} - bool resumed_a_thread = false; +static DEBUG_COMMAND_HANDLER(debug_command_pause) { + u32 thread_id = parse_int(debug, ctx); - // Release the thread(s) - bh_arr_each(debug_thread_state_t *, thread, debug->threads) { - if (thread_id == 0xffffffff || (*thread)->id == thread_id) { - resume_thread(*thread); - resumed_a_thread = true; - } - } + ON_THREAD(thread_id) { + (*thread)->run_count = 0; + } - send_response_header(debug, msg_id); - send_bool(debug, resumed_a_thread); - break; - } + send_response_header(debug, msg_id); +} - case CMD_PAUSE: { - u32 thread_id = parse_int(debug, ctx); +static DEBUG_COMMAND_HANDLER(debug_command_brk) { + char *filename = parse_string(debug, ctx); + unsigned int line = parse_int(debug, ctx); - ON_THREAD(thread_id) { - (*thread)->run_count = 0; - } + i32 instr = debug_info_lookup_instr_by_file_line(debug->info, filename, line); + if (instr < 0) goto brk_send_error; - send_response_header(debug, msg_id); - break; - } + printf("[INFO ] Setting breakpoint at %s:%d (%x)\n", filename, line, instr); - case CMD_BRK: { - char *filename = parse_string(debug, ctx); - unsigned int line = parse_int(debug, ctx); - - i32 instr = debug_info_lookup_instr_by_file_line(debug->info, filename, line); - if (instr < 0) goto brk_send_error; - - printf("[INFO ] Setting breakpoint at %s:%d (%x)\n", filename, line, instr); - - debug_file_info_t file_info; - debug_info_lookup_file_by_name(debug->info, filename, &file_info); - - debug_breakpoint_t bp; - bp.id = debug->next_breakpoint_id++; - bp.instr = instr; - bp.file_id = file_info.file_id; - bp.line = line; - bh_arr_push(debug->breakpoints, bp); - - send_response_header(debug, msg_id); - send_bool(debug, true); - send_int(debug, bp.id); - send_int(debug, line); - break; + debug_file_info_t file_info; + debug_info_lookup_file_by_name(debug->info, filename, &file_info); + + debug_breakpoint_t bp; + bp.id = debug->next_breakpoint_id++; + bp.instr = instr; + bp.file_id = file_info.file_id; + bp.line = line; + bh_arr_push(debug->breakpoints, bp); + + send_response_header(debug, msg_id); + send_bool(debug, true); + send_int(debug, bp.id); + send_int(debug, line); + return; + + brk_send_error: + printf("[WARN ] Failed to set breakpoint at %s:%d (%x)\n", filename, line, instr); + + send_response_header(debug, msg_id); + send_bool(debug, false); + send_int(debug, -1); + send_int(debug, 0); +} - brk_send_error: - printf("[WARN ] Failed to set breakpoint at %s:%d (%x)\n", filename, line, instr); +static DEBUG_COMMAND_HANDLER(debug_command_clr_brk) { + char *filename = parse_string(debug, ctx); - send_response_header(debug, msg_id); - send_bool(debug, false); - send_int(debug, -1); - send_int(debug, 0); - break; - } + debug_file_info_t file_info; + bool file_found = debug_info_lookup_file_by_name(debug->info, filename, &file_info); + if (!file_found) { + goto clr_brk_send_error; + } - case CMD_CLR_BRK: { - char *filename = parse_string(debug, ctx); + bh_arr_each(debug_breakpoint_t, bp, debug->breakpoints) { + if (bp->file_id == file_info.file_id) { + // This is kind of hacky but it does successfully delete + // a single element from the array and move the iterator. + bh_arr_fastdelete(debug->breakpoints, bp - debug->breakpoints); + bp--; + } + } - debug_file_info_t file_info; - bool file_found = debug_info_lookup_file_by_name(debug->info, filename, &file_info); - if (!file_found) { - goto clr_brk_send_error; - } + send_response_header(debug, msg_id); + send_bool(debug, true); + return; - bh_arr_each(debug_breakpoint_t, bp, debug->breakpoints) { - if (bp->file_id == file_info.file_id) { - // This is kind of hacky but it does successfully delete - // a single element from the array and move the iterator. - bh_arr_fastdelete(debug->breakpoints, bp - debug->breakpoints); - bp--; - } - } + clr_brk_send_error: + send_response_header(debug, msg_id); + send_bool(debug, false); +} - send_response_header(debug, msg_id); - send_bool(debug, true); - break; +static DEBUG_COMMAND_HANDLER(debug_command_step) { + u32 granularity = parse_int(debug, ctx); + u32 thread_id = parse_int(debug, ctx); + + if (granularity == 1) { + ON_THREAD(thread_id) { + (*thread)->pause_at_next_line = true; + (*thread)->pause_within = -1; + resume_thread(*thread); + } + } - clr_brk_send_error: - send_response_header(debug, msg_id); - send_bool(debug, false); - break; + if (granularity == 2) { + ON_THREAD(thread_id) { + (*thread)->run_count = 1; + resume_thread(*thread); } + } - case CMD_STEP: { - u32 granularity = parse_int(debug, ctx); - u32 thread_id = parse_int(debug, ctx); - - if (granularity == 1) { - ON_THREAD(thread_id) { - (*thread)->pause_at_next_line = true; - (*thread)->pause_within = -1; - resume_thread(*thread); - } - } + if (granularity == 3) { + ON_THREAD(thread_id) { + ovm_stack_frame_t *last_frame = &bh_arr_last((*thread)->ovm_state->stack_frames); + (*thread)->pause_at_next_line = true; + (*thread)->pause_within = last_frame->func->id; + (*thread)->extra_frames_since_last_pause = 0; + resume_thread(*thread); + } + } - if (granularity == 2) { - ON_THREAD(thread_id) { - (*thread)->run_count = 1; - resume_thread(*thread); - } + if (granularity == 4) { + ON_THREAD(thread_id) { + if (bh_arr_length((*thread)->ovm_state->stack_frames) == 1) { + (*thread)->pause_within = -1; + } else { + ovm_stack_frame_t *last_frame = &bh_arr_last((*thread)->ovm_state->stack_frames); + (*thread)->pause_within = (last_frame - 1)->func->id; } - if (granularity == 3) { - ON_THREAD(thread_id) { - ovm_stack_frame_t *last_frame = &bh_arr_last((*thread)->ovm_state->stack_frames); - (*thread)->pause_at_next_line = true; - (*thread)->pause_within = last_frame->func->id; - (*thread)->extra_frames_since_last_pause = 0; - resume_thread(*thread); - } - } + (*thread)->pause_at_next_line = true; + (*thread)->extra_frames_since_last_pause = 0; + resume_thread(*thread); + } + } - if (granularity == 4) { - ON_THREAD(thread_id) { - if (bh_arr_length((*thread)->ovm_state->stack_frames) == 1) { - (*thread)->pause_within = -1; - } else { - ovm_stack_frame_t *last_frame = &bh_arr_last((*thread)->ovm_state->stack_frames); - (*thread)->pause_within = (last_frame - 1)->func->id; - } + send_response_header(debug, msg_id); +} - (*thread)->pause_at_next_line = true; - (*thread)->extra_frames_since_last_pause = 0; - resume_thread(*thread); - } - } +static DEBUG_COMMAND_HANDLER(debug_command_trace) { + unsigned int thread_id = parse_int(debug, ctx); - send_response_header(debug, msg_id); + debug_thread_state_t *thread = NULL; + bh_arr_each(debug_thread_state_t *, pthread, debug->threads) { + if ((*pthread)->id == thread_id) { + thread = *pthread; break; } + } - case CMD_TRACE: { - unsigned int thread_id = parse_int(debug, ctx); + if (thread == NULL) { + send_response_header(debug, msg_id); + send_int(debug, 0); + return; + } - debug_thread_state_t *thread = NULL; - bh_arr_each(debug_thread_state_t *, pthread, debug->threads) { - if ((*pthread)->id == thread_id) { - thread = *pthread; - break; - } - } + bh_arr(ovm_stack_frame_t) frames = thread->ovm_state->stack_frames; - if (thread == NULL) { - send_response_header(debug, msg_id); - send_int(debug, 0); - break; - } + send_response_header(debug, msg_id); + send_int(debug, bh_arr_length(frames)); - bh_arr(ovm_stack_frame_t) frames = thread->ovm_state->stack_frames; + bh_arr_rev_each(ovm_stack_frame_t, frame, frames) { + debug_func_info_t func_info; + debug_file_info_t file_info; + debug_loc_info_t loc_info; - send_response_header(debug, msg_id); - send_int(debug, bh_arr_length(frames)); + get_stack_frame_location(debug, &func_info, &file_info, &loc_info, thread, frame); - bh_arr_rev_each(ovm_stack_frame_t, frame, frames) { - debug_func_info_t func_info; - debug_file_info_t file_info; - debug_loc_info_t loc_info; + send_string(debug, func_info.name); + send_string(debug, file_info.name); + send_int(debug, loc_info.line); + } +} - get_stack_frame_location(debug, &func_info, &file_info, &loc_info, thread, frame); +static DEBUG_COMMAND_HANDLER(debug_command_threads) { + bh_arr(debug_thread_state_t *) threads = debug->threads; - send_string(debug, func_info.name); - send_string(debug, file_info.name); - send_int(debug, loc_info.line); - } + send_response_header(debug, msg_id); + send_int(debug, bh_arr_length(threads)); - break; - } + char buf[128]; + bh_arr_each(debug_thread_state_t *, thread, threads) { + send_int(debug, (*thread)->id); - case CMD_THREADS: { - bh_arr(debug_thread_state_t *) threads = debug->threads; + snprintf(buf, 128, "thread #%d", (*thread)->id); + send_string(debug, buf); + } +} - send_response_header(debug, msg_id); - send_int(debug, bh_arr_length(threads)); +static DEBUG_COMMAND_HANDLER(debug_command_vars) { + i32 stack_frame = parse_int(debug, ctx); + u32 thread_id = parse_int(debug, ctx); + u32 layers = parse_int(debug, ctx); - char buf[128]; - bh_arr_each(debug_thread_state_t *, thread, threads) { - send_int(debug, (*thread)->id); + debug_thread_state_t **thread = NULL; + bh_arr_each(debug_thread_state_t *, t, debug->threads) + if ((*t)->id == thread_id) + thread = t; - snprintf(buf, 128, "thread #%d", (*thread)->id); - send_string(debug, buf); - } + if (thread == NULL) { + goto vars_error; + } - break; - } + bh_arr(ovm_stack_frame_t) frames = (*thread)->ovm_state->stack_frames; + if (stack_frame >= bh_arr_length(frames)) { + goto vars_error; + } - case CMD_VARS: { - i32 stack_frame = parse_int(debug, ctx); - u32 thread_id = parse_int(debug, ctx); - u32 layers = parse_int(debug, ctx); + ovm_stack_frame_t *frame = &frames[bh_arr_length(frames) - 1 - stack_frame]; - ON_THREAD(thread_id) { - bh_arr(ovm_stack_frame_t) frames = (*thread)->ovm_state->stack_frames; - if (stack_frame >= bh_arr_length(frames)) { - goto vars_error; - } + debug_func_info_t func_info; + debug_file_info_t file_info; + debug_loc_info_t loc_info; - ovm_stack_frame_t *frame = &frames[bh_arr_length(frames) - 1 - stack_frame]; + get_stack_frame_location(debug, &func_info, &file_info, &loc_info, *thread, frame); - debug_func_info_t func_info; - debug_file_info_t file_info; - debug_loc_info_t loc_info; - - get_stack_frame_location(debug, &func_info, &file_info, &loc_info, *thread, frame); - - send_response_header(debug, msg_id); - - debug_runtime_value_builder_t builder; - builder.state = debug; - builder.info = debug->info; - builder.ovm_state = (*thread)->ovm_state; - builder.ovm_frame = frame; - builder.func_info = func_info; - builder.file_info = file_info; - builder.loc_info = loc_info; - debug_runtime_value_build_init(&builder, bh_heap_allocator()); - - // - // The first layer specified is the symbol id to drill into. - // Therefore, the first layer is peeled off here before the - // following while loop. This makes the assumption that no - // symbol will have the id 0xffffffff. This is probably safe - // to assume, especially since just one more symbol and this - // whole system crashes... - u32 sym_id_to_match = 0xffffffff; - if (layers > 0) { - sym_id_to_match = parse_int(debug, ctx); - layers--; + send_response_header(debug, msg_id); + + debug_runtime_value_builder_t builder; + builder.state = debug; + builder.info = debug->info; + builder.ovm_state = (*thread)->ovm_state; + builder.ovm_frame = frame; + builder.func_info = func_info; + builder.file_info = file_info; + builder.loc_info = loc_info; + debug_runtime_value_build_init(&builder, bh_heap_allocator()); + + // + // The first layer specified is the symbol id to drill into. + // Therefore, the first layer is peeled off here before the + // following while loop. This makes the assumption that no + // symbol will have the id 0xffffffff. This is probably safe + // to assume, especially since just one more symbol and this + // whole system crashes... + u32 sym_id_to_match = 0xffffffff; + if (layers > 0) { + sym_id_to_match = parse_int(debug, ctx); + layers--; + } + + i32 symbol_scope = loc_info.symbol_scope; + while (symbol_scope != -1) { + debug_sym_scope_t sym_scope = debug->info->symbol_scopes[symbol_scope]; + builder.sym_scope = sym_scope; + + bh_arr_each(u32, sym_id, sym_scope.symbols) { + debug_sym_info_t *sym = &debug->info->symbols[*sym_id]; + debug_runtime_value_build_set_location(&builder, sym->loc_kind, sym->loc, sym->type, sym->name); + + // + // If we are drilling to a particular symbol, and this is that symbol, + // we have to do the generation a little differently. We first have to + // pull the other layer queries in, and descend into those layers. + // Then, we loop through each value at that layer and print their values. + if (sym->sym_id == sym_id_to_match && sym_id_to_match != 0xffffffff) { + while (layers--) { + u32 desc = parse_int(debug, ctx); + debug_runtime_value_build_descend(&builder, desc); } - i32 symbol_scope = loc_info.symbol_scope; - while (symbol_scope != -1) { - debug_sym_scope_t sym_scope = debug->info->symbol_scopes[symbol_scope]; - builder.sym_scope = sym_scope; - - bh_arr_each(u32, sym_id, sym_scope.symbols) { - debug_sym_info_t *sym = &debug->info->symbols[*sym_id]; - debug_runtime_value_build_set_location(&builder, sym->loc_kind, sym->loc, sym->type, sym->name); - - // - // If we are drilling to a particular symbol, and this is that symbol, - // we have to do the generation a little differently. We first have to - // pull the other layer queries in, and descend into those layers. - // Then, we loop through each value at that layer and print their values. - if (sym->sym_id == sym_id_to_match && sym_id_to_match != 0xffffffff) { - while (layers--) { - u32 desc = parse_int(debug, ctx); - debug_runtime_value_build_descend(&builder, desc); - } - - while (debug_runtime_value_build_step(&builder)) { - debug_runtime_value_build_string(&builder); - debug_type_info_t *type = &debug->info->types[builder.it_type]; - - send_int(debug, 0); - send_string(debug, builder.it_name); - send_bytes(debug, builder.output.data, builder.output.length); - send_string(debug, type->name); - send_int(debug, builder.it_index - 1); // CLEANUP This should be 0 indexed, but because this is after the while loop condition, it is 1 indexed. - send_bool(debug, builder.it_has_children); - - debug_runtime_value_build_clear(&builder); - } - - // This is important, as when doing a layered query, only one symbol - // should be considered, and once found, should immediate stop. - goto syms_done; - - } else if (sym_id_to_match == 0xffffffff) { - // Otherwise, we simply print the value of the symbol as is. - debug_type_info_t *type = &debug->info->types[sym->type]; - - debug_runtime_value_build_string(&builder); - - send_int(debug, 0); - send_string(debug, sym->name); - send_bytes(debug, builder.output.data, builder.output.length); - send_string(debug, type->name); - send_int(debug, sym->sym_id); - send_bool(debug, builder.it_has_children); - - debug_runtime_value_build_clear(&builder); - } - } + while (debug_runtime_value_build_step(&builder)) { + debug_runtime_value_build_string(&builder); + debug_type_info_t *type = &debug->info->types[builder.it_type]; - symbol_scope = sym_scope.parent; + send_int(debug, 0); + send_string(debug, builder.it_name); + send_bytes(debug, builder.output.data, builder.output.length); + send_string(debug, type->name); + send_int(debug, builder.it_index - 1); // CLEANUP This should be 0 indexed, but because this is after the while loop condition, it is 1 indexed. + send_bool(debug, builder.it_has_children); + send_int(debug, debug_runtime_value_get_it_addr(&builder)); + + debug_runtime_value_build_clear(&builder); } - syms_done: - send_int(debug, 1); - debug_runtime_value_build_free(&builder); - return; - } + // This is important, as when doing a layered query, only one symbol + // should be considered, and once found, should immediate stop. + goto syms_done; - vars_error: - send_response_header(debug, msg_id); - send_int(debug, 1); - break; + } else if (sym_id_to_match == 0xffffffff) { + // Otherwise, we simply print the value of the symbol as is. + debug_type_info_t *type = &debug->info->types[sym->type]; + + debug_runtime_value_build_string(&builder); + + send_int(debug, 0); + send_string(debug, sym->name); + send_bytes(debug, builder.output.data, builder.output.length); + send_string(debug, type->name); + send_int(debug, sym->sym_id); + send_bool(debug, builder.it_has_children); + send_int(debug, debug_runtime_value_get_it_addr(&builder)); + + debug_runtime_value_build_clear(&builder); + } } + + symbol_scope = sym_scope.parent; + } + + syms_done: + send_int(debug, 1); + debug_runtime_value_build_free(&builder); + return; + + vars_error: + send_response_header(debug, msg_id); + send_int(debug, 1); +} + +static DEBUG_COMMAND_HANDLER(debug_command_memory_read) { + u32 addr = parse_int(debug, ctx); + u32 count = parse_int(debug, ctx); + + count = bh_min(count, 2048); + + send_response_header(debug, msg_id); + send_bytes(debug, bh_pointer_add(debug->ovm_engine->memory, addr), count); +} + +static DEBUG_COMMAND_HANDLER(debug_command_memory_write) { + u32 addr = parse_int(debug, ctx); + u32 count; + + u8 *data = parse_bytes(debug, ctx, &count); + memcpy(bh_pointer_add(debug->ovm_engine->memory, addr), data, count); + + send_response_header(debug, msg_id); + send_int(debug, count); +} + +static debug_command_handler_t command_handlers[] = { + [CMD_NOP] = debug_command_nop, + [CMD_RES] = debug_command_res, + [CMD_PAUSE] = debug_command_pause, + [CMD_BRK] = debug_command_brk, + [CMD_CLR_BRK] = debug_command_clr_brk, + [CMD_STEP] = debug_command_step, + [CMD_TRACE] = debug_command_trace, + [CMD_THREADS] = debug_command_threads, + [CMD_VARS] = debug_command_vars, + [CMD_MEM_R] = debug_command_memory_read, + [CMD_MEM_W] = debug_command_memory_write, +}; + +static void process_command(debug_state_t *debug, struct msg_parse_ctx_t *ctx) { + u32 msg_id = parse_int(debug, ctx); + u32 command_id = parse_int(debug, ctx); + + if (command_id >= sizeof(command_handlers) / sizeof(command_handlers[0])) { + send_response_header(debug, msg_id); + return; } + + command_handlers[command_id](debug, ctx, msg_id); } static void process_message(debug_state_t *debug, char *msg, unsigned int bytes_read) { diff --git a/misc/vscode/out/ovmDebug.js b/misc/vscode/out/ovmDebug.js index 058e3dcd..f3812dd9 100644 --- a/misc/vscode/out/ovmDebug.js +++ b/misc/vscode/out/ovmDebug.js @@ -89,12 +89,12 @@ class OVMDebugSession extends debugadapter_1.LoggingDebugSession { // make VS Code send setExpression request response.body.supportsSetExpression = false; // make VS Code send disassemble request - response.body.supportsDisassembleRequest = false; + response.body.supportsDisassembleRequest = true; response.body.supportsSteppingGranularity = true; response.body.supportsInstructionBreakpoints = false; // make VS Code able to read and write variable memory - response.body.supportsReadMemoryRequest = false; - response.body.supportsWriteMemoryRequest = false; + response.body.supportsReadMemoryRequest = true; + response.body.supportsWriteMemoryRequest = true; response.body.supportSuspendDebuggee = false; response.body.supportTerminateDebuggee = true; response.body.supportsFunctionBreakpoints = true; @@ -157,7 +157,9 @@ class OVMDebugSession extends debugadapter_1.LoggingDebugSession { threadId: args.threadId, frameIndex: i }); - return new debugadapter_1.StackFrame(frameRef, f.funcname, source, f.line); + let stack_frame = new debugadapter_1.StackFrame(frameRef, f.funcname, source, f.line); + stack_frame.instructionPointerReference = "1234"; + return stack_frame; }) }; this.sendResponse(response); @@ -193,6 +195,9 @@ class OVMDebugSession extends debugadapter_1.LoggingDebugSession { if (v.hasChildren) { nv.variablesReference = this._variableReferences.create(Object.assign(Object.assign({}, frameRef), { variableChain: frameRef.variableChain.concat([v.symId]) })); } + if (v.memoryReference > 0) { + nv.memoryReference = v.memoryReference.toString(); + } return nv; }); response.body = { variables: vs }; @@ -263,6 +268,40 @@ class OVMDebugSession extends debugadapter_1.LoggingDebugSession { this.debugger.step("line", args.threadId); this.sendResponse(response); } + disassembleRequest(response, args, request) { + console.log(args); + response.body = { + instructions: [ + { + address: "0", + instruction: "i32.add %3, %1, %2", + } + ] + }; + this.sendResponse(response); + } + readMemoryRequest(response, args, request) { + return __awaiter(this, void 0, void 0, function* () { + let addr = parseInt(args.memoryReference) + args.offset; + let mem = yield this.debugger.read_memory(addr, args.count); + response.body = { + address: addr.toString(), + data: Buffer.from(mem.data).toString("base64"), + }; + this.sendResponse(response); + }); + } + writeMemoryRequest(response, args, request) { + return __awaiter(this, void 0, void 0, function* () { + let addr = parseInt(args.memoryReference) + args.offset; + let data = Buffer.from(args.data, "base64"); + let bytesWritten = yield this.debugger.write_memory(addr, data); + response.body = { + bytesWritten: bytesWritten, + }; + this.sendResponse(response); + }); + } fileNameToShortName(filename) { return filename.substring(filename.lastIndexOf("/") + 1); } @@ -279,6 +318,8 @@ var OVMCommand; OVMCommand[OVMCommand["TRACE"] = 6] = "TRACE"; OVMCommand[OVMCommand["THREADS"] = 7] = "THREADS"; OVMCommand[OVMCommand["VARS"] = 8] = "VARS"; + OVMCommand[OVMCommand["MEM_R"] = 9] = "MEM_R"; + OVMCommand[OVMCommand["MEM_W"] = 10] = "MEM_W"; })(OVMCommand || (OVMCommand = {})); var OVMEvent; (function (OVMEvent) { @@ -434,6 +475,35 @@ class OVMDebugger extends EventEmitter { this.pending_responses[cmd_id] = OVMCommand.VARS; return this.preparePromise(cmd_id); } + read_memory(addr, count) { + if (this.client == null) + return Promise.resolve({}); + let data = new ArrayBuffer(16); + let view = new DataView(data); + let cmd_id = this.next_command_id; + view.setUint32(0, cmd_id, true); + view.setUint32(4, OVMCommand.MEM_R, true); + view.setUint32(8, addr, true); + view.setUint32(12, count, true); + this.client.write(new Uint8Array(data)); + this.pending_responses[cmd_id] = OVMCommand.MEM_R; + return this.preparePromise(cmd_id); + } + write_memory(addr, data_to_write) { + if (this.client == null) + return Promise.resolve(0); + let data = new ArrayBuffer(16 + data_to_write.byteLength); + let view = new DataView(data); + let cmd_id = this.next_command_id; + view.setUint32(0, cmd_id, true); + view.setUint32(4, OVMCommand.MEM_W, true); + view.setUint32(8, addr, true); + view.setUint32(12, data_to_write.byteLength, true); + new Uint8Array(data).set(new Uint8Array(data_to_write), 16); + this.client.write(new Uint8Array(data)); + this.pending_responses[cmd_id] = OVMCommand.MEM_W; + return this.preparePromise(cmd_id); + } parseIncoming(data) { let parser = new DataParser(data); while (parser.offset != data.length) { @@ -534,11 +604,22 @@ class OVMDebugger extends EventEmitter { let type = parser.parseString(); let symId = parser.parseUint32(); let hasChildren = parser.parseBool(); - result.push({ name, value, type, symId, hasChildren }); + let memoryReference = parser.parseUint32(); + result.push({ name, value, type, symId, hasChildren, memoryReference }); } this.resolvePromise(msg_id, result); break; } + case OVMCommand.MEM_R: { + let data = parser.parseBytes(); + this.resolvePromise(msg_id, { data: data }); + break; + } + case OVMCommand.MEM_W: { + let count = parser.parseUint32(); + this.resolvePromise(msg_id, count); + break; + } default: // console.log("Unrecognized command. ", cmd_id, msg_id); } @@ -601,6 +682,12 @@ class DataParser { this.offset += len; return str; } + parseBytes() { + let len = this.parseUint32(); + let result = this.data.buffer.slice(this.offset, this.offset + len); + this.offset += len; + return result; + } parseBool() { if (this.offset >= this.data.length) return false; diff --git a/misc/vscode/ovmDebug.ts b/misc/vscode/ovmDebug.ts index 940f68c6..b6680210 100644 --- a/misc/vscode/ovmDebug.ts +++ b/misc/vscode/ovmDebug.ts @@ -3,7 +3,7 @@ import { LoggingDebugSession, InitializedEvent, TerminatedEvent, StoppedEvent, BreakpointEvent, OutputEvent, ProgressStartEvent, ProgressUpdateEvent, ProgressEndEvent, InvalidatedEvent, - Thread, StackFrame, Scope, Source, Handles, Breakpoint, MemoryEvent, ThreadEvent, LoadedSourceEvent, Variable + Thread, StackFrame, Scope, Source, Handles, Breakpoint, MemoryEvent, ThreadEvent, LoadedSourceEvent, Variable, } from '@vscode/debugadapter'; import { DebugProtocol } from '@vscode/debugprotocol'; import EventEmitter = require('node:events'); @@ -36,6 +36,7 @@ interface IVariableReference { frameIndex: number; threadId: number; variableChain: number[]; + memoryLocation?: number; } export class OVMDebugSession extends LoggingDebugSession { @@ -145,13 +146,13 @@ export class OVMDebugSession extends LoggingDebugSession { response.body.supportsSetExpression = false; // make VS Code send disassemble request - response.body.supportsDisassembleRequest = false; + response.body.supportsDisassembleRequest = true; response.body.supportsSteppingGranularity = true; response.body.supportsInstructionBreakpoints = false; // make VS Code able to read and write variable memory - response.body.supportsReadMemoryRequest = false; - response.body.supportsWriteMemoryRequest = false; + response.body.supportsReadMemoryRequest = true; + response.body.supportsWriteMemoryRequest = true; response.body.supportSuspendDebuggee = false; response.body.supportTerminateDebuggee = true; @@ -235,7 +236,11 @@ export class OVMDebugSession extends LoggingDebugSession { threadId: args.threadId, frameIndex: i }); - return new StackFrame(frameRef, f.funcname, source, f.line); + + let stack_frame = new StackFrame(frameRef, f.funcname, source, f.line); + stack_frame.instructionPointerReference = "1234"; + + return stack_frame; }) }; @@ -283,6 +288,10 @@ export class OVMDebugSession extends LoggingDebugSession { }); } + if (v.memoryReference > 0) { + nv.memoryReference = v.memoryReference.toString(); + } + return nv; }); @@ -366,6 +375,48 @@ export class OVMDebugSession extends LoggingDebugSession { this.sendResponse(response); } + protected disassembleRequest(response: DebugProtocol.DisassembleResponse, args: DebugProtocol.DisassembleArguments, request?: DebugProtocol.Request): void { + console.log(args); + + response.body = { + instructions: [ + { + address: "0", + instruction: "i32.add %3, %1, %2", + } + ] + }; + + this.sendResponse(response); + } + + protected async readMemoryRequest(response: DebugProtocol.ReadMemoryResponse, args: DebugProtocol.ReadMemoryArguments, request?: DebugProtocol.Request): Promise { + let addr = parseInt(args.memoryReference) + args.offset; + + let mem = await this.debugger.read_memory(addr, args.count); + + response.body = { + address: addr.toString(), + data: Buffer.from(mem.data).toString("base64"), + }; + + this.sendResponse(response); + } + + protected async writeMemoryRequest(response: DebugProtocol.WriteMemoryResponse, args: DebugProtocol.WriteMemoryArguments, request?: DebugProtocol.Request): Promise { + let addr = parseInt(args.memoryReference) + args.offset; + + let data = Buffer.from(args.data, "base64"); + + let bytesWritten = await this.debugger.write_memory(addr, data); + + response.body = { + bytesWritten: bytesWritten, + }; + + this.sendResponse(response); + } + private fileNameToShortName(filename: string): string { return filename.substring(filename.lastIndexOf("/") + 1); } @@ -394,6 +445,11 @@ interface IVariableInfo { type: string; symId: number; hasChildren: boolean; + memoryReference: number; +} + +interface IReadMemory { + data: ArrayBuffer; } enum OVMCommand { @@ -405,7 +461,9 @@ enum OVMCommand { STEP = 5, TRACE = 6, THREADS = 7, - VARS = 8 + VARS = 8, + MEM_R = 9, + MEM_W = 10, } enum OVMEvent { @@ -612,6 +670,48 @@ class OVMDebugger extends EventEmitter { return this.preparePromise(cmd_id); } + read_memory(addr: number, count: number): Promise { + if (this.client == null) return Promise.resolve({} as IReadMemory); + + let data = new ArrayBuffer(16); + let view = new DataView(data); + + let cmd_id = this.next_command_id; + + view.setUint32(0, cmd_id, true); + view.setUint32(4, OVMCommand.MEM_R, true); + view.setUint32(8, addr, true); + view.setUint32(12, count, true); + + this.client.write(new Uint8Array(data)); + + this.pending_responses[cmd_id] = OVMCommand.MEM_R; + + return this.preparePromise(cmd_id); + } + + write_memory(addr: number, data_to_write: ArrayBuffer): Promise { + if (this.client == null) return Promise.resolve(0); + + let data = new ArrayBuffer(16 + data_to_write.byteLength); + let view = new DataView(data); + + let cmd_id = this.next_command_id; + + view.setUint32(0, cmd_id, true); + view.setUint32(4, OVMCommand.MEM_W, true); + view.setUint32(8, addr, true); + view.setUint32(12, data_to_write.byteLength, true); + + new Uint8Array(data).set(new Uint8Array(data_to_write), 16); + + this.client.write(new Uint8Array(data)); + + this.pending_responses[cmd_id] = OVMCommand.MEM_W; + + return this.preparePromise(cmd_id); + } + private parseIncoming(data: Buffer): void { let parser = new DataParser(data); @@ -732,13 +832,28 @@ class OVMDebugger extends EventEmitter { let type = parser.parseString(); let symId = parser.parseUint32(); let hasChildren = parser.parseBool(); - result.push({name, value, type, symId, hasChildren}); + let memoryReference = parser.parseUint32(); + result.push({name, value, type, symId, hasChildren, memoryReference}); } this.resolvePromise(msg_id, result); break; } + case OVMCommand.MEM_R: { + let data = parser.parseBytes(); + + this.resolvePromise(msg_id, { data: data }); + break; + } + + case OVMCommand.MEM_W: { + let count = parser.parseUint32(); + + this.resolvePromise(msg_id, count); + break; + } + default: // console.log("Unrecognized command. ", cmd_id, msg_id); } @@ -818,6 +933,14 @@ class DataParser { return str; } + parseBytes(): ArrayBuffer { + let len = this.parseUint32(); + let result = this.data.buffer.slice(this.offset, this.offset + len); + this.offset += len; + + return result; + } + parseBool() { if (this.offset >= this.data.length) return false;