From 85c8272c9959558a05413a6a403535519f14164f Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Thu, 11 Aug 2022 20:31:19 -0500 Subject: [PATCH] added symbol info and message packing --- include/bh.h | 5 ++ include/ovm_debug.h | 36 +++++++- src/debug/debug_host.c | 6 ++ src/debug/debug_info.c | 31 +++++++ src/debug/debug_info_builder.c | 51 +++++++++-- src/debug/debug_thread.c | 152 ++++++++++++++++++++++++++------- src/vm/vm.c | 2 +- src/wasm/module_parsing.h | 4 + 8 files changed, 245 insertions(+), 42 deletions(-) diff --git a/include/bh.h b/include/bh.h index 3cf52f9..ef09cf6 100644 --- a/include/bh.h +++ b/include/bh.h @@ -541,6 +541,7 @@ typedef struct bh_buffer { BH_DEF void bh_buffer_init(bh_buffer* buffer, bh_allocator alloc, i32 length); BH_DEF void bh_buffer_free(bh_buffer* buffer); +BH_DEF void bh_buffer_clear(bh_buffer *buffer); BH_DEF void bh_buffer_grow(bh_buffer* buffer, i32 length); BH_DEF void bh_buffer_append(bh_buffer* buffer, const void * data, i32 length); BH_DEF void bh_buffer_concat(bh_buffer* buffer, bh_buffer other); @@ -2306,6 +2307,10 @@ BH_DEF void bh_buffer_free(bh_buffer* buffer) { buffer->capacity = 0; } +BH_DEF void bh_buffer_clear(bh_buffer *buffer) { + buffer->length = 0; +} + BH_DEF void bh_buffer_grow(bh_buffer* buffer, i32 length) { if (buffer == NULL) return; diff --git a/include/ovm_debug.h b/include/ovm_debug.h index 2037b3b..68ca597 100644 --- a/include/ovm_debug.h +++ b/include/ovm_debug.h @@ -8,7 +8,7 @@ typedef struct debug_loc_info_t { u32 file_id; u32 line; - u32 symbols; + u32 symbol_scope; } debug_loc_info_t; typedef struct debug_func_info_t { @@ -29,6 +29,25 @@ typedef struct debug_file_info_t { i32 line_buffer_offset; } debug_file_info_t; +typedef enum debug_sym_loc_kind_t { + debug_sym_loc_register = 1, + debug_sym_loc_stack = 2, + debug_sym_loc_global = 3 +} debug_sym_loc_kind_t; + +typedef struct debug_sym_info_t { + char *name; + u32 sym_id; + debug_sym_loc_kind_t loc_kind; + u32 loc; + u32 type; +} debug_sym_info_t; + +typedef struct debug_sym_scope_t { + bh_arr(u32) symbols; + i32 parent; // -1 for root +} debug_sym_scope_t; + typedef struct debug_info_t { bh_allocator alloc; @@ -48,12 +67,19 @@ typedef struct debug_info_t { // file_id -> file info bh_arr(debug_file_info_t) files; + + // sym_id -> sym info + bh_arr(debug_sym_info_t) symbols; + + // scope id -> symbol scope + bh_arr(debug_sym_scope_t) symbol_scopes; } debug_info_t; void debug_info_init(debug_info_t *); 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); +void debug_info_import_sym_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); @@ -74,7 +100,7 @@ typedef struct debug_info_builder_t { u32 current_line; u32 next_file_line_offset; - bh_arr(char) symbol_scope_stack; + i32 current_scope; u32 remaining_reps; @@ -127,6 +153,8 @@ typedef struct debug_thread_state_t { bh_arr(debug_breakpoint_t) breakpoints; u32 last_breakpoint_hit; + + u32 state_change_write_fd; } debug_thread_state_t; // @@ -151,6 +179,10 @@ typedef struct debug_state_t { u32 listen_socket_fd; u32 client_fd; + + bh_buffer send_buffer; + + u32 state_change_pipes[2]; } debug_state_t; void debug_host_init(debug_state_t *debug); diff --git a/src/debug/debug_host.c b/src/debug/debug_host.c index fa1dcf5..b306afd 100644 --- a/src/debug/debug_host.c +++ b/src/debug/debug_host.c @@ -17,6 +17,10 @@ void debug_host_init(debug_state_t *debug) { debug->listen_socket_fd = 0; debug->client_fd = 0; + + if (pipe(debug->state_change_pipes) != 0) { + printf("[ERROR] Failed to create thread notification pipes.\n"); + } } void debug_host_start(debug_state_t *debug) { @@ -45,6 +49,8 @@ u32 debug_host_register_thread(debug_state_t *debug, ovm_state_t *ovm_state) { u32 id = debug->next_thread_id++; new_thread->id = id; + new_thread->state_change_write_fd = debug->state_change_pipes[1]; + bh_arr_push(debug->threads, new_thread); return id; } diff --git a/src/debug/debug_info.c b/src/debug/debug_info.c index d50b234..7db2090 100644 --- a/src/debug/debug_info.c +++ b/src/debug/debug_info.c @@ -11,6 +11,8 @@ void debug_info_init(debug_info_t *info) { bh_arr_new(info->alloc, info->instruction_reducer, 4096); bh_arr_new(info->alloc, info->files, 16); bh_arr_new(info->alloc, info->line_to_instruction, 1024); + bh_arr_new(info->alloc, info->symbols, 128); + bh_arr_new(info->alloc, info->symbol_scopes, 128); } void debug_info_free(debug_info_t *info) { @@ -82,6 +84,35 @@ void debug_info_import_func_info(debug_info_t *info, u8 *data, u32 len) { assert(offset == len); } +void debug_info_import_sym_info(debug_info_t *info, u8 *data, u32 len) { + u32 offset = 0; + info->has_debug_info = true; + + i32 count = uleb128_to_uint(data, &offset); + fori (i, 0, count) { + debug_sym_info_t sym_info; + sym_info.sym_id = uleb128_to_uint(data, &offset); + + u32 name_length = uleb128_to_uint(data, &offset); + if (name_length == 0) { + sym_info.name = NULL; + } else { + sym_info.name = bh_alloc_array(info->alloc, char, name_length + 1); + memcpy(sym_info.name, data + offset, name_length); + sym_info.name[name_length] = 0; + offset += name_length; + } + + sym_info.loc_kind = uleb128_to_uint(data, &offset); + sym_info.loc = uleb128_to_uint(data, &offset); + sym_info.type = uleb128_to_uint(data, &offset); + + bh_arr_set_at(info->symbols, sym_info.sym_id, sym_info); + } + + assert(offset == len); +} + bool debug_info_lookup_location(debug_info_t *info, u32 instruction, debug_loc_info_t *out) { if (!info || !info->has_debug_info) return false; diff --git a/src/debug/debug_info_builder.c b/src/debug/debug_info_builder.c index 1b84def..13c33df 100644 --- a/src/debug/debug_info_builder.c +++ b/src/debug/debug_info_builder.c @@ -7,7 +7,6 @@ void debug_info_builder_init(debug_info_builder_t *builder, debug_info_t *info) memset(builder, 0, sizeof(*builder)); builder->info = info; - bh_arr_new(bh_heap_allocator(), builder->symbol_scope_stack, 8); } void debug_info_builder_prepare(debug_info_builder_t *builder, u8 *data) { @@ -15,10 +14,39 @@ 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->current_scope = -1; builder->next_file_line_offset = 0; builder->remaining_reps = 0; } +static i32 debug_info_builder_push_scope(debug_info_builder_t *builder) { + debug_sym_scope_t scope; + scope.symbols = NULL; + bh_arr_new(builder->info->alloc, scope.symbols, 4); + scope.parent = builder->current_scope; + + i32 scope_id = bh_arr_length(builder->info->symbol_scopes); + builder->current_scope = scope_id; + + bh_arr_push(builder->info->symbol_scopes, scope); + return scope_id; +} + +static i32 debug_info_builder_pop_scope(debug_info_builder_t *builder) { + if (builder->current_scope == -1) return -1; + + debug_sym_scope_t *scope = &builder->info->symbol_scopes[builder->current_scope]; + builder->current_scope = scope->parent; + return builder->current_scope; +} + +static void debug_info_builder_add_symbol(debug_info_builder_t *builder, u32 sym_id) { + if (builder->current_scope == -1) return; + + debug_sym_scope_t *scope = &builder->info->symbol_scopes[builder->current_scope]; + bh_arr_push(scope->symbols, sym_id); +} + static void debug_info_builder_parse(debug_info_builder_t *builder) { u32 count = 0; @@ -33,10 +61,19 @@ static void debug_info_builder_parse(debug_info_builder_t *builder) { builder->current_file_id = uleb128_to_uint(builder->data, &builder->reader_offset); builder->current_line = uleb128_to_uint(builder->data, &builder->reader_offset); break; - case 2: break; - case 3: break; + + case 2: + debug_info_builder_push_scope(builder); + break; + + case 3: + debug_info_builder_pop_scope(builder); + break; + case 4: - uleb128_to_uint(builder->data, &builder->reader_offset); + debug_info_builder_add_symbol( + builder, + uleb128_to_uint(builder->data, &builder->reader_offset)); break; } break; @@ -68,9 +105,9 @@ void debug_info_builder_step(debug_info_builder_t *builder) { debug_info_builder_parse(builder); debug_loc_info_t info; - info.file_id = builder->current_file_id; - info.line = builder->current_line; - info.symbols = 0; + info.file_id = builder->current_file_id; + info.line = builder->current_line; + info.symbol_scope = builder->current_scope; bh_arr_push(builder->info->line_info, info); debug_file_info_t *file_info = &builder->info->files[info.file_id]; diff --git a/src/debug/debug_thread.c b/src/debug/debug_thread.c index bffe9a9..6abc544 100644 --- a/src/debug/debug_thread.c +++ b/src/debug/debug_thread.c @@ -6,6 +6,7 @@ #include #include #include +#include #define CMD_NOP 0 @@ -14,10 +15,13 @@ #define CMD_CLR_BRK 3 #define CMD_STEP 5 #define CMD_TRACE 6 +#define CMD_THREADS 7 +#define CMD_VARS 8 #define EVT_NOP 0 #define EVT_BRK_HIT 1 #define EVT_PAUSE 2 +#define EVT_NEW_THREAD 3 // Not Implemented #define EVT_RESPONSE 0xffffffff struct msg_parse_ctx_t { @@ -28,34 +32,32 @@ struct msg_parse_ctx_t { static void send_response_header(debug_state_t *debug, unsigned int message_number) { unsigned int RESPONSE_HEADER = EVT_RESPONSE; - send(debug->client_fd, &RESPONSE_HEADER, 4, 0); - send(debug->client_fd, &message_number, 4, 0); + bh_buffer_write_u32(&debug->send_buffer, EVT_RESPONSE); + bh_buffer_write_u32(&debug->send_buffer, message_number); } static void send_string(debug_state_t *debug, const char *str) { - unsigned int len; if (!str) { - len = 0; - send(debug->client_fd, &len, 4, 0); + bh_buffer_write_u32(&debug->send_buffer, 0); } else { - len = strlen(str); - send(debug->client_fd, &len, 4, 0); - send(debug->client_fd, str, len, 0); + unsigned int len = strlen(str); + bh_buffer_write_u32(&debug->send_buffer, len); + bh_buffer_append(&debug->send_buffer, str, len); } } static void send_bytes(debug_state_t *debug, const char *bytes, unsigned int len) { - send(debug->client_fd, &len, 4, 0); - send(debug->client_fd, bytes, len, 0); + bh_buffer_write_u32(&debug->send_buffer, len); + bh_buffer_append(&debug->send_buffer, bytes, len); } static void send_int(debug_state_t *debug, unsigned int x) { - send(debug->client_fd, &x, 4, 0); + bh_buffer_write_u32(&debug->send_buffer, x); } static void send_bool(debug_state_t *debug, bool x) { bool v = x ? 1 : 0; - send(debug->client_fd, &v, 1, 0); + bh_buffer_write_byte(&debug->send_buffer, v); } static int parse_int(debug_state_t *debug, struct msg_parse_ctx_t *ctx) { @@ -90,6 +92,27 @@ static void resume_thread(debug_thread_state_t *thread) { sem_post(&thread->wait_semaphore); } +static void get_stack_frame_location(debug_state_t *debug, + debug_func_info_t *func_info, debug_file_info_t *file_info, debug_loc_info_t *loc_info, + debug_thread_state_t *thread, ovm_stack_frame_t *frame) { + + ovm_func_t *func = frame->func; + + assert(debug_info_lookup_func(debug->info, func->id, func_info)); + + u32 instr; + if (frame == &bh_arr_last(thread->ovm_state->stack_frames)) { + instr = thread->ovm_state->pc; + } else { + instr = (frame + 1)->return_address; + } + + while (!debug_info_lookup_location(debug->info, instr, loc_info)) + instr++; + + 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) { #define ON_THREAD(tid) \ bh_arr_each(debug_thread_state_t *, thread, debug->threads) \ @@ -252,8 +275,6 @@ static void process_command(debug_state_t *debug, struct msg_parse_ctx_t *ctx) { break; } - // The trace command should be probably eclipse - // the loc command at some point? case CMD_TRACE: { unsigned int thread_id = parse_int(debug, ctx); @@ -277,31 +298,80 @@ static void process_command(debug_state_t *debug, struct msg_parse_ctx_t *ctx) { send_int(debug, bh_arr_length(frames)); bh_arr_rev_each(ovm_stack_frame_t, frame, frames) { - ovm_func_t *func = frame->func; - debug_func_info_t func_info; - assert(debug_info_lookup_func(debug->info, func->id, &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_string(debug, func_info.name); + send_string(debug, file_info.name); + send_int(debug, loc_info.line); + } - debug_loc_info_t loc_info; - debug_file_info_t file_info; + break; + } - u32 instr; - if (frame == &bh_arr_last(frames)) { - instr = thread->ovm_state->pc; - } else { - instr = (frame + 1)->return_address; + case CMD_THREADS: { + bh_arr(debug_thread_state_t *) threads = debug->threads; + + send_response_header(debug, msg_id); + send_int(debug, bh_arr_length(threads)); + + char buf[128]; + bh_arr_each(debug_thread_state_t *, thread, threads) { + send_int(debug, (*thread)->id); + + snprintf(buf, 128, "thread #%d", (*thread)->id); + send_string(debug, buf); + } + + break; + } + + case CMD_VARS: { + i32 stack_frame = parse_int(debug, ctx); + u32 thread_id = parse_int(debug, ctx); + + 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; } - while (!debug_info_lookup_location(debug->info, instr, &loc_info)) - instr++; + ovm_stack_frame_t *frame = &frames[bh_arr_length(frames) - 1 - stack_frame]; - assert(debug_info_lookup_file(debug->info, loc_info.file_id, &file_info)); + debug_func_info_t func_info; + debug_file_info_t file_info; + debug_loc_info_t loc_info; - 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); + + send_response_header(debug, msg_id); + + i32 symbol_scope = loc_info.symbol_scope; + while (symbol_scope != -1) { + debug_sym_scope_t sym_scope = debug->info->symbol_scopes[symbol_scope]; + + bh_arr_each(u32, sym_id, sym_scope.symbols) { + debug_sym_info_t *sym = &debug->info->symbols[*sym_id]; + + send_int(debug, 0); + send_string(debug, sym->name); + send_string(debug, "???"); + send_string(debug, "unknown type"); + } + + symbol_scope = sym_scope.parent; + } + + send_int(debug, 1); + return; } + vars_error: + send_response_header(debug, msg_id); + send_int(debug, 1); break; } } @@ -349,11 +419,22 @@ void *__debug_thread_entry(void * data) { // Disable blocking reads and write in the client socket // Alternatively, a MSG_DONTWAIT could be used below fcntl(debug->client_fd, F_SETFL, O_NONBLOCK); + fcntl(debug->state_change_pipes[0], F_SETFL, O_NONBLOCK); printf("[INFO ] Client connected\n"); + bh_buffer_init(&debug->send_buffer, bh_heap_allocator(), 1024); + + struct pollfd poll_fds[2]; + poll_fds[0].fd = debug->state_change_pipes[0]; + poll_fds[0].events = POLLIN; + poll_fds[1].fd = debug->client_fd; + poll_fds[1].events = POLLIN; + char command[4096]; while (debug->debug_thread_running) { + poll(poll_fds, 2, 1000); + // // Try to read commands from the client. // If an error was returned, bail out of this thread. @@ -382,12 +463,15 @@ void *__debug_thread_entry(void * data) { // // Check the state of the running program and report any changes. - + + char buf; + int dummy = read(debug->state_change_pipes[0], &buf, 1); + bh_arr_each(debug_thread_state_t *, thread, debug->threads) { if ((*thread)->state == debug_state_hit_breakpoint) { (*thread)->state = debug_state_paused; - u32 instr, bp_id = (*thread)->last_breakpoint_hit; + i32 instr = -1, bp_id = (*thread)->last_breakpoint_hit; bh_arr_each(debug_breakpoint_t, bp, (*thread)->breakpoints) { if (bp->id == bp_id) { instr = bp->instr; @@ -395,6 +479,8 @@ void *__debug_thread_entry(void * data) { } } + if (instr == -1) continue; + debug_loc_info_t loc_info; debug_info_lookup_location(debug->info, instr, &loc_info); @@ -414,7 +500,9 @@ void *__debug_thread_entry(void * data) { send_int(debug, (*thread)->pause_reason); } } - + + send(debug->client_fd, debug->send_buffer.data, debug->send_buffer.length, 0); + bh_buffer_clear(&debug->send_buffer); bh_arena_clear(&debug->tmp_arena); } diff --git a/src/vm/vm.c b/src/vm/vm.c index 150644a..6567caa 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -717,12 +717,12 @@ ovm_value_t ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t goto shouldnt_wait; should_wait: + assert(write(state->debug->state_change_write_fd, "1", 1)); sem_wait(&state->debug->wait_semaphore); state->debug->state = debug_state_running; shouldnt_wait: if (state->debug->run_count > 0) state->debug->run_count--; - (void) 0; } // diff --git a/src/wasm/module_parsing.h b/src/wasm/module_parsing.h index 29d01d2..75b5fcd 100644 --- a/src/wasm/module_parsing.h +++ b/src/wasm/module_parsing.h @@ -91,6 +91,10 @@ static void parse_custom_section(build_context *ctx) { if (!strcmp(name, "ovm_debug_ops")) { debug_info_builder_prepare(&ctx->debug_builder, cs.data); } + + if (!strcmp(name, "ovm_debug_syms")) { + debug_info_import_sym_info(ctx->debug_builder.info, cs.data, cs.size); + } } ctx->offset = end_of_section; -- 2.25.1