From: Brendan Hansen Date: Sun, 31 Jul 2022 20:27:56 +0000 (-0500) Subject: starting to work on debug server X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=94c033582630454df3d52df8802b2b0013c4bb72;p=onyx-embedder.git starting to work on debug server --- diff --git a/include/ovm_debug.h b/include/ovm_debug.h index c76763e..b58aed2 100644 --- a/include/ovm_debug.h +++ b/include/ovm_debug.h @@ -2,6 +2,8 @@ #define _OVM_DEBUG_H #include "bh.h" +#include +#include typedef struct debug_loc_info_t { u32 file_id; @@ -20,14 +22,29 @@ typedef struct debug_func_info_t { u32 debug_op_offset; } debug_func_info_t; +typedef struct debug_file_info_t { + char *name; + u32 line_count; + i32 line_buffer_offset; +} debug_file_info_t; + typedef struct debug_info_t { bh_allocator alloc; + // func index -> func info bh_arr(debug_func_info_t) funcs; + + // reducer output -> line info bh_arr(debug_loc_info_t) line_info; + + // instruction index -> reducer output bh_arr(u32) instruction_reducer; - bh_arr(char *) file_names; + // line index -> instruction index + bh_arr(u32) line_to_instruction; + + // file_id -> file info + bh_arr(debug_file_info_t) files; } debug_info_t; void debug_info_init(debug_info_t *); @@ -35,6 +52,10 @@ void debug_info_free(debug_info_t *); void debug_info_import_file_info(debug_info_t *, u8 *data, u32 len); void debug_info_import_func_info(debug_info_t *, u8 *data, u32 len); +bool debug_info_lookup_location(debug_info_t *info, u32 instruction, debug_loc_info_t *out); +bool debug_info_lookup_file(debug_info_t *info, u32 file_id, debug_file_info_t *out); +bool debug_info_lookup_file_by_name(debug_info_t *info, char *name, debug_file_info_t *out); + // // This builder is used in conjunction with code builder to output // debug information for each instruction that is generated in OVM. @@ -47,11 +68,12 @@ typedef struct debug_info_builder_t { u32 current_file_id; u32 current_line; + u32 next_file_line_offset; bh_arr(char) symbol_scope_stack; u32 remaining_reps; - + b32 locked : 1; } debug_info_builder_t; @@ -62,4 +84,49 @@ void debug_info_builder_step(debug_info_builder_t *); void debug_info_builder_begin_func(debug_info_builder_t *, i32 func_idx); void debug_info_builder_end_func(debug_info_builder_t *); -#endif \ No newline at end of file + +typedef enum debug_exec_state_t { + debug_state_starting, + debug_state_ready, + debug_state_running, + debug_state_paused +} debug_exec_state_t; + +typedef struct debug_thread_state_t { + u32 id; + + debug_exec_state_t state; + struct ovm_state_t *ovm_state; + + i32 run_count; + sem_t wait_semaphore; + + bh_arr(u32) breakpoints; +} debug_thread_state_t; + +// +// This represents known state of the debugger. There should only +// be one state per running program, as it is tied to the ovm_engine_t. +// +typedef struct debug_state_t { + bh_allocator alloc; + + debug_info_t *info; + + bh_arr(debug_thread_state_t *) threads; + u32 next_thread_id; + + pthread_t debug_thread; + bool debug_thread_running; + + u32 listen_socket_fd; + u32 client_fd; +} debug_state_t; + +void debug_host_init(debug_state_t *debug); +void debug_host_start(debug_state_t *debug); +void debug_host_stop(debug_state_t *debug); +u32 debug_host_register_thread(debug_state_t *debug, struct ovm_state_t *ovm_state); +debug_thread_state_t *debug_host_lookup_thread(debug_state_t *debug, u32 id); + +#endif diff --git a/include/vm.h b/include/vm.h index e737868..44868f8 100644 --- a/include/vm.h +++ b/include/vm.h @@ -2,6 +2,7 @@ #define _ONYX_VM_H #include "bh.h" +#include "ovm_debug.h" #include #include @@ -88,6 +89,8 @@ struct ovm_engine_t { i64 memory_size; // This is probably going to always be 4GiB. void *memory; + + debug_state_t *debug; }; ovm_engine_t *ovm_engine_new(ovm_store_t *store); @@ -118,6 +121,8 @@ struct ovm_state_t { // running instances of the program *could* have different // native functions linked. bh_arr(ovm_external_func_t) external_funcs; + + debug_thread_state_t *debug; }; ovm_state_t *ovm_state_new(ovm_engine_t *engine, ovm_program_t *program); diff --git a/src/debug/debug_host.c b/src/debug/debug_host.c new file mode 100644 index 0000000..f6f0522 --- /dev/null +++ b/src/debug/debug_host.c @@ -0,0 +1,97 @@ + +#include "ovm_debug.h" +#include "vm.h" + +void debug_host_init(debug_state_t *debug) { + memset(debug, 0, sizeof(*debug)); + debug->alloc = bh_heap_allocator(); + + debug->info = NULL; + + debug->threads = NULL; + debug->next_thread_id = 0; + bh_arr_new(debug->alloc, debug->threads, 4); + + debug->listen_socket_fd = 0; + debug->client_fd = 0; +} + +static void *debug_thread_entry(void *); + +void debug_host_start(debug_state_t *debug) { + if (debug->debug_thread_running) return; + + pthread_create(&debug->debug_thread, NULL, debug_thread_entry, debug); +} + +void debug_host_stop(debug_state_t *debug) { + debug->debug_thread_running = false; + pthread_join(debug->debug_thread, NULL); +} + +u32 debug_host_register_thread(debug_state_t *debug, ovm_state_t *ovm_state) { + debug_thread_state_t *new_thread = bh_alloc(debug->alloc, sizeof(*new_thread)); + + new_thread->state = debug_state_starting; + new_thread->ovm_state = ovm_state; + new_thread->run_count = 0; // Start threads in stopped state. + sem_init(&new_thread->wait_semaphore, 0, 0); + + new_thread->breakpoints = NULL; + bh_arr_new(debug->alloc, new_thread->breakpoints, 8); + + u32 id = debug->next_thread_id++; + new_thread->id = id; + + bh_arr_push(debug->threads, new_thread); + return id; +} + +debug_thread_state_t *debug_host_lookup_thread(debug_state_t *debug, u32 id) { + bh_arr_each(debug_thread_state_t *, pthread, debug->threads) { + if ((*pthread)->id == id) return *pthread; + } + return NULL; +} + + +#include +#include +#include + +static void *debug_thread_entry(void *data) { + debug_state_t *debug = data; + debug->debug_thread_running = true; + + // Set up socket listener + // Wait for initial connection/handshake before entering loop? + + debug->listen_socket_fd = socket(AF_UNIX, SOCK_STREAM, 0); + + struct sockaddr_un local_addr, remote_addr; + local_addr.sun_family = AF_UNIX; + strcpy(local_addr.sun_path, "/tmp/ovm-debug.0000"); // TODO: Make this dynamic so mulitple servers can exist at a time. + unlink(local_addr.sun_path); // TODO: Remove this line for the same reason. + int len = strlen(local_addr.sun_path) + sizeof(local_addr.sun_family); + bind(debug->listen_socket_fd, (struct sockaddr *)&local_addr, len); + + // + // Currently, there can only be 1 connected debugger instance at a time. + listen(debug->listen_socket_fd, 1); + + len = sizeof(struct sockaddr_un); + debug->client_fd = accept(debug->listen_socket_fd, (void * restrict)&remote_addr, &len); + + close(debug->listen_socket_fd); + + printf("Client connected"); + + while (debug->debug_thread_running) { + // ... + + } + + close(debug->client_fd); + + return NULL; +} diff --git a/src/debug/debug_info.c b/src/debug/debug_info.c index 3a41586..0341563 100644 --- a/src/debug/debug_info.c +++ b/src/debug/debug_info.c @@ -8,7 +8,8 @@ void debug_info_init(debug_info_t *info) { bh_arr_new(info->alloc, info->funcs, 16); bh_arr_new(info->alloc, info->line_info, 1024); bh_arr_new(info->alloc, info->instruction_reducer, 4096); - bh_arr_new(info->alloc, info->file_names, 16); + bh_arr_new(info->alloc, info->files, 16); + bh_arr_new(info->alloc, info->line_to_instruction, 1024); } void debug_info_free(debug_info_t *info) { @@ -16,8 +17,10 @@ void debug_info_free(debug_info_t *info) { bh_arr_free(info->line_info); bh_arr_free(info->instruction_reducer); - bh_arr_each(char *, name, info->file_names) bh_free(info->alloc, *name); - bh_arr_free(info->file_names); + bh_arr_each(debug_file_info_t, file, info->files) { + bh_free(info->alloc, file->name); + } + bh_arr_free(info->files); } void debug_info_import_file_info(debug_info_t *info, u8 *data, u32 len) { @@ -25,14 +28,19 @@ void debug_info_import_file_info(debug_info_t *info, u8 *data, u32 len) { i32 count = uleb128_to_uint(data, &offset); fori (i, 0, (i32) count) { + debug_file_info_t file_info; + file_info.line_buffer_offset = -1; + u32 file_id = uleb128_to_uint(data, &offset); + file_info.line_count = uleb128_to_uint(data, &offset); + u32 name_length = uleb128_to_uint(data, &offset); - char *name = bh_alloc_array(info->alloc, char, name_length + 1); - memcpy(name, data + offset, name_length); - name[name_length] = 0; + file_info.name = bh_alloc_array(info->alloc, char, name_length + 1); + memcpy(file_info.name, data + offset, name_length); + file_info.name[name_length] = 0; offset += name_length; - bh_arr_set_at(info->file_names, file_id, name); + bh_arr_set_at(info->files, file_id, file_info); } assert(offset == len); @@ -69,3 +77,29 @@ void debug_info_import_func_info(debug_info_t *info, u8 *data, u32 len) { assert(offset == len); } + +bool debug_info_lookup_location(debug_info_t *info, u32 instruction, debug_loc_info_t *out) { + if (instruction > (u32) bh_arr_length(info->instruction_reducer)) return false; + *out = info->line_info[info->instruction_reducer[instruction]]; + return true; +} + +bool debug_info_lookup_file(debug_info_t *info, u32 file_id, debug_file_info_t *out) { + if (file_id > (u32) bh_arr_length(info->files)) return false; + *out = info->files[file_id]; + return true; +} + +// +// For now, this is going to compare the strings exactly. In the future, it might be a good +// to do a levenschtein distance or something, so the full path isn't needed. +bool debug_info_lookup_file_by_name(debug_info_t *info, char *name, debug_file_info_t *out) { + bh_arr_each(debug_file_info_t, file, info->files) { + if (!strcmp(file->name, name)) { + *out = *file; + return true; + } + } + + return false; +} diff --git a/src/debug/debug_info_builder.c b/src/debug/debug_info_builder.c index 343c452..d920af8 100644 --- a/src/debug/debug_info_builder.c +++ b/src/debug/debug_info_builder.c @@ -15,19 +15,20 @@ void debug_info_builder_prepare(debug_info_builder_t *builder, u8 *data) { builder->reader_offset = 0; builder->current_file_id = 0; builder->current_line = 0; + builder->next_file_line_offset = 0; builder->remaining_reps = 0; } static void debug_info_builder_parse(debug_info_builder_t *builder) { u32 count = 0; - while (1) { + while (!builder->locked) { u8 instr = builder->data[builder->reader_offset++]; switch (instr & 0b11000000) { case 0b00000000: instr &= 0b00111111; switch (instr) { - case 0: builder->locked = 1; break; + case 0: builder->locked = 1; break; // Early return! case 1: builder->current_file_id = uleb128_to_uint(builder->data, &builder->reader_offset); builder->current_line = uleb128_to_uint(builder->data, &builder->reader_offset); @@ -61,7 +62,9 @@ static void debug_info_builder_parse(debug_info_builder_t *builder) { } void debug_info_builder_step(debug_info_builder_t *builder) { - while (builder->remaining_reps == 0) { + if (builder->data == NULL) return; + + while (builder->remaining_reps == 0 && !builder->locked) { debug_info_builder_parse(builder); debug_loc_info_t info; @@ -69,10 +72,21 @@ void debug_info_builder_step(debug_info_builder_t *builder) { info.line = builder->current_line; info.symbols = 0; bh_arr_push(builder->info->line_info, info); + + debug_file_info_t *file_info = &builder->info->files[info.file_id]; + if (file_info->line_buffer_offset == -1) { + file_info->line_buffer_offset = builder->next_file_line_offset; + builder->next_file_line_offset += file_info->line_count; + } + + u32 line_index = file_info->line_buffer_offset + builder->current_line; + u32 target = bh_arr_length(builder->info->instruction_reducer) - 1; + bh_arr_set_at(builder->info->line_to_instruction, line_index, target); } if (builder->locked) return; + assert(builder->remaining_reps); builder->remaining_reps -= 1; return; } @@ -82,13 +96,19 @@ void debug_info_builder_emit_location(debug_info_builder_t *builder) { } void debug_info_builder_begin_func(debug_info_builder_t *builder, i32 func_idx) { - assert(func_idx < bh_arr_length(builder->info->funcs)); + if (func_idx >= bh_arr_length(builder->info->funcs)) return; + debug_func_info_t *func_info = &builder->info->funcs[func_idx]; builder->reader_offset = func_info->debug_op_offset; - assert(builder->reader_offset < 20000); + assert(builder->data[builder->reader_offset] == 2); + assert(builder->data[builder->reader_offset+1] == 1); + builder->remaining_reps = 0; builder->locked = 0; } void debug_info_builder_end_func(debug_info_builder_t *builder) { + assert(!builder->locked); + debug_info_builder_step(builder); + assert(builder->locked); } diff --git a/src/vm/vm.c b/src/vm/vm.c index 155d2e4..70adeee 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -84,7 +84,7 @@ int ovm_program_register_static_ints(ovm_program_t *program, int len, int *data) void ovm_program_modify_static_int(ovm_program_t *program, int arr, int idx, int new_value) { if (arr >= bh_arr_length(program->static_data)) return; - + ovm_static_integer_array_t array = program->static_data[arr]; if (idx >= array.len) return; @@ -445,6 +445,8 @@ ovm_engine_t *ovm_engine_new(ovm_store_t *store) { engine->memory = mmap(NULL, engine->memory_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); pthread_mutex_init(&engine->atomic_mutex, NULL); + engine->debug = NULL; + return engine; } @@ -491,9 +493,14 @@ ovm_state_t *ovm_state_new(ovm_engine_t *engine, ovm_program_t *program) { state->external_funcs = NULL; bh_arr_new(store->heap_allocator, state->external_funcs, 8); + if (engine->debug) { + u32 thread_id = debug_host_register_thread(engine->debug, state); + state->debug = debug_host_lookup_thread(engine->debug, thread_id); + } + #ifdef OVM_VERBOSE ovm_program_print_instructions(program, 0, bh_arr_length(program->code)); -#endif +#endif return state; } @@ -653,8 +660,21 @@ ovm_value_t ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t bool release_mutex_at_end = false; ovm_value_t tmp_val; - + while (state->pc < bh_arr_length(program->code)) { + // This will become the line change detection + // debug_loc_info_t loc_info; + // debug_info_lookup_location(engine->debug->info, state->pc, &loc_info); + // if (loc_info.file_id != last_file || loc_info.line != last_line) { + // last_file = loc_info.file_id; + // last_line = loc_info.line; + + // debug_file_info_t file_info; + // debug_info_lookup_file(engine->debug->info, last_file, &file_info); + // + // printf("(%d, %d) %s:%d\n", last_file, last_line, file_info.name, last_line); + // } + #ifdef OVM_VERBOSE ovm_program_print_instructions(program, state->pc, 1); #endif @@ -920,7 +940,7 @@ ovm_value_t ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t VAL(instr.r).u64 = 0; \ VAL(instr.r).dtype = instr.stype; \ break; - + OVM_IMM(OVM_TYPE_I8, i8, i) OVM_IMM(OVM_TYPE_I16, i16, i) OVM_IMM(OVM_TYPE_I32, i32, i) @@ -1018,7 +1038,7 @@ ovm_value_t ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t case OVMI_RETURN: { ovm_value_t val = VAL(instr.a); - ovm_stack_frame_t frame = ovm__func_teardown_stack_frame(engine, state, program); + ovm_stack_frame_t frame = ovm__func_teardown_stack_frame(engine, state, program); state->pc = frame.return_address; if (bh_arr_length(state->stack_frames) == 0) { @@ -1035,11 +1055,11 @@ ovm_value_t ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t } #ifdef OVM_VERBOSE - printf("Returning from %s to %s: ", frame.func->name, bh_arr_last(state->stack_frames).func->name); + printf("Returning from %s to %s: ", frame.func->name, bh_arr_last(state->stack_frames).func->name); ovm_print_val(val); printf("\n\n"); #endif - + break; } @@ -1130,7 +1150,7 @@ ovm_value_t ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t case OVM_TYPED_INSTR(OVMI_CVT_I64_S, OVM_TYPE_I8): CVT(i64, i8, OVM_TYPE_I8, i8); case OVM_TYPED_INSTR(OVMI_CVT_I64_S, OVM_TYPE_I16): CVT(i64, i16, OVM_TYPE_I16, i16); case OVM_TYPED_INSTR(OVMI_CVT_I64_S, OVM_TYPE_I32): CVT(i64, i32, OVM_TYPE_I32, i32); - + case OVM_TYPED_INSTR(OVMI_CVT_I64, OVM_TYPE_F32): CVT(u64, f32, OVM_TYPE_F32, f32); case OVM_TYPED_INSTR(OVMI_CVT_I64_S, OVM_TYPE_F32): CVT(i64, f32, OVM_TYPE_F32, f32); case OVM_TYPED_INSTR(OVMI_CVT_I64, OVM_TYPE_F64): CVT(u64, f64, OVM_TYPE_F64, f64); @@ -1203,4 +1223,3 @@ ovm_value_t ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t return ((ovm_value_t) {0}); } - diff --git a/src/wasm/engine.c b/src/wasm/engine.c index 8f2162d..55df041 100644 --- a/src/wasm/engine.c +++ b/src/wasm/engine.c @@ -3,24 +3,37 @@ #include "vm.h" wasm_engine_t *wasm_engine_new() { + wasm_engine_t *engine = wasm_engine_new_with_config(NULL); + return engine; +} + +wasm_engine_t *wasm_engine_new_with_config(wasm_config_t *config) { ovm_store_t *store = ovm_store_new(); wasm_engine_t *engine = bh_alloc_item(store->heap_allocator, wasm_engine_t); - engine->config = NULL; + engine->config = config; engine->store = store; ovm_engine_t *ovm_engine = ovm_engine_new(store); engine->engine = ovm_engine; - return engine; -} -wasm_engine_t *wasm_engine_new_with_config(wasm_config_t *config) { - wasm_engine_t *engine = wasm_engine_new(); - engine->config = config; + if (config && config->debug_enabled) { + // This should maybe be moved elsewhere? + debug_state_t *debug = bh_alloc_item(store->heap_allocator, debug_state_t); + engine->engine->debug = debug; + + debug_host_init(engine->engine->debug); + debug_host_start(engine->engine->debug); + } + return engine; } void wasm_engine_delete(wasm_engine_t *engine) { + if (engine->engine->debug) { + debug_host_stop(engine->engine->debug); + } + ovm_store_t *store = engine->store; ovm_engine_delete(engine->engine); bh_free(store->heap_allocator, engine); diff --git a/src/wasm/module.c b/src/wasm/module.c index f561afb..8e469a6 100644 --- a/src/wasm/module.c +++ b/src/wasm/module.c @@ -71,6 +71,11 @@ wasm_module_t *wasm_module_new(wasm_store_t *store, const wasm_byte_vec_t *binar debug_info_init(&module->debug_info); + if (store->engine->engine->debug) { + assert(store->engine->engine->debug->info == NULL); + store->engine->engine->debug->info = &module->debug_info; + } + bool success = module_build(module, binary); return module; } diff --git a/src/wasm/module_parsing.h b/src/wasm/module_parsing.h index 1776d2c..8d1e2ef 100644 --- a/src/wasm/module_parsing.h +++ b/src/wasm/module_parsing.h @@ -20,10 +20,11 @@ struct build_context { int func_table_arr_idx; int next_external_func_idx; + + debug_info_builder_t debug_builder; // This will be set/reset for every code (function) entry. ovm_code_builder_t builder; - debug_info_builder_t debug_builder; }; #define PEEK_BYTE(ctx) ((ctx)->binary.data[(ctx)->offset]) @@ -504,6 +505,8 @@ static void parse_fe_instruction(build_context *ctx) { } static void parse_instruction(build_context *ctx) { + debug_info_builder_step(&ctx->debug_builder); + unsigned char instr_byte = CONSUME_BYTE(ctx); switch (instr_byte) { case 0x00: break; @@ -891,8 +894,6 @@ static void parse_instruction(build_context *ctx) { default: assert(("UNHANDLED INSTRUCTION", 0)); } - - debug_info_builder_step(&ctx->debug_builder); } static void parse_expression(build_context *ctx) {